PackagePackagetypeA library you install and call yourself.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

cds-routing-handlers

Decorator-based routing of CAP service handlers in TypeScript, from before the core had an opinion — the framework the CDS-TS-Dispatcher post names as its unmaintained predecessor.

Facts

Latestv3.0.7ReleasedLast pushLicenceMIT

Where it sits

Lives in
Provides
TypeScript patternsCAPTypeScript patternscapabilityHow an application is arranged once it is written in TypeScript — where handlers live, what registers them, and how business logic is kept apart from persistence. Distinct from typing, which is about the model being visible to the compiler; this is about the shape of the code around it.
Flavors

Who is behind it

mrbandlermrbandlerPersonMaintains this
In the atlas: maintains 2, contributes to 2, wrote 1
@mrbandler
maintainerSimonPersonContributes to this
In the atlas: contributes to 2
@HeneryHawk
iDschepePersonContributes to this
In the atlas: contributes to 1
@iDschepe
2 contributors

Before choosing this

Signals

ActivityQuietRelease and repository quiet for 46 months

Both observable publication channels have been quiet for at least 24 full months. This may be normal for stable software; check whether its current state still fits your use.

What it declares

Every artifact

Type
Package
Origin
Independently maintained

As a package

Where it runs
does not say
Distributed through
npm

For typeScript patterns

How handlers bind
decorators
Layers it names
handlers

Tasks this contributes to2Beta

Handle a draft's own events apart from the active entity'sA draft-enabled entity's own events — NEW, PATCH, EDIT, SAVE and DISCARD — are handled in places of their own in a CAP service written in TypeScript, apart from the CREATE, UPDATE and DELETE handlers of the active entity.Node.jsAssessed, not knownhandler registration from decorators
What it does

The decorator reference read today covers the four CRUD events, actions and functions, and names no draft event. Nothing in it rules a draft handler out either, so what happens for a draft-enabled entity is not established.

Remaining work

Not established from the README. A project would have to find out whether a draft event reaches an annotated method at all.

All options on the TypeScript patterns page →
Split one CAP service implementation into a class per entityThe custom logic of a CAP service written in TypeScript sits in one class per entity rather than in a single service implementation file, and each class's methods still reach the framework as the before, on and after handlers for that entity.Node.jsCovers, under conditionshandler registration from decorators
What it does

@Handler marks a class for an entity and its annotated methods are registered as that entity's actions, the same mechanism the dispatcher uses and the earliest of these to offer it.

Conditions

reflect-metadata is installed and imported before the package is used; emitDecoratorMetadata and experimentalDecorators are set in the project's tsconfig.json

Remaining work

The TypeScript decorator setup is the project's: reflect-metadata imported before anything else, and emitDecoratorMetadata and experimentalDecorators set in tsconfig.json. The README names no @sap/cds version it was built against.

All options on the TypeScript patterns page →

Connections1

Install

npm add cds-routing-handlers

Derived from the package name. No recipe recorded; the README has the configuration.

Learn more1

Readings
  1. Taking CAP to the next level - with TypeScriptmrbandler, SAP Community

README

quoted from mrbandler/cds-routing-handlers under MIT, read
mrbandler/cds-routing-handlers/README.mdOn GitHub

CDS Routing-Handlers

npm version Actions Status GitHub License

Package to route and implement CDS handlers via a class based system in Typescript.

Table of Content

  1. Installation 💻
  2. Usage ⌨️
  3. Decorator Reference 📋
  4. Example 🧷
  5. Bugs and Features 🐞💡
  6. License 📃

1. Installation

$ npm install cds-routing-handlers

OR

$ yarn add cds-routing-handlers

Before you can use it you also need to install reflect-metadata.

$ npm install reflect-metadata

OR

$ yarn add reflect-metadata

Once installed, make sure you import it before using CDS Routing-Handlers. My recommendation would be to place the import at the top of your main entry file.

import "reflect-metadata";

Finally, one last step is required to use it. We need to tell the Typescript compiler to use decorators.

Place the following settings in your tsconfig.json:

{
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
}

2. Usage

Before:

const express = require("express");

