An open API service indexing awesome lists of open source software.

https://github.com/dxfrontier/cds-ts-dispatcher

SAP CAP TypeScript entity handler dispatcher.
https://github.com/dxfrontier/cds-ts-dispatcher

cap cds cds-ts-dispatcher cds-ts-repository nodejs sap sap-btp sap-cap typescript ui5

Last synced: about 1 year ago
JSON representation

SAP CAP TypeScript entity handler dispatcher.

Awesome Lists containing this project

README

          

CDS-TS-Dispatcher

![SAP](https://img.shields.io/badge/SAP-0FAAFF?style=for-the-badge&logo=sap&logoColor=white)
![ts-node](https://img.shields.io/badge/ts--node-3178C6?style=for-the-badge&logo=ts-node&logoColor=white)
![Node.js](https://img.shields.io/badge/Node%20js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)
![Express.js](https://img.shields.io/badge/Express%20js-000000?style=for-the-badge&logo=express&logoColor=white)
![json](https://img.shields.io/badge/json-5E5C5C?style=for-the-badge&logo=json&logoColor=white)
![npm](https://img.shields.io/badge/npm-CB3837?style=for-the-badge&logo=npm&logoColor=white)

![NPM Downloads](https://img.shields.io/npm/dy/@dxfrontier/cds-ts-dispatcher?logo=npm)
![NPM Downloads](https://img.shields.io/npm/dm/%40dxfrontier%2Fcds-ts-dispatcher?logo=npm)
![NPM Version](https://img.shields.io/npm/v/%40dxfrontier%2Fcds-ts-dispatcher?logo=npm)

![Tests](https://img.shields.io/github/actions/workflow/status/dxfrontier/cds-ts-dispatcher/tests.yml?logo=git&label=test%20(unit%2C%20e2e%2C%20integration))
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/dxfrontier/cds-ts-dispatcher/deployment.yaml?logo=git)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/dxfrontier/cds-ts-dispatcher/main?logo=git)
![GitHub issues](https://img.shields.io/github/issues/dxfrontier/cds-ts-dispatcher?logo=git)
![GitHub contributors](https://img.shields.io/github/contributors/dxfrontier/cds-ts-dispatcher?logo=git)
![GitHub Repo stars](https://img.shields.io/github/stars/dxfrontier/cds-ts-dispatcher?style=flat&logo=git)

The goal of **CDS-TS-Dispatcher** is to significantly reduce the boilerplate code required to implement **Typescript handlers** provided by the SAP CAP framework.

## Table of Contents

- [Table of Contents](#table-of-contents)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [`Option 1 :` Install CDS-TS-Dispatcher - `New project`](#option-1--install-cds-ts-dispatcher---new-project)
- [`Option 2 :` Install CDS-TS-Dispatcher - `Existing TypeScript project`](#option-2--install-cds-ts-dispatcher---existing-typescript-project)
- [`Generate CDS Typed entities`](#generate-cds-typed-entities)
- [`Important`](#important)
- [`Migration:` from @sap/cds `v7` to `v8`](#migration-from-sapcds-v7-to-v8)
- [Usage](#usage)
- [`Architecture`](#architecture)
- [`CDSDispatcher`](#cdsdispatcher)
- [`Decorators`](#decorators)
- [`Class`](#class)
- [@EntityHandler](#entityhandler)
- [@ServiceLogic](#servicelogic)
- [@Repository](#repository)
- [`[Optional]` - CDS-TS-Repository - BaseRepository](#optional---cds-ts-repository---baserepository)
- [@UnboundActions](#unboundactions)
- [@Use](#use)
- [`Field`](#field)
- [@Inject](#inject)
- [@Inject(`CDS_DISPATCHER.SRV`)](#injectcds_dispatchersrv)
- [@Inject(CDS\_DISPATCHER.OUTBOXED\_SRV)](#injectcds_dispatcheroutboxed_srv)
- [`Parameter`](#parameter)
- [@Req](#req)
- [@Res](#res)
- [@Results / @Result](#results--result)
- [@Next](#next)
- [@Error](#error)
- [@Jwt](#jwt)
- [@IsPresent](#ispresent)
- [@IsRole](#isrole)
- [@IsColumnSupplied](#iscolumnsupplied)
- [@GetQuery](#getquery)
- [@GetRequest](#getrequest)
- [@SingleInstanceSwitch](#singleinstanceswitch)
- [@ValidationResults](#validationresults)
- [@Locale](#locale)
- [@Env](#env)
- [@Msg](#msg)
- [`Method`-`active entity`](#method-active-entity)
- [`Before`](#before)
- [@BeforeCreate](#beforecreate)
- [@BeforeRead](#beforeread)
- [@BeforeUpdate](#beforeupdate)
- [@BeforeDelete](#beforedelete)
- [@BeforeAll](#beforeall)
- [`After`](#after)
- [@AfterCreate](#aftercreate)
- [@AfterRead](#afterread)
- [@AfterReadEachInstance](#afterreadeachinstance)
- [@AfterUpdate](#afterupdate)
- [@AfterDelete](#afterdelete)
- [@AfterAll](#afterall)
- [`On`](#on)
- [@OnCreate](#oncreate)
- [@OnRead](#onread)
- [@OnUpdate](#onupdate)
- [@OnDelete](#ondelete)
- [@OnAction](#onaction)
- [@OnFunction](#onfunction)
- [@OnEvent](#onevent)
- [@OnSubscribe](#onsubscribe)
- [@OnError](#onerror)
- [@OnBoundAction](#onboundaction)
- [@OnBoundFunction](#onboundfunction)
- [@OnAll](#onall)
- [`Method`-`draft entity`](#method-draft-entity)
- [`Before`](#before-1)
- [@BeforeNewDraft](#beforenewdraft)
- [@BeforeCancelDraft](#beforecanceldraft)
- [@BeforeEditDraft](#beforeeditdraft)
- [@BeforeSaveDraft](#beforesavedraft)
- [`After`](#after-1)
- [@AfterNewDraft](#afternewdraft)
- [@AfterCancelDraft](#aftercanceldraft)
- [@AfterEditDraft](#aftereditdraft)
- [@AfterSaveDraft](#aftersavedraft)
- [`On`](#on-1)
- [@OnNewDraft](#onnewdraft)
- [@OnCancelDraft](#oncanceldraft)
- [@OnEditDraft](#oneditdraft)
- [@OnSaveDraft](#onsavedraft)
- [`Other draft decorators`](#other-draft-decorators)
- [`Method`-`helpers`](#method-helpers)
- [@AfterReadSingleInstance](#afterreadsingleinstance)
- [@Prepend](#prepend)
- [@Validate](#validate)
- [@FieldsFormatter](#fieldsformatter)
- [@ExecutionAllowedForRole](#executionallowedforrole)
- [@Use](#use-1)
- [@CatchAndSetErrorCode](#catchandseterrorcode)
- [@CatchAndSetErrorMessage](#catchandseterrormessage)
- [`Deployment` to BTP using MTA](#deployment-to-btp-using-mta)
- [`Best practices` \& `tips`](#best-practices--tips)
- [`Samples`](#samples)
- [Contributing](#contributing)
- [License](#license)
- [Authors](#authors)

## Prerequisites

Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/), `typescript`, `ts-node` globally:

```bash
npm install -g @sap/cds-dk typescript ts-node
```

## Installation

### `Option 1 :` Install CDS-TS-Dispatcher - `New project`

Use the following steps if you want to create a new **SAP CAP project.**

1. Create new folder :

```bash
mkdir project
cd project
```

2. Initialize the CDS folder structure :

```bash
cds init
```

3. Add `TypeScript` and [CDS-Typer](#generate-cds-typed-entities) to your npm package.json:

```bash
cds add typescript
```

4. Add `CDS-TS-Dispatcher` to your npm package.json :

```bash
npm install @dxfrontier/cds-ts-dispatcher
```

5. It is recommended to use the following **tsconfig.json** properties:

```json
{
"compilerOptions": {
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
"resolveJsonModule": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,

"strict": true,

"experimentalDecorators": true,
"emitDecoratorMetadata": true,

"target": "ES2021",
"module": "NodeNext",
"moduleResolution": "NodeNext",

"outDir": "./gen/srv",
"rootDir": ".",

"paths": {
"#cds-models/*": ["./@cds-models/*/index.ts"]
}
},
"include": ["./srv", "./@dispatcher"]
}
```

6. Install packages

```bash
npm install
```

7. Run the `CDS-TS` server

```bash
cds-ts w
```

> [!IMPORTANT]
> CDS-TS-Dispatcher uses `@sap/cds`, `@sap/cds-dk` [version 8](https://cap.cloud.sap/docs/releases/jun24)

(back to top)

### `Option 2 :` Install CDS-TS-Dispatcher - `Existing TypeScript project`

Use the following steps if you want to add only the **@dxfrontier/cds-ts-dispatcher to an existing project :**

```bash
npm install @dxfrontier/cds-ts-dispatcher
```

It is recommended to use the following **tsconfig.json** properties:

```json
{
"compilerOptions": {
"esModuleInterop": true,
"skipLibCheck": true,
"allowJs": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",

"experimentalDecorators": true,
"emitDecoratorMetadata": true,

"strict": true,

"lib": ["es2022"],

"outDir": "./gen/srv"
},
"include": ["./srv"]
}
```

> [!WARNING]
> If below message appears
>
> ```bash
> -----------------------------------------------------------------------
> WARNING: Package '@sap/cds' was loaded from different installations: [
> '***/node_modules/@sap/cds/lib/index.js',
> '***/node_modules/@dxfrontier/cds-ts-dispatcher/node_modules/@sap/cds/lib/index.js'
> ] Rather ensure a single install only to avoid hard-to-resolve errors.
> -----------------------------------------------------------------------
> ```
>
> Run the following command :
>
> ```bash
> npm install -g @sap/cds-dk@latest
> ```

(back to top)

### `Generate CDS Typed entities`

Execute the following commands :

```bash
cds add typer
```

```bash
npm install
```

> [!TIP]
> If above option is being used, this means whenever we change a `.CDS` file the changes will reflect in the generated `@cds-models` folder.

(back to top)

#### `Important`

> [!IMPORTANT]
> Import always the `generated entities` from the `service` folders and not from the `index.ts`

![alt text](https://github.com/dxfrontier/markdown-resources/blob/main/common/cds_typer_entities_@cds-models.png?raw=true)

> [!TIP]
> By default cds-typer will create in your `package.json` a quick path alias like :
>
> ```json
> "imports": {
> "#cds-models/*": "./@cds-models/*/index.js"
> }
> ```
>
> **Use import helper to import entities from `#cds-models` like example :**
>
> - **`import { Book } from '#cds-models/CatalogService';`**

(back to top)

### `Migration:` from @sap/cds `v7` to `v8`

Use the following steps if you want to migrate from `@sap/cds@7` to `@sap/cds@8`:

1. Verify you've installed the `cds@v8` globally by running the following command:

```bash
cds -v -i
```

| packages | version |
| ---------------------- | ------------------------------------------------------------------------------ |
| @cap-js/asyncapi | 1.0.1 |
| @cap-js/cds-typer | 0.24.0 |
| @cap-js/cds-types | 0.6.4 |
| @cap-js/openapi | 1.0.4 |
| @cap-js/sqlite | 1.7.3 |
| `@sap/cds` | `8.1.0` |
| @sap/cds-compiler | 5.1.2 |
| `@sap/cds-dk (global)` | `8.0.2` |
| @sap/cds-fiori | 1.2.7 |
| @sap/cds-foss | 5.0.1 |
| @sap/cds-lsp | 8.0.0 |
| @sap/cds-mtxs | 1.18.2 |
| @sap/eslint-plugin-cds | 3.0.4 |
| Node.js | v22.4.1 |

> [!TIP]
> If you see a smaller version than `@sap/cds-dk (global)` `8.0.2` run the following command :
>
> ```bash
> npm install -g @sap/cds-dk@latest
> ```

2. Run the following command inside of your project:

```bash
cds add typescript
```

> [!TIP]
> Command above will add the following packages:
>
> - `@types/node`
> - `@cap-js/cds-types`
> - `@cap-js/cds-typer`
> - `typescript`

3. After running command above the `package.json` will look similar to :

```json
{
"dependencies": {
"@dxfrontier/cds-ts-dispatcher": "^4.0.0",
"@dxfrontier/cds-ts-repository": "^5.1.6",
"@sap/cds": "^8.7.2"
},
"devDependencies": {
"@cap-js/sqlite": "^1.8.0",
"@cap-js/cds-types": "^0.9.0",
"typescript": "^5.5.4",
"@types/node": "^22.1.0",
"@cap-js/cds-typer": "0.33.0"
},
"scripts": {
"start": "cds-serve",
"watch": "cds-ts w",
},
}
```

> [!IMPORTANT]
> You might delete the `node_modules` folder and `package-lock.json` in case `npm run watch` fails working.
>
> Re-run the following command :
>
> ```bash
> npm install
> ```

## Usage

### `Architecture`

**We recommend adhering** to the **Controller-Service-Repository** design pattern using the following folder structure:

1. [EntityHandler](#entityhandler) `(Controller)` - Responsible for managing the REST interface to the business logic implemented in [ServiceLogic](#servicelogic)
2. [ServiceLogic](#servicelogic) `(Service)` - Contains business logic implementations
3. [Repository](#repository) `(Repository)` - This component is dedicated to handling entity manipulation operations by leveraging the power of [CDS-QL](https://cap.cloud.sap/docs/node.js/cds-ql).

`Controller-Service-Repository` suggested folder structure

![alt text](https://github.com/dxfrontier/markdown-resources/blob/main/cds-ts-dispatcher/architecture_folder_structure.png?raw=true) <= expanded folders => ![alt text](https://github.com/dxfrontier/markdown-resources/blob/main/cds-ts-dispatcher/architecture_folder_structure_expanded.png?raw=true)

(back to top)

> [!TIP]
> You can have a look over the [CDS-TS-Dispatcher-Samples](https://github.com/dxfrontier/cds-ts-samples) where we use the Controller-Service-Repository pattern and Dispatcher.

### `CDSDispatcher`

**CDSDispatcher**(`entities` : `Constructable[]`)

The `CDSDispatcher` constructor allows you to create an instance for dispatching and managing entities.

`Parameters`

- `entities (Array)`: An array of **[Entity handler](#entityhandler)(s)** (Constructable) that represent the entities in the CDS.

`Method`

- `initialize`: The `initialize` method of the `CDSDispatcher` class is used to initialize **[Entity handler](#entityhandler)(s)** and all of their dependencies : [Services](#servicelogic), [Repositories](#repository), [UnboundActions](#unboundactions)

`Example`

```typescript
import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';

export = new CDSDispatcher([
// Entities
BookHandler,
ReviewHandler,
BookStatsHandler,
// Draft
BookEventsHandler,
// Unbound actions
UnboundActionsHandler,
]).initialize();

// or use
// module.exports = new CDSDispatcher([ ...
```

`Visual image`

(back to top)

### `Decorators`

#### `Class`

##### @EntityHandler

The `@EntityHandler` decorator is utilized at the `class-level` to annotate a class with:

1. A specific `entity` that will serve as the base entity for all handler decorators within the class.
2. `'*'` as `all entities` that will serve as the base entity for all handler decorators within the class.

`Overloads`

| Method | Parameters | Description |
| :----------------------------------- | :--------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1. EntityHandler(`entity`: CDSTyper) | Must be a `CDS-Typer` generated class | It ensures that all handlers within the class operate with the specified `entity context`. |
| 2. EntityHandler(`entity`: `'*'`) | A wildcard `'*'` indicating all entities | It ensures that all handlers within the class operate with a generic context indicating that registered events will be triggered for all `all entities` (`active entities` and `draft entities`)

Excluded will be [@OnAction()](#onaction), [@OnFunction()](#onfunction), [@OnEvent()](#onevent), [@OnError()](#onerror) as these actions belongs to the Service itself. |

`Parameters`

- `entity (CDSTyperEntity | '*')`: A specialized class generated using the [CDS-Typer](#generate-cds-typed-entities) or generic wild card `'*'` applicable to all entities.

`Example 1` using `CDS-Typer`

```typescript
import { EntityHandler } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'
}
```
> [!TIP]
> After creation of `BookHandler` class, you can `import it` into the [CDSDispatcher](#cdsdispatcher).
>
> ```typescript
> import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';
>
> export = new CDSDispatcher([
> // Entities
> BookHandler,
> // Unbound actions
> // ...
> ]).initialize();
> ```

`Example 2` using `*` wildcard indicating that events will be triggered for all entities

```typescript
import { EntityHandler, CDS_DISPATCHER } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(CDS_DISPATCHER.ALL_ENTITIES) // or use the '*'
export class AllEntities {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered on all entities using wildcard '*'
}
```
> [!TIP]
> After creation of `AllEntities` class, you can `import it` into the [CDSDispatcher](#cdsdispatcher).
>
> ```typescript
> import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';
>
> export = new CDSDispatcher([
> // Entities
> AllEntities,
> // Unbound actions
> // ...
> ]).initialize();
> ```

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the class.

(back to top)

##### @ServiceLogic

**@ServiceLogic()**

The `@ServiceLogic` decorator is utilized at the `class-level` to annotate a `class` as a specialized class containing only business logic.

`Example`

```typescript
import { ServiceLogic } from '@dxfrontier/cds-ts-dispatcher';

@ServiceLogic()
export class CustomerService {
// ...
constructor() {}
// ...
}
```

> [!TIP]
> When applying `@ServiceLogic()` decorator, the class becomes eligible to be used with [Inject](#inject) decorator for `Dependency injection`.

(back to top)

##### @Repository

**@Repository()**

The `@Repository` decorator is utilized as a `class-level` annotation that designates a particular `class` as a specialized `Repository`, this class should contain only [CDS-QL](https://cap.cloud.sap/docs/node.js/cds-ql) code.

```typescript
import { Repository } from '@dxfrontier/cds-ts-dispatcher';

@Repository()
export class CustomerRepository {
// ...
constructor() {}
// ...
}
```

> [!TIP]
> When applying `@Repository()` decorator, the class becomes eligible to be used with [Inject](#inject) decorator for `Dependency injection`.

###### `[Optional]` - CDS-TS-Repository - BaseRepository

The **[CDS-TS-Repository - BaseRepository](https://github.com/dxfrontier/cds-ts-repository)** was designed to reduce the boilerplate code required to implement data access layer for persistance entities.

It simplifies the implementation by offering a set of ready-to-use actions for interacting with the database. These actions include:

- `.create()`: Create new records in the database.
- `.getAll()`: Retrieve all records from the database.
- `.find()`: Query the database to find specific data.
- `.delete()`: Remove records from the database.
- `.exists()`: Check the existence of data in the database.
- and many more ...

`Example`

```typescript
import { Repository } from '@dxfrontier/cds-ts-dispatcher';
import { BaseRepository } from '@dxfrontier/cds-ts-repository';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@Repository()
export class CustomerRepository extends BaseRepository {
constructor() {
super(MyEntity);
}

public async aMethod() {
const created = await this.create(...)
const createdMany = await this.createMany(...)
const updated = await this.update(...)
// ...
}
}
```

To get started, refer to the official documentation **[CDS-TS-Repository - BaseRepository](https://github.com/dxfrontier/cds-ts-repository)**.
Explore the capabilities it offers and enhance your data access layer with ease.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the class.

(back to top)

##### @UnboundActions

**@UnboundActions()**

The `@UnboundActions` decorator is utilized at the `class-level` to annotate a `class` as a specialized class which will be used only for **_Unbound actions._**

The following decorators can be used inside of `@UnboundActions()` :

- [@OnAction()](#onaction)
- [@OnFunction()](#onfunction)
- [@OnEvent()](#onevent)
- [@OnError()](#onerror)
- [@OnSubscribe()](#onsubscribe)

`Example`

```typescript
import { UnboundActions, OnAction, OnFunction, OnEvent, Req, Next, Error, OnSubscribe } from '@dxfrontier/cds-ts-dispatcher';
import { MyAction, MyFunction, MyEvent } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { ActionRequest, ActionReturn, Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

@UnboundActions()
export class UnboundActionsHandler {
// ... @Inject dependencies, if needed.

constructor() {}

// Unbound action
@OnAction(MyAction)
private async onActionMethod(
@Req() req: ActionRequest,
@Next() next: NextEvent,
): ActionReturn {
// ...
}

// Unbound Function
@OnFunction(MyFunction)
private async onFunctionMethod(
@Req() req: ActionRequest,
@Next() next: NextEvent,
): ActionReturn {
// ...
}

// Unbound event
@OnEvent(MyEvent)
private async onEventMethod(@Req() req: Request) {
// ...
}

// Unbound error
@OnError()
private onErrorMethod(@Error() err: Error, @Req() req: Request) {
// ...
}

// Unbound event
@OnSubscribe({
eventName: 'event_name'
type: 'SAME_NODE_PROCESS'
})
private async onSubscribe(@Req() req: Request<{foo: string, bar: number}>): Promise {
//
}
}
```

`Imported it` in the [CDSDispatcher](#cdsdispatcher)

```typescript
import { CDSDispatcher } from '@dxfrontier/cds-ts-dispatcher';

export = new CDSDispatcher([ UnboundActionsHandler, ...])
// or
// use module.exports = new CDSDispatcher( ... )
```

> [!NOTE]
> The reason behind introducing a distinct decorator for `Unbound actions` stems from the fact that these actions are not associated with any specific `Entity` but instead these actions belongs to the Service itself.

(back to top)

##### @Use

**@Use**(`...Middleware[]`)

The `@Use` decorator simplifies the integration of middlewares into your classes.

When `@Use` decorator applied at the `class-level` this decorator inject middlewares into the class and gain access to the `req: Request` and `next: NextMiddleware` middleware across all events `(@AfterRead, @OnRead ...)` within that class.

Middleware decorators can perform the following tasks:

- Execute any code.
- Make changes to the request object.
- End the request-response cycle.
- Call the next middleware function in the stack.
- If the current middleware function does not end the request-response cycle, it must call `next()` to pass control to the next middleware function. Otherwise, the request will be left hanging.

`Parameters`

- `...Middleware[])`: Middleware classes to be injected.

`Example:` middleware implementation:

```typescript
import type { MiddlewareImpl, NextMiddleware, Request } from '@dxfrontier/cds-ts-dispatcher';

export class MiddlewareClass implements MiddlewareImpl {
public async use(req: Request, next: NextMiddleware) {
console.log('Middleware use method called.');

await next(); // call next middleware
}
}
```

> [!TIP]
>
> Inside of the decorator `use` method you can use [@CatchAndSetErrorCode](#catchandseterrorcode) and [@CatchAndSetErrorMessage](#catchandseterrormessage) decorators.

`Example` usage:

```typescript
import { EntityHandler, Use, Inject, CDS_DISPATCHER } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';
import { Middleware1, Middleware2, MiddlewareN } from 'YOUR_MIDDLEWARE_LOCATION';

import type { Service } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
@Use(Middleware1, Middleware2, MiddlewareN)
export class CustomerHandler {
// ...
@Inject(CDS_DISPATCHER.SRV) private srv: Service;
// ...
constructor() {}
// ...
}
```

> [!TIP]
>
> 1. Think of it _(middleware)_ like as a reusable class, enhancing the functionality of all events within the class.
> 2. Middlewares when applied with `@Use` are executed before the normal events.
> 3. If you need to apply middleware to a `method` you should use the method specific [@Use](#use-1) decorator .

> [!WARNING]
> If `req.reject()` is used inside of middleware this will stop the stack of middlewares, this means that next middleware will not be executed.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the class.

(back to top)

#### `Field`

##### @Inject

**@Inject**(`serviceIdentifier: ServiceIdentifierOrFunc`)

The `@Inject` decorator is utilized as a `field-level` decorator and allows you to inject dependencies into your classes.

`Parameters`

- `serviceIdentifier(ServiceIdentifierOrFunc)`: A Class representing the service to inject.

`Example`

```typescript
import { EntityHandler, Inject, CDS_DISPATCHER } from "@dxfrontier/cds-ts-dispatcher";
import type { Service } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
export class CustomerHandler {
...
@Inject(CustomerService) private customerService: CustomerService
@Inject(CustomerRepository) private customerService: CustomerRepository
@Inject(AnyOtherInjectableClass) private repository: AnyOtherInjectableClass

@Inject(CDS_DISPATCHER.SRV) private srv: Service
// ...
constructor() {}
// ...
}
```

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the class.

(back to top)

##### @Inject(`CDS_DISPATCHER.SRV`)

**@Inject**(`CDS_DISPATCHER.SRV`) private srv: `Service`

This specialized `@Inject` can be used as a `constant` in and contains the `CDS.ApplicationService` for further enhancements.

It can be injected in the following :

- [@EntityHandler()](#entityhandler)
- [@ServiceLogic()](#servicelogic)
- [@Repository()](#repository)
- [@UnboundActions()](#unboundactions)

`Example`

```typescript
import { EntityHandler, Inject, CDS_DISPATCHER } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Service } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
// OR @ServiceLogic()
// OR @Repository()
// OR @UnboundActions()
export class CustomerHandler {
// @Inject dependencies
@Inject(CDS_DISPATCHER.SRV) private readonly srv: Service;

constructor() {}
// ...
}
```

> [!TIP]
> The CDS.ApplicationService can be accessed trough `this.srv`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

##### @Inject(CDS_DISPATCHER.OUTBOXED_SRV)

**@Inject**(`CDS_DISPATCHER.OUTBOXED_SRV`) private srv: `Service`

This specialized `@Inject` can be used as a `constant` and contains the `CDS.outboxed` service.

It can be injected in the following :

- [@EntityHandler()](#entityhandler)
- [@ServiceLogic()](#servicelogic)
- [@Repository()](#repository)
- [@UnboundActions()](#unboundactions)

`Example`

```typescript
import { EntityHandler, Inject, CDS_DISPATCHER } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Service } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
// OR @ServiceLogic()
// OR @Repository()
// OR @UnboundActions()
export class CustomerHandler {
// @Inject dependencies
@Inject(CDS_DISPATCHER.OUTBOXED_SRV) private readonly outboxedSrv: Service;

constructor() {}
// ...
}
```

> [!TIP]
> More info about `outboxed` ca be found at [SAP CAP Node.js Outboxed](https://cap.cloud.sap/docs/node.js/outbox)

> [!TIP]
> The CDS.ApplicationService can be accessed trough `this.outboxedSrv`

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

#### `Parameter`

##### @Req

**@Req()**

The `@Req` decorator is utilized at the `parameter level` to annotate a parameter with the `Request` object, providing access to request-related information of the current event.

`Return`

- `Request`: An instance of `@sap/cds` - `Request`

`Example`

```typescript
import { EntityHandler, Req, Results } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// ... all events like @AfterRead, @BeforeRead ...

@AfterRead()
private async aMethod(@Req() req: Request, @Results() results: MyEntity[]) {
// ... req...
}
}
```

(back to top)

##### @Res

**@Res()**

The `@Res` decorator is utilized at the `parameter level` to annotate a parameter with the `Request.http.res - (Response)` object, providing access to response-related information of the current event and it can be used to enhance the `Response`.

`Return`

- `RequestResponse`: An instance of `RequestResponse` providing you response-related information.

`Example`

```typescript
import { EntityHandler, Req, Results } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request, RequestResponse } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// ... all events like @AfterRead, @BeforeRead ...

@AfterRead()
private async aMethod(@Req() req: Request, @Res() response: RequestResponse, @Results() results: MyEntity[]) {
// Example: we assume we want to add a new header language on the response
// We use => res.setHeader('Accept-Language', 'DE_de');
}
}
```

> [!TIP]
> Decorator `@Res` can be used in all [After](#after), [Before](#before) and [On](#on) events.

(back to top)

##### @Results / @Result

**@Results()** / **@Result**

The `@Results` decorator is utilized at the `parameter level` to annotate a parameter with the request `Results`.

`Return`

- `Array / object`: Contains the OData Request `Body`.

`Example`

```typescript
import { EntityHandler, Req, Results } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// ... all events like @AfterRead, @BeforeRead ...

@AfterRead()
private async aMethod(@Req() req: Request, @Results() results: MyEntity[]) {
// ...
}
}
```

> [!TIP]
> When using [@AfterCreate()](#aftercreate), [@AfterUpdate()](#afterupdate) and [@AfterDelete()](#afterdelete) it's recommended to use the `@Result` decorator for single object result and `@Results` for arrays of objects.

```ts
@AfterCreate()
@AfterUpdate()
private async aMethod(
@Result() result: Book, // <== @Result() decorator used to annotate it's a an object and not an array
@Req() req: Request,
) {
// ...
}

@AfterRead()
private async aMethod(
@Results() result: Book[], // <== @Results() decorator used to annotate as array of objects
@Req() req: Request,
) {
// ...
}

@AfterDelete()
private async aMethod(
@Result() deleted: boolean, // <== @Result() decorator used to annotate as a boolean
@Req() req: Request,
) {
// ...
}
```

> [!TIP]
> Decorators `@Results()` and `@Result()` can be applied to all [After](#after) events.

(back to top)

##### @Next

**@Next()**

The `@Next` decorator is utilized at the `parameter level` to annotate a parameter with the `Next` function, which is used to proceed to the next event in the chain of execution.

`Return`

- `NextEvent`: The next event in chain to be called.

`Example`

```typescript
import { EntityHandler, Req, Results } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// ... all events like @AfterRead, @BeforeRead, @OnCreate ...

@OnCreate()
public async onCreate(@Req() req: Request, @Next() next: NextEvent) {
return next();
}
}
```

> [!TIP]
> Decorator `@Next` can be applied to all [On](#on), [On - draft](#on-1) event decorators.

(back to top)

##### @Error

**@Error()**

The `@Error` decorator is utilized at the `parameter level` to annotate a parameter with the `Error` and contains information regarding the failed `Request`.

`Return`

- `Error`: An instance of type `Error`.

`Example`

```typescript
import { UnboundActions, Req, Error } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@UnboundActions()
export class UnboundActionsHandler {
// ...
constructor() {}

@OnError()
public onError(@Error() err: Error, @Req() req: Request): void {
// ...
}
}
```

> [!TIP]
> Decorator `@Error` can be applied to [@OnError()](#onerror) decorator which resides inside of the [@UnboundActions()](#unboundactions).

##### @Jwt

**@Jwt()**

The `@Jwt` decorator is utilized at the `parameter level`. It will retrieve the to retrieve `JWT` from the `Request` that is based on the node `req.http.req - IncomingMessage`.

Fails if no authorization header is given or has the wrong format.

`Return`

- `string` | `undefined` : The retrieved `JWT token` or undefined if no token was found.

`Example`

```typescript
import { EntityHandler, Req, Results, Jwt } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// ... all events like @AfterRead, @BeforeRead ...

@AfterRead()
private async aMethod(@Req() req: Request, @Results() results: MyEntity[], @Jwt() jwt: string | undefined) {
// ... req...
}
}
```

> [!IMPORTANT]
> Expected format is `Bearer `.

(back to top)

##### @IsPresent

**@IsPresent\(key: Key, property: PickQueryPropsByKey\)**

The `@IsPresent` decorator is utilized at the `parameter level`. It allows you to verify the existence of a specified Query `property` values.

`Parameters`

- `key (string)`: Specifies the type of query operation. Accepted values are `INSERT`, `SELECT`, `UPDATE`, `UPSERT`, `DELETE`.
- `property (string)`: Specifies the property based on the `key`.

`Return`

- `boolean`: This decorator returns `true` if `property` `value` is filled, `false` otherwise

`Example`

```typescript
import { EntityHandler, Req, Results, IsPresent } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
class BookHandler {
// ...
constructor() {}

@AfterRead()
private async aMethod(
@Req() req: Request,
@Results() results: MyEntity[],

@IsPresent('SELECT', 'columns') columnsPresent: boolean,
) {
if (columnsPresent) {
// ...
}

// ...
}
}
```

> [!TIP]
> Decorator [@IsPresent()](#ispresent) works well with [@GetQuery()](#getquery).

(back to top)

##### @IsRole

**@IsRole(...roles: string[])**

The `@IsRole` decorator is utilized at the `parameter level`. It allows you to verify
if the `User` has assigned a given role.

It applies an logical `OR` on the specified roles, meaning it checks if at `least one` of the specified roles is assigned

`Parameters`

- `role (...string[])`: An array of role names to check if are assigned.

`Return`

- `boolean`: This decorator returns `true` if at least one of the specified roles is assigned to the current request user, otherwise `false`.

`Example`

```typescript
import { EntityHandler, Req, Results, IsPresent, IsRole } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
class BookHandler {
// ...
constructor() {}

@AfterRead()
private async aMethod(
@Req() req: Request,
@Results() results: MyEntity[],

@IsRole('role', 'anotherRole') roleAssigned: boolean,
) {
if (roleAssigned) {
// ...
}

// ...
}
}
```

> [!TIP]
> The role names correspond to the values of `@requires` and the `@restrict.grants.to` annotations in your `CDS` models.

(back to top)

##### @IsColumnSupplied

**@IsColumnSupplied\(field : keyof T)**

The `@IsColumnSupplied(field : keyof T)` decorator is utilized at the `parameter level`. It allows your to verify the existence of a column in the `SELECT`, `INSERT` or `UPSERT` Query.

`Parameters`

- `field (string)`: A string representing the name of the column to be verified.

`Type Parameters`

- `T`: The entity type (e.g., `MyEntity`) representing the table or collection on which the decorator operates. This allows TypeScript to enforce type safety for the field parameter.

`Return` :

- `boolean`: This decorator returns `true` if `field / column` was found, `false` otherwise

`Example`

```typescript
import { EntityHandler, Req, Results, IsPresent } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
class BookHandler {
// ...
constructor() {}

@AfterRead()
private async aMethod(
@Req() req: Request,
@Results() results: MyEntity[],

@IsColumnSupplied('price') priceSupplied: boolean,
) {
if (priceSupplied) {
// ...
}

// ...
}
}
```

(back to top)

##### @GetQuery

**@GetQuery\(key: Key, property: PickQueryPropsByKey\)**

The `@GetQuery` decorator is utilized at the `parameter level`. It allows you to retrieve Query `property` values.

`Parameters`

- `key (string)`: Specifies the type of query operation. Accepted values are `INSERT`, `SELECT`, `UPDATE`, `UPSERT`, `DELETE`.
- `property (string)`: Specifies the property based on the `key`.

`Return`: Varies based on the specified property :

-

SELECT

- @GetQuery(`'SELECT'`, `'columns'`) columns: `GetQueryType['columns']['forSelect']`
- @GetQuery(`'SELECT'`, `'distinct'`) distinct: `GetQueryType['distinct']`
- @GetQuery(`'SELECT'`, `'excluding'`) excluding: `GetQueryType['excluding']`
- @GetQuery(`'SELECT'`, `'from'`) from: `GetQueryType['from']['forSelect']`
- @GetQuery(`'SELECT'`, `'groupBy'`) groupBy: `GetQueryType['groupBy']`
- @GetQuery(`'SELECT'`, `'having'`) having: `GetQueryType['having']`
- @GetQuery(`'SELECT'`, `'limit'`) limit: `GetQueryType['limit']`
- @GetQuery(`'SELECT'`, `'limit.rows'`) limitRows: `GetQueryType['limit']['rows']`
- @GetQuery(`'SELECT'`, `'limit.offset'`) limitOffset: `GetQueryType['limit']['offset']`
- @GetQuery(`'SELECT'`, `'mixin'`) mixin: `GetQueryType['mixin']`
- @GetQuery(`'SELECT'`, `'one'`) one: `GetQueryType['one']`
- @GetQuery(`'SELECT'`, `'orderBy'`) orderBy: `GetQueryType['orderBy']`
- @GetQuery(`'SELECT'`, `'where'`) where: `GetQueryType['where']`

-

INSERT

- @GetQuery(`'INSERT'`, `'as'`) as: `GetQueryType['as']`
- @GetQuery(`'INSERT'`, `'columns'`) columns: `GetQueryType['columns']['forInsert']`
- @GetQuery(`'INSERT'`, `'entries'`) entries: `GetQueryType['entries']`
- @GetQuery(`'INSERT'`, `'into'`) into: `GetQueryType['into']`
- @GetQuery(`'INSERT'`, `'rows'`) rows: `GetQueryType['rows']`
- @GetQuery(`'INSERT'`, `'values'`) values: `GetQueryType['values']`

-

UPDATE

- @GetQuery(`'UPDATE'`, `'data'`) data: `GetQueryType['data']`
- @GetQuery(`'UPDATE'`, `'entity'`) entity: `GetQueryType['entity']`
- @GetQuery(`'UPDATE'`, `'where'`) where: `GetQueryType['where']`

-

UPSERT

- @GetQuery(`'UPSERT'`, `'columns'`) columns: `GetQueryType['columns'][forUpsert]`
- @GetQuery(`'UPSERT'`, `'entries'`) entries: `GetQueryType['entries']`
- @GetQuery(`'UPSERT'`, `'into'`) into: `GetQueryType['into']`
- @GetQuery(`'UPSERT'`, `'rows'`) rows: `GetQueryType['rows']`
- @GetQuery(`'UPSERT'`, `'values'`) values: `GetQueryType['values']`

-

DELETE

- @GetQuery(`'DELETE'`, `'from'`) from: `GetQueryType['from'][forDelete]`
- @GetQuery(`'DELETE'`, `'where'`) columns: `GetQueryType['where']`

`Example`

```typescript
import { EntityHandler, Req, Results, IsPresent, GetQuery } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request, GetQueryType } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
class BookHandler {
// ...
constructor() {}

@AfterRead()
private async aMethod(
@Req() req: Request,
@Results() results: MyEntity[],

// Check existence of columns
@IsPresent('SELECT', 'columns') columnsPresent: boolean,

// Get columns
@GetQuery('SELECT', 'columns') columns: GetQueryType['columns']['forSelect'],

@GetQuery('SELECT', 'orderBy') orderBy: GetQueryType['orderBy'],
@GetQuery('SELECT', 'groupBy') groupBy: GetQueryType['groupBy'],
) {
if (columnsPresent) {
// do something with columns values
// columns.forEach(...)
}

// ...
}
}
```

> [!TIP]
> Decorator [@GetQuery()](#getquery) can be used to get the Query property and [@IsPresent()](#ispresent) can check if the Query property is empty or not.

(back to top)

##### @GetRequest

**@GetRequest(property : keyof Request)**

The `@GetRequest` decorator is utilized at the `parameter level`. It allows you to retrieve the specified `property` value from the `Request` object.

`Parameters`

- `property (string)`: Specifies the property to retrieve from the `Request` object.

`Return`: Varies based on the specified property :

- **@GetRequest**(`'entity'`) entity: `Request['entity']`,
- **@GetRequest**(`'event'`) event: `Request['event']`,
- **@GetRequest**(`'features'`) features: `Request['features']`,
- **@GetRequest**(`'headers'`) headers: `Request['headers']`,
- **@GetRequest**(`'http'`) http: `Request['http']`,
- **@GetRequest**(`'id'`) id: `Request['id']`,
- **@GetRequest**(`'locale'`) locale: `Request['locale']`,
- **@GetRequest**(`'method'`) method: `Request['method']`,
- **@GetRequest**(`'params'`) params: `Request['params']`,
- **@GetRequest**(`'query'`) query: `Request['query']`,
- **@GetRequest**(`'subject'`) subject: `Request['subject']`,
- **@GetRequest**(`'target'`) target: `Request['target']`,
- **@GetRequest**(`'tenant'`) tenant: `Request['tenant']`,
- **@GetRequest**(`'timestamp'`) timestamp: `Request['timestamp']`,
- **@GetRequest**(`'user'`) user: `Request['user']`,

`Example`

```typescript
import { EntityHandler, Results, GetRequest } from '@dxfrontier/cds-ts-dispatcher';
import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@EntityHandler(MyEntity)
class BookHandler {
// ...
constructor() {}

@AfterRead()
private async aMethod(
// @Req() req: Request, we assume we don't need the hole Request object and we need only 'locale' and 'method'
@Results() results: MyEntity[],

@GetRequest('locale') locale: Request['locale'],
@GetRequest('method') method: Request['method'],
) {
// do something with 'locale' and 'method' ...
}
}
```

> [!TIP]
> Type `Request` can be import from :
>
> ```ts
> import type { Request } from '@dxfrontier/cds-ts-dispatcher';
> ```

(back to top)

##### @SingleInstanceSwitch

**@SingleInstanceSwitch**

The `@SingleInstanceSwitch()` decorator is applied at the `parameter level`.

It allows you to manage different behaviors based on whether the request is for a `single entity instance` or `an entity set`, the parameter assigned to the decorator will behave like a **`switch`**.

`Return`

- `true` when the `Request` is `single instance`
- `false` when the `Request` is `entity set`

`Example 1`

Single request :

```typescript
import { AfterRead, SingleInstanceCapable } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterRead()
private async singeInstanceMethodAndEntitySet(@Results() results : MyEntity[], @Req() req: Request, @SingleInstanceSwitch() isSingleInstance: boolean) {
if(isSingleInstance) {
// This will be executed only when single instance is called : http://localhost:4004/odata/v4/main/MyEntity(ID=2f12d711-b09e-4b57-b035-2cbd0a023a09)
return this.customerService.handleSingleInstance(req)
}

// nothing to entity set
}
```

`Example 2`

Entity request :

```typescript
import { AfterRead, SingleInstanceCapable } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterRead()
private async singeInstanceMethodAndEntitySet(@Results() results : MyEntity[], @Req() req: Request, @SingleInstanceSwitch() isSingleInstance: boolean) {
if(isSingleInstance) {
// This will be executed only when single instance is called : http://localhost:4004/odata/v4/main/MyEntity(ID=2f12d711-b09e-4b57-b035-2cbd0a023a09)
// ...
}

// ... this will be executed when entity set is called : http://localhost:4004/odata/v4/main/MyEntity
results[0] = {
name : 'new value'
}
}
```

> [!TIP]
> Decorator `@SingleInstanceSwitch` can be used together with the following decorator events:
>
> - [@AfterRead()](#afterread)
> - [@BeforeRead()](#beforeread)
> - [@OnRead()](#onread)

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

##### @ValidationResults

**@ValidationResults**

The `@ValidationResults` decorator allows to capture and inject validation results directly into a method parameter, allowing access to `individual validation flags` within the decorated method.

When used alongside the [@Validate](#validate) decorator, it enables you to perform conditional logic based on specific validation outcomes.

`Example`

```ts
@BeforeCreate()
@Validate({ action: 'isLowercase', exposeValidatorResult: true }, 'comment')
@Validate({ action: 'endsWith', target: 'N', exposeValidatorResult: true }, 'description')
public async beforeCreate(
@Req() req: Request,
@ValidationResults() validator: ValidatorFlags<'isLowercase' | 'endsWith'>
) {
// Conditional handling based on validation flags
if (validator.isLowercase) {
// Execute logic when field `comment` is lowercase
}
else {
// Execute logic when field `comment` is not lowercase
}

if (validator.endsWith) {
// Execute logic when field `description` is endsWith with letter 'N'
}
else {
// Execute logic when field `description` doesn't endsWith with letter 'N'
}
}
```

> [!IMPORTANT]
> For `@ValidationResults` to work, each [@Validate](#validate) decorator must set the `exposeValidatorResult` option to `true`. This ensures that the validation results are available as flags in the method.

(back to top)

##### @Locale

**@Locale**

Parameter decorator used to inject locale information into a method parameter.

`Example`

```ts
@BeforeCreate()
public async beforeCreate(
@Req() req: Request,
@Locale() locale: string
) {
if (locale === 'en-US') {
// handle logic specific to the 'en-US' locale
}
}
```

(back to top)

##### @Env

**@Env\(env: PropertyStringPath\)**

The `@Env` decorator is a parameter decorator used to inject values from the cds.env configuration object directly into a method parameter.

`Parameters`

- `env (string)`: A string path representing a property from `cds.env`. This path follows the format `property string path`, which allows access to deeply nested configuration properties.
- E.g. : `'requires.db.credentials.url'` corresponds to **cds.env.requires.db.credentials.url** object.

`Type Parameters`

- `T`: The `CDS environmental variables` type (e.g., `cds env get`) representing the collection on which the decorator operates. This allows TypeScript to enforce type safety.

`Return` :

- The decorator returns the value of the specified `cds.env` property value.

`Example`

```ts
import { CDS_ENV } from '#dispatcher';

@BeforeCreate()
public async beforeCreate(
@Req() req: Request,
@Env('requires.db.credentials.url') dbUrl: CDS_ENV['requires']['db']['credentials']['url'],
// or @Env('requires.db.credentials.url') dbUrl: string
// or @Env('requires.db.credentials.url') dbUrl: any
// or any other type if you do not want to use the CDS_ENV generated types
) {
if (dbUrl) {
// handle custom logic ...
}
}
```

> [!NOTE]
> When you install cds-ts-dispatcher `(e.g. npm install @dxfrontier/cds-ts-dispatcher)` or run a general `npm install`, the following will be generated or updated :
>
> - New `@dispatcher` folder is generated at the project _**root**_.
> This folder contains the `CDS ENV TS interfaces`, generated based on the structure of your current `cds.env` project specific configuration (retrieved from `cds env get` cli command).
>
> ```text
> ...
> @dispatcher
> ...
> ```
>
> - `package.json` will be updated with a new `import`:
>
> ```json
> "imports": {
> "#dispatcher": "./@dispatcher/index.js"
> }
> ```
>
> - `tsconfig.json` will be updated:
>
> ```json
> "include": [
> "...",
> "./@dispatcher"
> ]
> ```
>
> - `.gitignore` will be updated::
>
> ```text
> ...
> @dispatcher
> ```

> [!NOTE]
> The `@dispatcher` folder is _**regenerated each time you run npm install.**_

> [!TIP]
> You can import the generated `CDS env` from the generated `@dispatcher` folder by using :
>
> ```ts
> import { CDS_ENV } from '#dispatcher';
> ```

(back to top)

##### @Msg

**@Msg()**

The `@Msg` decorator is a parameter decorator used to inject response [Emitter](#https://cap.cloud.sap/docs/guides/messaging/#typical-emitter-and-receiver-roles) response object directly into a method parameter.

`Parameters`

`Return` :

- The decorator returns an object of type :

```ts
{
event: string,
data: any,
headers: any,
inbound: boolean
}
```

`Example`

```ts
import { OnSubscribe, Messaging } from '@dxfrontier/cds-ts-dispatcher';
import type { SubscriberType } from '@dxfrontier/cds-ts-dispatcher';

@OnSubscribe({
eventName: 'AnEventName',
type: 'MESSAGE_BROKER',
})
public async onSubscribe(@Msg() msg: SubscriberType<{ foo: number; bar: string }>): Promise {
// ...
}
```
> [!IMPORTANT]
> To have the `msg` typed you can use TypeScript type `SubscriberType` where T can be a CDS event or any object

> [!TIP]
> Decorator `@Msg` should be used exclusively on decorator [@OnSubscribe](#onsubscribe).

(back to top)

#### `Method`-`active entity`

##### `Before`

Use [@BeforeCreate()](#beforecreate), [@BeforeRead()](#beforeread), [@BeforeUpdate()](#beforeupdate), [@BeforeDelete()](#beforedelete) to register handlers to run before `.on` handlers, frequently used for `validating user input.`

The handlers receive one argument:

- `req` of type `Request`

See also the official SAP JS **[CDS-Before](https://cap.cloud.sap/docs/node.js/core-services#srv-before-request) event**

> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use
>
> - `@BeforeCreateDraft()`
> - `@BeforeReadDraft()`
> - `@BeforeUpdateDraft()`
> - `@BeforeDeleteDraft()`

(back to top)

###### @BeforeCreate

**@BeforeCreate**()

`Example`

```typescript
import { BeforeCreate } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeCreate()
private async beforeCreateMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('CREATE', MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> It is important to note that the decorator `@BeforeCreate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeRead

**@BeforeRead**()

`Example`

```typescript
import { BeforeRead } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeRead()
private async beforeReadMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('READ', MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeRead()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeUpdate

**@BeforeUpdate**()

`Example`

```typescript
import { BeforeUpdate } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeUpdate()
private async beforeUpdateMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('UPDATE', MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeUpdate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeDelete

**@BeforeDelete**()

`Example`

```typescript
import { BeforeDelete } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeDelete()
private async beforeDeleteMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('DELETE', MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeDelete()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeAll

The `@BeforeAll` decorator is triggered whenever **_any CRUD (Create, Read, Update, Delete)_** event occurs, whether the entity is `active` or in `draft` mode.

`ACTIVE ENTITY`

For active entities, the @BeforeAll decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeCreate()](#beforecreate), [@AfterCreate()](#aftercreate), [@OnCreate()](#oncreate)
- `READ` [@BeforeRead()](#beforeread), [@AfterRead()](#afterread), [@OnRead()](#onread)
- `UPDATE` [@BeforeUpdate()](#beforeupdate), [@AfterUpdate()](#afterupdate), [@OnUpdate()](#onupdate)
- `DELETE` [@BeforeDelete()](#beforedelete), [@AfterDelete()](#afterdelete), [@OnDelete()](#ondelete)
- `BOUND ACTIONS` [@OnBoundAction()](#onboundaction)
- `BOUND FUNCTIONS` [@OnBoundFunction()](#onboundfunction)

`DRAFT`

For draft entities, the @BeforeAll decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeNewDraft()](#beforenewdraft), [@AfterNewDraft()](#afternewdraft), [@OnNewDraft()](#onnewdraft)
- `CANCEL` [@BeforeCancelDraft()](#beforecanceldraft), [@AfterCancelDraft()](#aftercanceldraft), [@OnCancelDraft()](#oncanceldraft)
- `EDIT` [@BeforeEditDraft()](#beforeeditdraft), [@AfterEditDraft()](#aftereditdraft), [@OnEditDraft()](#oneditdraft)
- `SAVE` [@BeforeSaveDraft()](#beforesavedraft), [@AfterSaveDraft()](#aftersavedraft), [@OnSaveDraft()](#onsavedraft)
- :heavy_plus_sign: All active entity [Before](#before), [After](#after), [On](#on) events which have a `Draft` variant.

**@BeforeAll**()

`Example 1`

In this example, the `@BeforeAll()` decorator of the `beforeAllEvents` method is executed before any CRUD operation `(CREATE, READ, UPDATE, DELETE, BOUND ACTIONS, BOUND FUNCTIONS))` on the `MyEntity` entity.

Since the class is annotated with `@EntityHandler(MyEntity)`, the decorator is scoped to this specific `entity`, meaning it will only be triggered for operations related to `MyEntity`.

```typescript
import { BeforeAll, EntityHandler } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@BeforeAll()
private async beforeAllEvents(@Req() req: Request) {
// ...
}
}
```

`Equivalent to 'JS'`

```typescript
this.before('*', MyEntity, async (req) => {
// ...
});
```

`Example 2`

In this example, the `@BeforeAll()` decorator is used in a more generic way. Unlike `Example 1`, where it applies only to `MyEntity`, this setup ensures that `beforeAllEvents` is triggered for `all entities`.

This means the method will execute before any CRUD operation on `any` `entity` handled within the class.

```typescript
import { BeforeAll, EntityHandler, CDS_DISPATCHER } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(CDS_DISPATCHER.ALL_ENTITIES)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@BeforeAll()
private async beforeAllEvents(@Req() req: Request) {
// ...
}
}
```

`Equivalent to 'JS'`

```typescript
this.before('*', '*', async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeAll()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!TIP]
> If the entity has drafts enabled `@odata.draft.enabled: true`, the `@BeforeAll` decorator will still be triggered for draft events.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

##### `After`

Use [@AfterCreate()](#aftercreate), [@AfterRead()](#afterread), [@AfterUpdate()](#afterupdate), [@AfterDelete()](#afterdelete) register handlers to run after the `.on` handlers, frequently used to `enrich outbound data.`

The handlers receive two arguments:

| Parameters | Decorator | Description |
| -------------- | --------------------------------- | -------------------------------------------------------------------------- |
| `results, req` | `@AfterRead` | An array of type `MyEntity[]` and the `Request`. |
| `result, req` | `@AfterUpdate`
`@AfterCreate` | An object of type `MyEntity` and the `Request`. |
| `deleted, req` | `@AfterDelete` | A `boolean` indicating whether the instance was deleted and the `Request`. |

> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use :
>
> - `@AfterCreateDraft()`
> - `@AfterReadDraft()`
> - `@AfterReadDraftSingleInstance()`
> - `@AfterUpdateDraft()`
> - `@AfterDeleteDraft()`

(back to top)

###### @AfterCreate

**@AfterCreate**()

`Example`

```typescript
import { AfterCreate } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterCreate()
private async afterCreateMethod(@Result() result: MyEntity, @Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('CREATE', MyEntity, async (result, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterCreate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @AfterRead

**@AfterRead**()

`Example`

```typescript
import { AfterRead, Results, Req } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterRead()
private async afterReadMethod(@Results() results: MyEntity[], @Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('READ', MyEntity, async (results, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterRead()` will be triggered based on the [EntityHandler](#entityhandler) `argument` `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @AfterReadEachInstance

**@AfterReadEachInstance**()

The `@AfterReadEachInstance` decorator is used to execute custom logic after performing a read operation on `each individual instance`. This behavior is analogous to the JavaScript `Array.prototype.forEach` method.

`Example`

```typescript
import { AfterReadEachInstance, Result, Req } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterReadEachInstance()
private async afterEach(@Result() result: MyEntity, @Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('each', MyEntity, async (result, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterReadEachInstance()` will be triggered based on the [EntityHandler](#entityhandler) `argument` `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @AfterUpdate

**@AfterUpdate**()

`Example`

Single request :

```typescript
import { AfterUpdate } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterUpdate()
private async afterUpdateMethod(@Result() result: MyEntity, @Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('UPDATE', MyEntity, async (result, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterUpdate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @AfterDelete

**@AfterDelete**()

`Example`

```typescript
import { AfterDelete} from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@AfterDelete()
private async afterDeleteMethod(@Result() deleted: boolean, @Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('DELETE', MyEntity, async (deleted, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterDelete()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @AfterAll

The `@AfterAll` decorator is triggered whenever **_any CRUD (Create, Read, Update, Delete)_** event occurs, whether the entity is `active` or in `draft` mode.

`ACTIVE ENTITY`

For active entities, the `@AfterAll` decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeCreate()](#beforecreate), [@AfterCreate()](#aftercreate), [@OnCreate()](#oncreate)
- `READ` [@BeforeRead()](#beforeread), [@AfterRead()](#afterread), [@OnRead()](#onread)
- `UPDATE` [@BeforeUpdate()](#beforeupdate), [@AfterUpdate()](#afterupdate), [@OnUpdate()](#onupdate)
- `DELETE` [@BeforeDelete()](#beforedelete), [@AfterDelete()](#afterdelete), [@OnDelete()](#ondelete)
- `BOUND ACTIONS` [@OnBoundAction()](#onboundaction)
- `BOUND FUNCTIONS` [@OnBoundFunction()](#onboundfunction)

`DRAFT`

For draft entities, the `@AfterAll` decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeNewDraft()](#beforenewdraft), [@AfterNewDraft()](#afternewdraft), [@OnNewDraft()](#onnewdraft)
- `CANCEL` [@BeforeCancelDraft()](#beforecanceldraft), [@AfterCancelDraft()](#aftercanceldraft), [@OnCancelDraft()](#oncanceldraft)
- `EDIT` [@BeforeEditDraft()](#beforeeditdraft), [@AfterEditDraft()](#aftereditdraft), [@OnEditDraft()](#oneditdraft)
- `SAVE` [@BeforeSaveDraft()](#beforesavedraft), [@AfterSaveDraft()](#aftersavedraft), [@OnSaveDraft()](#onsavedraft)
- :heavy_plus_sign: All active entity [Before](#before), [After](#after), [On](#on) events which have a `Draft` variant.

**@AfterAll**()

`Example 1`

In this example, the `@AfterAll()` decorator of the `afterAll` method is executed after any CRUD operation `(CREATE, READ, UPDATE, DELETE, BOUND ACTIONS, BOUND FUNCTIONS))` on the `MyEntity` entity.

Since the class is annotated with `@EntityHandler(MyEntity)`, the decorator is scoped to this specific `entity`, meaning it will only be triggered for operations related to `MyEntity`.

```typescript
import { AfterAll, EntityHandler } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@AfterAll()
private async afterAll(@Result() result: MyEntity | MyEntity[] | boolean, @Req() req: Request) {
if(Array.isArray(result)) {
// when after `READ` event was triggered
}
else if(typeof result === 'boolean' ) {
// when after `DELETE` event was triggered
}
else {
// when after `CREATE`, `UPDATE` was triggered
}

// ...
}
}
```

`Equivalent to 'JS'`

```typescript
this.after('*', MyEntity, async (result, req) => {
// ...
});
```

`Example 2`

In this example, the `@AfterAll()` decorator is used in a more generic way. Unlike `Example 1`, where it applies only to `MyEntity`, this setup ensures that `afterAll` is triggered for `all entities`.

This means the method will execute before any CRUD operation on `any` `entity` handled within the class.

```typescript
import { AfterAll, EntityHandler} from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(CDS_DISPATCHER.ALL_ENTITIES)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@AfterAll()
private async afterAll(@Result() result: MyEntity | MyEntity[] | boolean, @Req() req: Request) {
if(Array.isArray(result)) {
// when after `READ` event was triggered
}
else if(typeof result === 'boolean' ) {
// when after `DELETE` event was triggered
}
else {
// when after `CREATE`, `UPDATE` was triggered
}

// ...
}
}
```

`Equivalent to 'JS'`

```typescript
this.after('*', '*', async (result, req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@AfterAll()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!TIP]
> If the entity has drafts enabled `@odata.draft.enabled: true`, the `@AfterAll` decorator will also be triggered for draft events.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

##### `On`

Use [@OnCreate()](#oncreate), [@OnRead()](#onread), [@OnUpdate()](#onupdate), [@OnDelete()](#ondelete), [OnAction()](#onaction), [@OnFunction()](#onfunction), [@OnBoundAction()](#onboundaction), [@OnBoundFunction()](#onboundfunction) handlers to fulfill requests, e.g. by reading/writing data from/to databases handlers.

The handlers receive two arguments:

- `req` of type `Request`
- `next` of type `NextEvent`

> [!TIP]
> If `@odata.draft.enabled: true` to manage event handlers for draft version you can use :
>
> - `@OnCreateDraft()`
> - `@OnReadDraft()`
> - `@OnUpdateDraft()`
> - `@OnDeleteDraft()`
> - `@OnBoundActionDraft()`
> - `@OnBoundFunctionDraft()`

(back to top)

###### @OnCreate

**@OnCreate**()

`Example`

```typescript
import { OnCreate, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnCreate()
private async onCreateMethod(@Req() req: Request, @Next() next: NextEvent) {
// ...

return next();
}
```

`Equivalent to 'JS'`

```typescript
this.on('CREATE', MyEntity, async (req, next) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnCreate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnRead

**@OnRead**()

`Example`

```typescript
import { OnRead, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnRead()
private async onReadMethod(@Req() req: Request, @Next() next: NextEvent) {
// ...

return next();
}
```

`Equivalent to 'JS'`

```typescript
this.on('READ', MyEntity, async (req, next) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnRead()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnUpdate

**@OnUpdate**()

`Example`

```typescript

import { OnUpdate, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnUpdate()
private async onUpdateMethod(@Req() req: Request, @Next() next: NextEvent) {
// ...

return next();
}
```

`Equivalent to 'JS'`

```typescript
this.on('UPDATE', MyEntity, async (req, next) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnUpdate()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnDelete

**@OnDelete**()

`Example`

```typescript
import { OnDelete, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnDelete()
private async onDeleteMethod(@Req() req: Request, @Next() next: NextEvent) {
// ...

return next();
}
```

`Equivalent to 'JS'`

```typescript
this.on('DELETE', MyEntity, async (req, next) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnDelete()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnAction

**@OnAction**(`name` : CdsAction)

`Parameters`

- `name (CdsAction)` : Representing the `CDS action` defined in the `CDS file`

`Example`

```typescript

import { OnAction, Req, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { ActionRequest, ActionReturn, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { AnAction } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnAction(AnAction)
private async onActionMethod(@Req() req: ActionRequest, @Next() next: NextEvent): ActionReturn {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on(AnAction, async (req, next) => {
// ...
});
```

> [!NOTE]
> AnAction was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

> [!IMPORTANT]
> Decorator `@OnAction` should be used inside [@UnboundActions()](#unboundactions) class.

(back to top)

###### @OnFunction

**@OnFunction**(`name` : CdsFunction)

`Parameters`

- `name (CdsFunction)` : Representing the `CDS action` defined in the `CDS file`.

`Example`

```typescript
import { OnFunction, Req, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { ActionRequest, ActionReturn, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { AFunction } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnFunction(AFunction)
private async onFunctionMethod(@Req() req: ActionRequest, @Next() next: NextEvent): ActionReturn {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on(AFunction, async (req) => {
// ...
});
```

> [!NOTE]
> AFunction was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

> [!IMPORTANT]
> Decorator `@OnFunction` should be used inside [@UnboundAction()](#unboundactions) class.

(back to top)

###### @OnEvent

**@OnEvent**(`name` : CdsEvent)

The `@OnEvent` decorator facilitates the listening of events when the [Emit](https://cap.cloud.sap/docs/guides/messaging/) and receiver are in the same `NODE JS PROCESS` and `SAME SERVICE`.

This decorator is particularly useful in conjunction with the [Emit method](https://cap.cloud.sap/docs/guides/messaging/#emitting-events) to handle triggered events.

`Parameters`

- `name (CdsEvent)` : Representing the `CDS event` defined in the `CDS file`.

`Example`

```typescript
import { OnEvent, Req, OnSubscribe } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { AEvent } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnEvent(AEvent)
private async onEventMethod(@Req() req: Request) {
// ...
}

// same as

@OnSubscribe({
eventName: AEvent,
type: 'SAME_NODE_PROCESS',
})
private async onEventMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on('AEvent', async (req) => {
// ...
});
```

> [!NOTE]
> The `AEvent` was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

> [!IMPORTANT]
> Decorator `@OnEvent` should be used inside [@UnboundActions](#unboundactions) class.

> [!TIP]
> An exended version of this event can be found [@OnSubscribe](#onsubscribe).

(back to top)

###### @OnSubscribe

```ts
@OnSubscribe(options : {
eventName: string | object,
type : 'SAME_NODE_PROCESS' | 'MESSAGE_BROKER' | 'SAME_NODE_PROCESS_DIFFERENT_SERVICE'
externalServiceName: string // applicable only for 'SAME_NODE_PROCESS_DIFFERENT_SERVICE'
showReceiverMessage?: boolean
consoleStyle?: 'table | 'debug'
})
```

Use `@OnSubscribe` decorator enables execution of custom logic when messaging events (`event bus` / `publish subscribe`) are triggered in your SAP CAP application.

This decorator is particularly useful in conjunction with the [Emit method](https://cap.cloud.sap/docs/guides/messaging/#emitting-events) to handle triggered events.

`Parameters`

- `options`:
- `name`: CdsEvent - The name of the event to subscribe to. This can be a `string` or a `CDS event` type imported from your `@cds-models` folder.
- `type`: Defines the messaging transport mechanism for SAP CAP applications.
- `'SAME_NODE_PROCESS'` - Use when both `emitter` and `receiver` run in the same `CAP server instance` & `same service`.
- `'SAME_NODE_PROCESS_DIFFERENT_SERVICE'` - Use when `emitter` can be found in E.g. `Service A` and `receiver` can reside in E.g. `Service B`, having same `CAP server instance` but `different services`.
- `'MESSAGE_BROKER'` - Recommended for production with external message brokers, different CAP Server instances, different services.
- `showReceiverMessage?` `[optional]`: **boolean** - When enabled, logs inbound message payloads in the specified `consoleStyle` format.
- `externalServiceName`: - The name of the external service to attach the subscribe decorator to. `[Applicable only when type is 'SAME_NODE_PROCESS_DIFFERENT_SERVICE' ]`
- `consoleStyle?` `[optional]` - Specifies the log output format for received messages (when `showReceiverMessage` is true).
- `'table'`: Displays data using `console.table()`, ideal for structured messages.
- `'debug'`: Displays data using `console.debug()`, ideal for nested or dynamic messages.

`Example 1`

In this example we emit the event in same `node process` and `same service` and we subscribe to it.

`Emitting and subscribing:`

```ts
import { OnSubscribe, Req, OnEvent } from '@dxfrontier/cds-ts-dispatcher';
import type { SubscriberType, Request } from '@dxfrontier/cds-ts-dispatcher';

// Emitting the event
@AfterRead()
private async afterRead(
@Req() req: Request,
@Results() results: MyEntity[],
): Promise {
this.srv.emit('anEventName', { foo: 11, bar: '22' });
}

// Subscribing to the event
@OnSubscribe({
eventName: 'anEventName'
type: 'SAME_NODE_PROCESS',
})
private async onSubscribe(@Req() req: Request<{ foo: number, bar: string }>): Promise {
//
}

// same as

@OnEvent('anEventName')
private async onEventMethod(@Req() req: Request<{ foo: number, bar: string }>) {
// ...
}

```

`Equivalent to 'JS'`

```typescript
this.after('READ', MyEntity, async (req, res) => {
this.emit('anEventName', { foo: 11, bar: '22' });
});

this.on('anEventName', async (msg) => {
// ...
});
```

`Example 2`

In this example we emit the event in same `node process` and `same service` and we subscribe to it, the only difference, is in this case we get the Event from the `@cds-models` folder

`Service Definition (CDS):`

```yml
// We declare an event into our Service
service CatalogService {
event SendData : {
foo : Integer;
bar : String;
}
}
```

`Emitting and subscribing:`

```ts
import { OnSubscribe, Req, AfterRead, Results, OnEvent } from '@dxfrontier/cds-ts-dispatcher';
import type { SubscriberType } from '@dxfrontier/cds-ts-dispatcher';

import { SendData } from '#cds-models/CatalogService'; // <== location of @cds-models can differ.

// Emitting the event
@AfterRead()
private async afterRead(
@Req() req: Request,
@Results() results: MyEntity[],
): Promise {
this.srv.emit('SendData', { foo: 11, bar: '22' });
}

// Receiving the event + data
@OnSubscribe({
eventName: SendData
type: 'SAME_NODE_PROCESS'
})
private async onSubscribe(@Req() req: Request): Promise {
//
// req.data.foo ...
// req.data.bar ...
// req.headers ...
// ...
}

// same as

@OnEvent(SendData)
private async onEventMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.after('READ', MyEntity, async (req, res) => {
this.emit('SendData', { foo: 11, bar: '22' });
});

this.on('SendData', async (msg) => {
// ...
});
```

> [!NOTE]
> The `SendData` was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

`Example 3`

In this example we assume we have `2 node instances` and `2 services` and we use external messaging `Cross-Instance Messaging (Message Broker)`

`Emitter (Service 1 & node instance 1):`

```ts
import { Req, AfterRead, Results } from '@dxfrontier/cds-ts-dispatcher';
import cds from '@sap/cds';

@AfterRead()
private async afterRead(
@Req() req: Request,
@Results() results: MyEntity[],
): Promise {

const messaging = await cds.connect.to('messaging');
messaging.emit('SendData', { foo: 11, bar: '22' });

}
```

`Equivalent to 'JS'`

```ts
this.after('READ', MyEntity, async (req, res) => {
const messaging = await cds.connect.to('messaging');
messaging.emit('SendData', { foo: 11, bar: '22' });
});
```

`Subscriber (Service 2 & Node instance 2):`

```ts

import { SendData } from '#cds-models/CatalogService'; // <== location of @cds-models can differ.
import { Messaging, OnSubscribe } from '@dxfrontier/cds-ts-dispatcher';
import type { SubscriberType } from '@dxfrontier/cds-ts-dispatcher';

// Receiving the event + data
@OnSubscribe({
eventName: SendData,
type: 'MESSAGE_BROKER'
})
private async onSubscribe(@Msg() msg: SubscriberType): Promise {
//
// req.data.foo ...
// req.data.bar ...
// req.headers ...
// ...
}
```

`Equivalent to 'JS'`

```ts
const messaging = await cds.connect.to('messaging');
messaging.on(SendData, async (msg) => {
// ...
});
```

> [!IMPORTANT]
> To have the `msg` parameter typed you can use TypeScript type `SubscriberType` where T can be a CDS event or any object

> [!TIP]
> When using `messaging (broker)` the decorator [@Msg](#msg) must be applied as the request is anonymous at this level.

`Example 4`

In this example we emit an event in `Service 1` & `node process 1`, and we attach from `Service 2` a subscribe to `Service 1` & `node process 1`, meaning that, when emit `SendData` the service `Service 2` will be notified and the `onSubscribe` method will be triggered which resides attached to `Service 1`.

`Emitter (Service 1 & node process 1):`

```ts
import { Req, AfterRead, Results } from '@dxfrontier/cds-ts-dispatcher';
import type { SubscriberType } from '@dxfrontier/cds-ts-dispatcher';

import { SendData } from '#cds-models/CatalogService'; // <== location of @cds-models can differ.

// Emitting the event
// This event resides in Service_1
@AfterRead()
private async afterRead(
@Req() req: Request,
@Results() results: MyEntity[],
): Promise {
this.srv.emit('SendData', { foo: 11, bar: '22' });
}
```

`Equivalent to 'JS'`

```ts
// This event resides in Service_1
this.after('READ', MyEntity, async (req, res) => {
this.emit('SendData', { foo: 11, bar: '22' });
});
```

`Subscriber (Service 2 & node process 1):`

```ts

import { OnSubscribe, Req } from '@dxfrontier/cds-ts-dispatcher';

// Receiving the event + data
// This event resides in Service_2 but the subscribe will be attached to Service_1
@OnSubscribe({
eventName: 'SendData',
type: 'SAME_NODE_PROCESS_DIFFERENT_SERVICE',
externalService: 'Service_1'
})
private async onSubscribe(@Req() req: Request): Promise {
// req.data.foo ...
// req.data.bar ...
// req.headers ...
// ...
}
```

`Equivalent to 'JS'`

```ts
// This code resides in Service_2 but it is subscribed to Service_1
const Service_1 = cds.connect.to('Service_1');
Service_1.on('SendData', (msg) => {
//
});
```

> [!TIP]
> You can find more info about SAP CAP messaging in the following url [SAP CAP Events and Messaging](https://cap.cloud.sap/docs/guides/messaging/).

> [!IMPORTANT]
> Decorator `@OnSubscribe` should be used inside [@UnboundActions](#unboundactions) class, but not mandatory it can reside also in [@EntityHandler](#entityhandler) class.

(back to top)

###### @OnError

**@OnError**()

Use `@OnError` decorator to register custom error handler.

Error handlers are invoked whenever an error occurs during event processing of all potential events and requests, and are used to augment or modify error messages, before they go out to clients.

`Example`

```typescript
import { OnError, Error, Req } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

@OnError()
private onError(@Error() err: Error, @Req() req: Request) { // sync func
err.message = 'New message'
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on('error', (err, req) => {
err.message = 'New message';
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnError` should be used inside [@UnboundActions](#unboundactions) class.

> [!CAUTION]
> OnError callback are expected to be a **`sync`** function, i.e., **`not async`**, not returning `Promises`.

> [!TIP]
> More info can be found at [SAP CAP Error](https://cap.cloud.sap/docs/node.js/core-services#srv-on-error)

(back to top)

###### @OnBoundAction

**@OnBoundAction**(`name` : CdsAction)

`Parameters`

- `name (CdsAction)` : Representing the `CDS action` defined in the `CDS file`.

`Example`

```typescript
import { OnBoundAction, Req, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { ActionRequest, ActionReturn, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnBoundAction(MyEntity.actions.AnAction)
private async onActionMethod(@Req() req: ActionRequest, @Next() next: NextEvent): ActionReturn {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on(MyEntity.actions.AnAction, MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnBoundAction()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnBoundFunction

**@OnBoundFunction**(`name` : CdsFunction)

`Parameters`

- `name (CdsFunction)` : Representing the `CDS action` defined in the `CDS file`.

`Example`

```typescript
import { OnBoundFunction, Req, Next } from "@dxfrontier/cds-ts-dispatcher";
import type { ActionRequest, ActionReturn, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@OnBoundFunction(MyEntity.actions.AFunction)
private async onFunctionMethod(@Req() req: ActionRequest, @Next() next: NextEvent): ActionReturn {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.on(MyEntity.actions.AFunction, MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnBoundFunction()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @OnAll

**@OnAll**()

The `@OnAll` decorator is triggered whenever **_any CRUD (Create, Read, Update, Delete)_** event occurs, whether the entity is `active` or in `draft` mode.

`ACTIVE ENTITY`

For active entities, the `@OnAll` decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeCreate()](#beforecreate), [@AfterCreate()](#aftercreate), [@OnCreate()](#oncreate)
- `READ` [@BeforeRead()](#beforeread), [@AfterRead()](#afterread), [@OnRead()](#onread)
- `UPDATE` [@BeforeUpdate()](#beforeupdate), [@AfterUpdate()](#afterupdate), [@OnUpdate()](#onupdate)
- `DELETE` [@BeforeDelete()](#beforedelete), [@AfterDelete()](#afterdelete), [@OnDelete()](#ondelete)
- `BOUND ACTIONS` [@OnBoundAction()](#onboundaction)
- `BOUND FUNCTIONS` [@OnBoundFunction()](#onboundfunction)

`DRAFT`

For draft entities, the `@OnAll` decorator will be triggered when at least __one__ of the following events occurs:

- `CREATE` [@BeforeNewDraft()](#beforenewdraft), [@AfterNewDraft()](#afternewdraft), [@OnNewDraft()](#onnewdraft)
- `CANCEL` [@BeforeCancelDraft()](#beforecanceldraft), [@AfterCancelDraft()](#aftercanceldraft), [@OnCancelDraft()](#oncanceldraft)
- `EDIT` [@BeforeEditDraft()](#beforeeditdraft), [@AfterEditDraft()](#aftereditdraft), [@OnEditDraft()](#oneditdraft)
- `SAVE` [@BeforeSaveDraft()](#beforesavedraft), [@AfterSaveDraft()](#aftersavedraft), [@OnSaveDraft()](#onsavedraft)
- :heavy_plus_sign: All active entity [Before](#before), [After](#after), [On](#on) events which have a `Draft` variant.

> [!NOTE]
> Exception will be the following decorators [@OnEvent()](#onevent), [@OnError()](#onerror) and `UNBOUND ACTIONS` [@OnAction()](#onaction), `UNBOUND FUNCTIONS` [@OnFunction()](#onfunction) as these are bound to the service itself and not to an entity.

`Example 1`

In this example, the `@OnAll()` decorator of the `onAll` method is executed after any CRUD operation `(CREATE, READ, UPDATE, DELETE, BOUND ACTIONS, BOUND FUNCTIONS)` on the `MyEntity` entity.

Since the class is annotated with `@EntityHandler(MyEntity)`, the decorator is scoped to this specific `entity`, meaning it will only be triggered for operations related to `MyEntity`.

```typescript
import { OnAll, Next, EntityHandler } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(MyEntity)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@OnAll()
private async onAll(@Req() req: Request, @Next() next: NextEvent) {
// ...
return next();
}
}
```

`Equivalent to 'JS'`

```typescript
this.on('*', MyEntity, async (req, next) => {
// ...
});
```
`Example 2`

In this example, the `@OnAll()` decorator is used in a more generic way. Unlike `Example 1`, where it applies only to `MyEntity`, this setup ensures that `afterAll` is triggered for `all entities`.

This means the method will execute before any CRUD operation on `any` `entity` handled within the class.

```typescript
import { OnAll, Next, EntityHandler, CDS_DISPATCHER } from "@dxfrontier/cds-ts-dispatcher";
import type { Request, NextEvent } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@EntityHandler(CDS_DISPATCHER.ALL_ENTITIES)
export class BookHandler {
// ...
constructor() {}
// All events like @AfterRead, @BeforeRead, ... will be triggered based on 'MyEntity'

@OnAll()
private async onAll(@Req() req: Request, @Next() next: NextEvent) {
// ...
return next();
}
}
```

`Equivalent to 'JS'`

```typescript
this.on('*', '*', async (req, next) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@OnAll()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!TIP]
> If the entity has drafts enabled `@odata.draft.enabled: true`, the `@OnAll` decorator will still be triggered for draft events.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

#### `Method`-`draft entity`

##### `Before`

Use `@BeforeNewDraft(), @BeforeCancelDraft(), @BeforeEditDraft(), @BeforeSaveDraft(), @BeforeCreateDraft(), @BeforeReadDraft(), @BeforeUpdateDraft(), @BeforeDeleteDraft()` to register handlers to run before`.on`handlers, frequently used for `validating user input.`

The handlers receive one argument:

- `req` of type `Request`

###### @BeforeNewDraft

**@BeforeNewDraft**()

Use this decorator when you want to validate inputs before a new draft is created.

`Example`

```typescript
import { BeforeNewDraft, Request } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeNewDraft()
private async beforeCreateDraftMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('NEW', MyEntity.drafts, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeNewDraft()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeCancelDraft

**@BeforeCancelDraft**()

Use this decorator when you want to validate inputs before a draft is discarded.

`Example`

```typescript
import { BeforeCancelDraft } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeCancelDraft()
private async beforeCancelDraftMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('CANCEL', MyEntity.drafts, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeCancelDraft()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeEditDraft

**@BeforeEditDraft**()

Use this decorator when you want to validate inputs when a new draft is created from an active instance.

`Example`

```typescript
import { BeforeEditDraft } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeEditDraft()
private async beforeEditDraftMethod(@Req() req: Request) {
// ...
}
```

`Equivalent to 'JS'`

```typescript
this.before('EDIT', MyEntity, async (req) => {
// ...
});
```

> [!IMPORTANT]
> Decorator `@BeforeEditDraft()` will be triggered based on the [EntityHandler](#entityhandler) `argument` => `MyEntity`.

> [!NOTE]
> MyEntity was generated using [CDS-Typer](#generate-cds-typed-entities) and imported in the the class.

(back to top)

###### @BeforeSaveDraft

**@BeforeSaveDraft**()

Use this decorator when you want to validate inputs when active entity is changed.

`Example`

```typescript
import { BeforeSaveDraft } from "@dxfrontier/cds-ts-dispatcher";
import type { Request } from '@dxfrontier/cds-ts-dispatcher';

import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION';

@BeforeSaveDraft()
private async beforeSaveDraftMethod(@Req() req: Request) {
// ...
}
```

`Equivalent