# express.mediator

> lightweight and easy implementation of an mediator pattern with express.js

Latest version **1.0.6-beta.2** (published 2024-05-31) · MIT license · 0 weekly downloads

## Install

```sh
npm install express.mediator
pnpm add express.mediator
yarn add express.mediator
bun add express.mediator
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.6-beta.2 |
| Published | 2024-05-31 |
| First published | 2023-07-31 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 2 |
| Unpacked size | 1.8 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Rene Bartels |
| Maintainers | rennegade |
| Keywords | express, framework, mediator, web, http, rest, app, api, components |

## Links

- npm: https://www.npmjs.com/package/express.mediator
- Repository: https://github.com/Elevatour/express.mediator
- Homepage: https://github.com/Elevatour/express.mediator#readme
- Issues: https://github.com/Elevatour/express.mediator/issues
- npm.io page: https://npm.io/package/express.mediator

## Dependencies (2)

- [cors](https://npm.io/package/cors.md) ^2.8.5
- [express](https://npm.io/package/express.md) ^4.18.2

## Alternatives

- [launchdarkly-js-client-sdk](https://npm.io/package/launchdarkly-js-client-sdk.md) — 2.5M weekly downloads
- [@elastic/elasticsearch](https://npm.io/package/@elastic/elasticsearch.md) — 2.1M weekly downloads
- [@c8y/client](https://npm.io/package/@c8y/client.md) — 15.3K weekly downloads
- [@signaldb/maverickjs](https://npm.io/package/@signaldb/maverickjs.md) — 1.7K weekly downloads
- [@bbc/http-transport-cache](https://npm.io/package/@bbc/http-transport-cache.md) — 1.2K weekly downloads

## Recent versions

- 1.0.6-beta.2 (latest) — 2024-05-31
- 1.0.6-beta.1 — 2024-05-30
- 1.0.5-alpha.6 — 2024-02-27
- 1.0.5-alpha.5 — 2024-01-22
- 1.0.5-alpha.4 — 2024-01-16
- 1.0.5-alpha.3 — 2024-01-16
- 1.0.5-alpha.2 — 2024-01-16
- 1.0.5-alpha.1 — 2023-12-04
- 1.0.5-alpha.0 — 2023-12-04
- 1.0.4 — 2023-11-22
- 1.0.2 — 2023-07-31
- 1.0.1 — 2023-07-31
- 1.0.0 — 2023-07-31

## README

An lightweight and easy implementation of an mediator pattern with [express.js](https://www.npmjs.com/package/express).

```ts
const app = new ExpressMediator();
app.get("/", IndexRequest);
```

```ts
@body
export class IndexRequest extends IRequest {
    name!;
}

@requestHandler(IndexRequest)
export class IndexRequestHandler implements IRequestHandler<IndexRequest, string> {
    async handle(value: IndexRequest): Promise<string> {
        return `Hello ${value.name}!`
    }
}
```

See [usage](#usage) to learn all the different types of implementation.

---

Please be kind. I tried my best with this documentation but Iam still learning.

![sadmouse](https://media1.tenor.com/m/cZm0iAcE2wIAAAAC/sad-mouse-big-eyes.gif)

# Getting Started

## Installation

```bash
npm i express.mediator
```

or if you want to access early builds

```bash
npm i express.mediator@X.X.X(-alpha.X | -beta.X)
```

## Usage

The most magic of the mediator happens in the background. You basically just have to place some decorators onto your existing requests and replace the express app with the new `ExpressMediator`. In the following captions are all possible types of implementations and examples. Just pick what fits best for your purpose.

### Request (required)

A request defines the whole processing for the resource. If you request data is located specifically use an [request type](#request-type) to tell the parse pipeline where exactly to expect the data.

```ts
@body
class ExampleRequest {
	user!: string;
	password!: string;
}

@body
class ExampleRequest {
	user = "";
	password = "";
}
```

To use specific types of property declaration see the [property setup](#parse-pipeline-property-setup).

#### Request Type

All usable http request types are accessable via an specific typescript [decorator](https://www.typescriptlang.org/docs/handbook/decorators.html). There are two different (class and property) decorator types with two (non nullable and nullable) features available. These decorator are needed for the parse pipeline to determine where the data is in the request.

The class decorator function require a functional constructor to access the name of the provided class.

```ts
{{HTTPTYPE}}<T extends { new (): {} }>(constructor: T);
```

> The decorator add san default auth entry into the [IRequestAuthResolver]() without any role configuration and disabled anonymous request mode. This is required to ensure that every available handler request is covered by the auth component and a missed configuration will be highlighted correctly.

##### Request Type **Body**

Reads the data from the body of express request.

```ts
@body
@bodyNullable
```

##### Request Type **Params**

Reads the data from the params of express request.

```ts
@params
@paramsNullable
```

##### Request Type **Query**

Reads the data from the query of express request.

```ts
@query
@queryNullable
```

##### Request Type **Empty**

Ignores the parsing and won't proceed any value of the request.

```ts
@empty
```

### Request Handler (required)

Every request has logic which is located in an handler. 

The mediator assigns the handler to the request with a specific decorator.

```ts
@body
class ExampleRequest { ... }