function registerHandlers(srv) {
    srv.on("READ", "Entity", async () => {
        // Handle the read here...
    });
}

const server = express();
cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(registerHandlers);

With CDS Routing-Handlers:

// ./handlers/entity.handler.ts

import { Handler, OnRead } from "cds-routing-handlers";

@Handler("Entity")
export class EntityHandler {
    @OnRead()
    public async read(@Srv() srv: any, @Req() req: any): Promise<void> {
        // Handle the read here...
    }
}
// ./server.ts

import express from "express";
import { createCombinedHandler } from "cds-routing-handlers";

const server = express();

// Either:
const handler = createCombinedHandler({
    handler: [__dirname + "/handlers/**/*.js"],
});

cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(handler);

// OR

import { EntityHandler } from "./handlers/entity.handler.ts";

const handler = createCombinedHandler({
    handler: [EntityHandler],
});

cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(handler);
Depencency Injection Support

CDS Routing-Handlers can be used in conjunction with typedi.

import { useContainer } from "cds-routing-handlers";
import { Container } from "typedi";

useContainer(Container);

That's it now you can inject service into your handler classes.

import { Handler, Func, Param } from "cds-routing-handlers";
import { Service, Inject } from "typedi";

@Service()
export class GreeterService {
    public greet(name: string): string {
        return "Hello, " + name;
    }
}

@Handler()
export class GreeterHandler {
    @Inject()
    private greeterService: GreeterService;

    @Func("hello")
    public async hello(@Param("name") name: string): Promise<string> {
        return this.greeterService.greet(name);
    }
}
Middleware

In addition to the before handler you can implement custom middlewares for either the complete service (global) or a specific entity.

Global Middleware
// ./global.middleware.ts

import { ICdsMiddleware, Middleware, Req, Jwt } from "cds-routing-handlers";

@Middleware({ global: true, priority: 1 })
export class GobalMiddleware implements ICdsMiddleware {
    // You can inject the request parameters as you would in any other handler.
    public async use(@Req() req: any, @Jwt() jwt: string): Promise<void> {
        console.log("I am global middleware prio 1");
    }
}
// ./server.ts

import express from "express";
import { createCombinedHandler } from "cds-routing-handlers";
import { GlobalMiddleware } from "./global.middleware";

const server = express();

const handler = createCombinedHandler({
    handler: [__dirname + "/handlers/**/*.js"],
    middlewares: [GlobalMiddleware],
});

cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(handler);
Handler Middleware
// ./handler.middleware.ts

import { ICdsMiddleware, Middleware, Req, Jwt } from "cds-routing-handlers";

@Middleware() // Omit options for handler middlewares
export class HandlerMiddleware implements ICdsMiddleware {
    // You can inject the request parameters as you would in any other handler.
    public async use(@Req() req: any, @Jwt() jwt: string): Promise<void> {
        console.log("I am handler middleware!");
    }
}
// ./handlers/entity.handler.ts

import { Handler, Use, OnRead } from "cds-routing-handlers";
import { HandlerMiddleware } from "../handler.middleware";

@Handler("Entity")
@Use(HandlerMiddleware)
export class EntityHandler {
    @OnRead()
    public async read(@Srv() srv: any, @Req() req: any): Promise<void> {
        // Handle the read here...
    }
}
// ./server.ts

import express from "express";
import { createCombinedHandler } from "cds-routing-handlers";
import { HandlerMiddleware } from "./handler.middleware";

const server = express();

const handler = createCombinedHandler({
    handler: [__dirname + "/handlers/**/*.js"],
    middlewares: [HandlerMiddleware],
});

cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(handler);
User Checker

To avoid duplicate code for user retrieval in all handlers over the JWT token you can implement a user checker which lets you inject your custom user object with the @User() decorator.

// ./custom.userchecker.ts

import { IUserChecker, UserChecker, Jwt } from "cds-routing-handlers";

export interface IUser {
    username: string;
}

@UserChecker() // Omit options for handler middlewares
export class CustomUserChecker implements IUserChecker {
    // You can inject the request parameters as you would in any other handler.
    // NOTE: With one exception the @User() decorator will not work.
    public async check(@Jwt() jwt: string): Promise<IUser> {
        // Custom code here to create user from JWT...
        // Let's fake it here
        return {
            username: "mrbandler",
        };
    }
}
// ./handlers/entity.handler.ts

