express.mediator
v1.0.6-beta.2
Published
lightweight and easy implementation of an mediator pattern with express.js
Downloads
9
Maintainers
Readme
An lightweight and easy implementation of an mediator pattern with express.js.
const app = new ExpressMediator();
app.get("/", IndexRequest);
@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 to learn all the different types of implementation.
Please be kind. I tried my best with this documentation but Iam still learning.
Getting Started
Installation
npm i express.mediator
or if you want to access early builds
npm i [email protected](-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 to tell the parse pipeline where exactly to expect the data.
@body
class ExampleRequest {
user!: string;
password!: string;
}
@body
class ExampleRequest {
user = "";
password = "";
}
To use specific types of property declaration see the property setup.
Request Type
All usable http request types are accessable via an specific typescript decorator. 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.
{{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.
@body
@bodyNullable
Request Type Params
Reads the data from the params of express request.
@params
@paramsNullable
Request Type Query
Reads the data from the query of express request.
@query
@queryNullable
Request Type Empty
Ignores the parsing and won't proceed any value of the request.
@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.
@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
@params
@handler(TestRequestHandler)
class TestRequest_Param {
data!: string;
}
Handler File
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.
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
.
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.
const app = new ExpressMediator().app;
Tests
The tests are located inside the folder ./tests/
and are constructed with jest respectively ts-jest. To configure jest use the config file jest.config.js
.
npm run test
Before uploading the files to npm run the following script.
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.
@requestHandler(TestClass, SuccessHttpType.OK, MediaTypes.common.json)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
@requestHandler(TestClass, SuccessHttpType.Created, MediaTypes.common.txt)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
before
@requestHandler(TestClass)
class TestClassHandler<...> implements IRequestHandler<...> { ... }
@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.
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).
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.
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.
data = "value";
Optional Properties
All properties which have an null assertion operator ?
or an non-null assertion operator !
will be treated as optional properties.
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 won't has to return the express router and doesn't need any explicit mediator instance for declaration like before.
const exampleRouter = router().get(exampleRequest);
before
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()
andanonymous
decorators accept class constructs now (previously functions)exception
field has been removed fromPrePipelineResponse
. Pre pipelines must have to throw pipeline errors explicitly nownew 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 unnecessaryLogger.get()
now only returns the internal logger without any required properties- Property data of
E__Pipeline
is not optional (previously required)