@requestHandler(ExampleRequest)
class ExampleHandler implements IRequestHandler<ExampleRequest, string> { ... }
```

#### Request Handler in another file

If the **handler** is **not in the same file like the request** you have to **put the following decorator onto the request**. This step is required due to the fact that the handler won't be called by the application setup because the mediator only knowns the request.

##### Request File

```ts
@params
@handler(TestRequestHandler)
class TestRequest_Param {
	data!: string;
}
```

##### Handler File

```ts
class TestRequestHandler implements IRequestHandler<TestRequest_Param, string> {
	async handle(value: TestRequest_Param): Promise<string> {
		return value.data;
	}
}
```

### Routers

A simplified router can be created to add multiple routes to express respectively multiple requests and handlers to the mediator.

#### Router Declaration

To create an express mediator router use the fluent `router` funtion.

```ts
const exampleRouter = (instance: IMediatorInstance) => router(instance)
	.get(ExampleRequest0)
	.get(ExampleRequest1, "nextRoute");
```

Multiple identical paths within the router will throw an configuration error.

#### Router Registration

The created express mediator router must be registered in the `ExpressMediator`.

```ts
const appMediator = new ExpressMediator();
appMediator.route("/testRoute", exampleRouter);
```

All the specified routes on the router will be added onto the base path (e.g. `nextRoute` will be `/testRoute/nextRoute`).

### Access Root Express Application

To access the root express application just call the `express app` on the `ExpressMediator` instance.

```ts
const app = new ExpressMediator().app;
```

## Tests

The tests are located inside the folder `./tests/` and are constructed with [jest](https://www.npmjs.com/package/jest) respectively [ts-jest](https://www.npmjs.com/package/ts-jest). To configure jest use the config file `jest.config.js`.  

```ts
npm run test
```

Before uploading the files to [npm](https://www.npmjs.com/package/express.mediator) run the following script.

```ts
npm run preupload
```

This script runs the jest test file `jest ./tests/default.test.ts` which tests the basic functionalities of the build files in `./lib`.

> All test files with the prefix `!` will be ignored by jest. If you want to change this behavior remove this line inside the jest configuration.

# Version Overview

## 1.0.6

### Request Handler Decorator Adjustments

This version introduces the option to specifie any media type and success status code within the request handler decorator declaration.

```ts
@requestHandler(TestClass, SuccessHttpType.OK, MediaTypes.common.json)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
```

```ts
@requestHandler(TestClass, SuccessHttpType.Created, MediaTypes.common.txt)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
```

***before*** 

```ts
@requestHandler(TestClass)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
```

```ts
@requestHandler(TestClass)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
```

Like before the default for all successfull proceeded requests is HTTP 200 with an JSON response. The status code property of the request handler interface has been removed.

### Parse Pipeline Property Setup

All properties of the registered request will be categorized into the following types.

#### Required Properties

All properties which have an empty default value will be categorized as an required property. Please read the following special cases to avoid unintentional errors.

```ts
value = "";
```

If the request object won't provide any value for the required fields an parsing error will be thrown. All missing required fields will be summarized into one error response.

##### Special Case: Numbers, Big Integers and Booleans

Because of the fact that the default value of types like `number`, `bigInt` and `bool` are not separatable from empty values (e.g. an empty string) any declarated value will be treated as an default value (see [ignoreable properties](#ignorable-properties)).

```ts
n0 = 0;
n1 = 60;
b = false;
```

##### Special Case: Undefined Flag

Even though the property `value` is specified with an null assertion operator `?` the parser won't be able to specifie those as ignorable properties. If you want to use ignorable properties see [ignoreable properties](#ignorable-properties).

```ts
value? = "";
```

#### Ignorable Properties

All properties which have a *filled* default value will be added to the parsed object automatically after the initial parsing process when the object does not provide any value for the specific field.

```ts
data = "value";
```

#### Optional Properties

All properties which have an null assertion operator `?` or an non-null assertion operator `!` will be treated as optional properties. 

```ts
data?;
data!;
```

If the request object won't provide the specified optional properties no error will be thrown.

### Router Declaration Refactoring

The declared router for the [router registration](#router-registration) won't has to return the express router and doesn't need any explicit mediator instance for declaration like before.

```ts
const exampleRouter = router().get(exampleRequest);
```

***before*** 

```ts
const exampleRouter = (instance: IMediatorInstance) => router(instance).get(exampleRequest).exe();
```

If the express router is needed for development reasons use the function `.getRouter()`.

### MISC

- `role()`, `roles()` and `anonymous` decorators accept class constructs now (previously functions)
- `exception` field has been removed from `PrePipelineResponse`. Pre pipelines must have to throw pipeline errors explicitly now
- `new ExpressMediator().listen()` now accept a number for the port definition only (previously string)
- `IRequest` has been removed due to the fact that is was completly unnecessary
- `Logger.get()` now only returns the internal logger without any required properties
- Property data of `E__Pipeline` is not optional (previously required)

---
_Source: https://npm.io/package/express.mediator · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