import { Handler, OnRead } from "cds-routing-handlers";
import { IUser } from "../custom.userchecker";

@Handler("Entity")
export class EntityHandler {
    @OnRead()
    public async read(@User() user: IUser): Promise<void> {
        // Use your custom user object in the read logic...
    }
}
// ./server.ts

import express from "express";
import { createCombinedHandler } from "cds-routing-handlers";
import { CustomUserChecker } from "./custom.userchecker";

const server = express();

const handler = createCombinedHandler({
    handler: [__dirname + "/handlers/**/*.js"],
    userChecker: CustomUserChecker,
});

cds.serve("./gen/")
    .at("odata")
    .in(server)
    .with(handler);
Complete Handler API
import { Handler } from "cds-routing-handlers";
import { BeforeCreate, OnCreate, AfterCreate } from "cds-routing-handlers";
import { BeforeRead, OnRead, AfterRead } from "cds-routing-handlers";
import { BeforeUpdate, OnUpdate, AfterUpdate } from "cds-routing-handlers";
import { BeforeDelete, OnDelete, AfterDelete } from "cds-routing-handlers";
import { Func, Action } from "cds-routing-handlers";
import { Req, Srv, Param, ParamObj, Jwt } from "cds-routing-handlers";

interface IFoobar {
    Id: string;
    Foo: string;
    Bar: number;
}

/**
 *  Basic OData operations.
 */
@Handler("Foobar")
export class FooBarHandler {
    /**
     *  CREATE handlers.
     */
    @BeforeCreate()
    public async beforeCreate(): Promise<IFoobar> {
        // Handle the before create here...
    }

    @OnCreate()
    public async onCreate(): Promise<IFoobar> {
        // Handle the create here...
    }

    @AfterCreate()
    public async afterCreate(): Promise<IFoobar> {
        // Handle the after create here...
    }

    /**
     *  READ handlers.
     */
    @BeforeRead()
    public async beforeRead(): Promise<IFoobar> {
        // Handle the before read here...
    }

    @OnRead()
    public async onRead(): Promise<IFoobar> {
        // Handle the read here...
    }

    @AfterRead()
    public async afterRead(): Promise<IFoobar> {
        // Handle the after read here...
    }

    /**
     *  UPDATE handlers.
     */
    @BeforeUpdate()
    public async beforeUpdate(): Promise<IFoobar> {
        // Handle the before update here...
    }

    @OnUpdate()
    public async onUpdate(): Promise<IFoobar> {
        // Handle the update here...
    }

    @AfterUpdate()
    public async afterUpdate(): Promise<IFoobar> {
        // Handle the after update here...
    }

    /**
     *  DELETE handlers.
     */
    @BeforeDelete()
    public async beforeDelete(): Promise<void> {
        // Handle the before delete here...
    }

    @OnDelete()
    public async onDelete(): Promise<void> {
        // Handle the delete here...
    }

    @AfterDelete()
    public async afterDelete(): Promise<void> {
        // Handle the after delete here...
    }
}

/**
 *  Root service handler.
 */
@Handler("Foobar")
export class FooBarRejectHandler {
    @OnRead()
    @OnReject(500, "Foobar not found")
    public async onRead(): Promise<IFoobar> {
        // When something fails in here, a error object like defined above will be returned.
        /**
         * {
         *    "error": {
         *      "code": 500,
         *      "message": "Foobar not found"
         *    }
         * }
         *
         */
    }

    @OnRead()
    @OnReject(500, "Foobar not found", true)
    public async onRead(): Promise<IFoobar> {
        // When something fails in here, a error object like defined above will be returned.
        // This time the JS error message is appended to the message.
        /**
         * {
         *    "error": {
         *      "code": 500,
         *      "message": "Foobar not found: JS Error Message"
         *    }
         * }
         *
         */
    }
}

/**
 *  Error handling and rejection operations.
 */
@Handler()
export class FooBarRejectHandler {
    /**
     *  Function Import.
     *
     *  CDS:
     *      function foo(bar: String) returns String;
     *
     */
    @Func("foo")
    public async foo(@Param("bar") bar: string): Promise<string> {
        return "Foo, " + bar;
    }

    /**
     *  Action Import.
     *
     *  CDS:
     *      action bar(foo: String, noop: String);
     *
     */
    @Action("bar")
    public async bar(@Param("foo") foo: string, @Param("noop") noop: string): Promise<void> {
        console.log("Foo Param", foo);
        console.log("Noop Param", noop);
    }
}

interface IDoItParams {
    id: string;
    do: string;
    times: number;
}

/**
 *  Error handling and rejection operations.
 */
@Handler()
export class ParamExampleHandler {
    /**
     *  Function Import.
     *
     *  CDS:
     *      function hello(title: String, name: String) returns String;
     *
     */
    @Func("hello")
    public async foo(@Param("title") title: string, @Param("name") name: string): Promise<string> {
        return `Hello, ${title} ${name}`;
    }

    /**
     *  Function Import.
     *
     *  CDS:
     *      action doIt(id: String, do: String, tunes: number);
     *
     */
    @Action("doIt")
    public async doIt(@ParamObj() params: IDoItParams): Promise<string> {
        console.log(params); // => { id: "12345", do: "over", "times": 9000 }
    }

    /**
     * Additionaly you can inject the service aswell as the request object.
     */
    @OnRead()
    public async read(@Srv() srv: any, @Req() req: any): Promise<string> {
        // Do something with srv and req.
    }

    /**
     * If the incoming request contains a JWT token you can inject that aswell.
     */
    @OnRead()
    public async read(@Jwt() jwt: string): Promise<string> {
        // Do something with srv and req.
    }

    /**
     * After handlers a bit if a special case, they always give you a array of entities that was worked on prior to the after handler.
     *
     * This list can be injected via the @Entities decorator.
     */
    @AfterRead()
    public async afterRead(@Entities() entities: IDoItParams[]): Promise<IDoItParams[]> {
        return entities.map(e => {
            e.id = "After handler was here";
            return e;
        });
    }
}

3. Decorator Reference

Middleware Decorators
SignatureExampleDescription
@Middleware(options?: { global?: boolean, priority?: number })@Middleware({ global: true, priority: 1 }) class CustomMiddleware implements ICdsMiddleware
or
@Middleware() class CustomMiddleware implements ICdsMiddleware
Class that is marked with this decorator is registered as a middleware and needs to implement the ICdsMiddleware interface.
The middleware can then be used to intercept all incoming request via a global definition or intercept all incoming requests on a specific entity handler.
User Checker Decorators
SignatureExampleDescription
@UserChecker()@UserChecker() class CustomUserChecker implements IUserCheckerClass that is marked with this decorator is registered as a user checker and needs to implement the IUserChecker interface.
The user checker can then be used to provide a custom user object via the @User() decorator.
Handler Decorators
SignatureExampleDescription
@Handler(entity?: string)@Handler("Books") class BooksHandlerClass that is marked with this decorator is registered as a handler and its annotated methods are registered as actions. The entity parameter is used to differentiate and register the correct actions for the corresponding entity.
@Use(...middlewares: Function[])@Use(MiddlewareClass) class BooksHandlerA class that is marked with this decorator also needs to be marked with the @Handler() decorator to be used. If a handler is implemented the use method from the middleware is called every time a new requests comes in, regardless of the action (CREATE, READ, UPDATE, DELETE) to be executed.
Handler Action Decorators

In this table we assume that all action handlers a within a @Handler decorated class with the entity Books.

SignatureExampleDescription@sap/cds analogue
@BeforeCreate()@BeforeCreate() async beforeCreate()Methods marked with this decorator will register a request made with POST HTTP Method to the specified entity, before it will be handled by the actual handler.srv.before("CREATE", "Books", async (req) => ...)
@OnCreate()@OnCreate() async onCreate()Methods marked with this decorator will register a request made with POST HTTP Method to the specified entity.srv.on("CREATE", "Books", async (req) => ...)
@AfterCreate()@AfterCreate() async afterCreate()Methods marked with this decorator will register a request made with POST HTTP Method to the specified entity, after it was handled by the actual handler.srv.after("CREATE", "Books", async (books, req) => ...)
@BeforeRead()@BeforeRead() async beforeRead()Methods marked with this decorator will register a request made with GET HTTP Method to the specified entity, before it will be handled by the actual handler.srv.before("READ", "Books", async (req) => ...)
@OnRead()@OnRead() async onRead()Methods marked with this decorator will register a request made with GET HTTP Method to the specified entity.srv.on("READ", "Books", async (req) => ...)
@AfterRead()@AfterRead() async afterRead()Methods marked with this decorator will register a request made with GET HTTP Method to the specified entity, after it was handled by the actual handler.srv.after("READ", "Books", async (books, req) => ...)
@BeforeUpdate()@BeforeUpdate() async beforeUpdate()Methods marked with this decorator will register a request made with PUT HTTP Method to the specified entity, before it will be handled by the actual handler.srv.before("UPDATE", "Books", async (req) => ...)
@OnUpdate()@OnUpdate() async onUpdate()Methods marked with this decorator will register a request made with PUT HTTP Method to the specified entity.srv.on("UPDATE", "Books", async (req) => ...)
@AfterUpdate()@AfterUpdate() async afterUpdate()Methods marked with this decorator will register a request made with DELETE HTTP Method to the specified entity, after it was handled by the actual handler.srv.after("UPDATE", "Books", async (books, req) => ...)
@BeforeDelete()@BeforeDelete() async beforeDelete()Methods marked with this decorator will register a request made with DELETE HTTP Method to the specified entity, before it will be handled by the actual handler.srv.before("DELETE", "Books", async (req) => ...)
@OnDelete()@OnDelete() async onDelete()Methods marked with this decorator will register a request made with DELETE HTTP Method to the specified entity.srv.on("DELETE", "Books", async (req) => ...)
@AfterDelete()@AfterDelete() async afterDelete()Methods marked with this decorator will register a request made with DELETE HTTP Method to the specified entity, after it was handled by the actual handler.srv.after("DELETE", "Books", async (books, req) => ...)
@Func(name: string)@Func("doIt") async doIt()Methods marked with this decorator will register a request made with either GET or POST HTTP. The payload can either be given by path parameters or body.srv.on("doIt", "Books", async (req) => ...) OR srv.on("doIt", async (req) => ...)
@Action(name: string)@Action("doItNow") async doItNow()Methods marked with this decorator will register a request made with either GET or POST HTTP. The payload can either be given by path parameters or body.srv.on("doItNow", "Books", async (req) => ...) OR srv.on("doItNow", async (req) => ...)
@OnReject(code: number, message: string, appendErrorMessage: boolean = false)@OnReject(500, "Nope, that didn't work") async handler()Methods marked with this decorator will return a error object when a handler fails.srv.on("READ", "Books", async (req) => {req.reject(500, "Nope, that didn't work")})
Method Parameter Decorators
SignatureExampleDescription@sap/cds analogue
@Req()onRead(@Req() req: any)Injects the request object.srv.on("READ", "Books", async (req) => ...)
@Srv()onRead(@Srv() srv: any)Injects the service object.srv.on("READ", "Books", async (req) => { // Acess srv here })
@Param(name: string)doIt(@Param("times") times: number)Injects a Function/Action Import parameter.srv.on("READ", "Books", async (req) => { let times = req.data["times"] as number; })
@ParamObj()doIt(@ParamObj() doItParams: IDoItParams)Injects a Function/Action Import parameter object.srv.on("READ", "Books", async (req) => { let doItParams = req.data as IDoItParams; })
@Entities()afterRead(@Entities() entities: IBook[])Injects entities from a previous handler on @After* handlers.srv.after("READ", "Books", async (books, req) => ...)
@Jwt()onRead(@Jwt() jwt: string)Injects the JWT token, when found on incoming request.N/A
@Data()onRead(@Data() book: IBook)Injects the data object from a incoming request. It's actually the same as @ParamObj() with a different identifier to differentiate between Function/Action imports and other handlers.srv.on("READ", "Books", async(req) => { const book = req.data as I Book})
@Next()onRead(@Next() next: Function)Injects the next handler function for flow control with multiple handlers.srv.on("READ", "Books", async(req, next) => ... )
@Locale()onRead(@Locale() locale: string)Injects the locale of the current requesting user.srv.on("READ", "Books", async(req) => const locale = req.user.locale)
@User()onRead(@User() user: IUser)Injects the custom user object that is created via the UserChecker implementation.N/A

4. Example

For a complete example checkout the ./test directory which contains the project I am testing with.

Additionally you can use my cds-routing-handler Postman collection which contains definitions for every endpoint from the project.

5. Bugs and Features

Please open a issue when you encounter any bugs 🐞 or if you have an idea for a additional feature 💡.


6. License

MIT License

Copyright (c) 2019 mrbandler

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

MIT, read

History

Latest

v3.0.7

Notes

Remove dependency @sap-cloud-sdk/core

  1. v3.0.6

  2. v3.0.5

  3. v3.0.4

  4. v3.0.2

  5. v3.0.1

  6. v3.0.0

1,317downloads per week, npm
Weekly downloads for cds-routing-handlers26 complete calendar weeks from 2026-04-27 to 2026-10-25; the latest total is 1317.27 Apr – 3 May: 3,6764 May – 10 May: 6,56611 May – 17 May: 7,56018 May – 24 May: 6,12025 May – 31 May: 6,8931 Jun – 7 Jun: 3,6288 Jun – 14 Jun: 5,29715 Jun – 21 Jun: 4,48222 Jun – 28 Jun: 4,70029 Jun – 5 Jul: 3,7456 Jul – 12 Jul: 4,01313 Jul – 19 Jul: 4,51920 Jul – 26 Jul: 7,74127 Jul – 2 Aug: 5,9723 Aug – 9 Aug: 4,07410 Aug – 16 Aug: 4,40517 Aug – 23 Aug: 4,49624 Aug – 30 Aug: 5,32831 Aug – 6 Sept: 2,5497 Sept – 13 Sept: 4,84914 Sept – 20 Sept: 4,96121 Sept – 27 Sept: 4,03528 Sept – 4 Oct: 3,5065 Oct – 11 Oct: 3,00212 Oct – 18 Oct: 2,01319 Oct – 25 Oct: 1,317
TypeScript patterns
  1. @dxfrontier/cds-ts-dispatcherDecorator-driven handler registration for CAP in TypeScriptfrom CAP
  2. @dxfrontier/cds-ts-repositoryTyped BaseRepository over CAP entitiesfrom CAP
  3. cds-plugin-handlersOne handler file per entity in CAP servicesfrom CAP
  4. cds2typesCLI generating TypeScript interfaces from a CDS modelfrom CAP
  5. cap-handler-frameworkConvention-based handler layer for CAP with draft lifecycle hooksfrom CAP
  6. cds-plugin-typescriptCDS build task compiling TypeScript sources during cds buildfrom CAP
  7. cds-hyper-app-serviceTyped class-based service layer over CAP ApplicationServicefrom CAP

Details

Sources5
npm registrydiscovered herechecked
The discovery passdiscovered herechecked
npm registry, cds-routing-handlersversion, releases, installschecked
github.com/git+https://github.com/mrbandler/cds-routing-handlers.gittree, pushes, contributors, CIchecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
latestv3.0.7
first release
releases and cadence33, ~95d median
discovered vianpm registry, The discovery pass
first seen in the atlas
ecosystem and domaincap, developer-experience
Flavors1
Node.js
“$ npm install cds-routing-handlers” — mrbandler/cds-routing-handlers — READMEchecked
Contributors2

From the repository’s contributor list, .

History in the atlas4
  1. what_it_does and two facet values
  2. Typed cds-plugin by placement, which defaults every candidate to that. It is not one.
  3. Decorator-based routing of CAP service handlers in TypeScript, from before the core had an opinion — the framework the CDS-TS-Dispatcher post names as its unmaintained predecessor.
  4. First seen.
For your agent
Ref
artifact:mrbandler-cds-routing-handlers
JSON
/artifacts/mrbandler-cds-routing-handlers.json
MCP
Connecting an agent