@yamato-daiwa/express-extensions
v1.2.2
Published
Additional functionality for Express.js and also "routing-controllers" aimed to reduce the routine code.
Maintainers
Readme
Yamato Daiwa Express Extensions
Additional functions for express and its plugins, and also for routing-controllers.
Installation
npm i @yamato-daiwa/express-extensions -EAlso, install the following peer dependencies if not installed yet.
- body-parser: ~2.2.0
- express: ~5.1.0
- express-session: ~1.19.0
- routing-controllers: ~0.11.0
Functionality
Session
Request Body
ExpressMiddleware
The abstract class intended to be extended for the creating of Express middleware and using with
@UseBefore and @UseAfter decorators.
Unlike MiddlewareInterface and @Middleware() decorator
of routing-controllers, has safely typed parameters.
classDiagram
class ExpressMiddleware {
<<abstract>>
#handleRequest(request: Express.Request, response: Express.Response, toNextMiddleware: ToNextMiddlewareTransfer) Promise<void>
}The only method need to be implemented is handleRequest:
(
request: Express.Request,
response: Express.Response,
toNextMiddleware: ExpressMiddleware.ToNextMiddlewareTransfer
): Promise<void>Example
import type Express from "express";
import { ExpressMiddleware } from "@yamato-daiwa/express-extensions";
import { Logger } from "@yamato-daiwa/es-extensions";
export default class DebuggerMiddleware extends ExpressMiddleware {
protected override async handleRequest(
request: Express.Request,
response: Express.Response,
toNextMiddleware: ExpressMiddleware.ToNextMiddlewareTransfer
): Promise<void> {
Logger.logInfo({
title: "DebuggerMiddleware",
description: "",
additionalData: {
request,
response
}
});
toNextMiddleware();
return Promise.resolve();
}
}initializeRoutingControllersExpressHTTPS_Application
Initializer of the application using routing-controllers, Express and HTTPS protocol.
(
{
configuration: {
HTTPS,
routingControllers
},
eventsHandlers: {
onExpressApplicationCreated,
onHTTPS_ServerCreated,
onRoutingControllersSetupComplete,
onApplicationStarted
}
}: Readonly<{
configuration: Readonly<{
HTTPS: Readonly<
{ port: number; } &
(
(
{ SSL_Key: string; } |
{ SSL_KeyFilePath__absoluteOrRelative: string; }
) &
(
{ SSL_Certificate: string; } |
{ SSL_CertificateFilePath__absoluteOrRelative: string; }
)
)
>;
routingControllers: RoutingControllersOptions;
}>;
eventsHandlers: Readonly<{
onExpressApplicationCreated: (expressApplication: ExpressApplication) => Promise<void>;
onHTTPS_ServerCreated?: (HTTPS_Server: NodeHTTPS.Server, expressApplication: ExpressApplication) => Promise<void>;
onRoutingControllersSetupComplete?: () => Promise<void>;
onApplicationStarted?: () => Promise<void>;
}>;
}>
): Promise<void>Background
The initialization of routing-controllers + Express + HTTPS application has not been documented well and a little bit complicated:
import Express, { type Express as ExpressApplication } from "express";
import createExpressApplication from "express";
import { useExpressServer as supportClassSyntax } from "routing-controllers";
import HTTPS from "https";
import FileSystem from "fs";
const expressApplication: ExpressApplication = createExpressApplication();
expressApplication.get(
"/",
(_request: Express.Request, response: Express.Response): void => {
response.send("<h1>Hello, world!</h1>");
}
);
const HTTPS_Server: HTTPS.Server = HTTPS.createServer(
{
key: FileSystem.readFileSync("./SSL/key.pem"),
cert: FileSystem.readFileSync("./SSL/cert.pem")
},
expressApplication
);
supportClassSyntax(expressApplication);
HTTPS_Server.listen(443, "127.0.0.1");initializeRoutingControllersExpressHTTPS_Application encapsulates this complexity.
Configuration
HTTPS - The HTTPS Requirements
routingControllers - The "routing-controllers" configuration
Options of routing-controllers.
See the TypeScript type definitions of RoutingControllersOptions type
for reference.
Events Handers
onExpressApplicationCreated
(expressApplication: ExpressApplication) => Promise<void>As it obviously from the function name, called when basic Express application created. Basically being used to use the Express plugins and middlewares.
- routing-controllers library has not been involved yet.
- The express application instance will be passed via parameter.
onHTTPS_ServerCreated
(HTTPS_Server: NodeHTTPS.Server, expressApplication: ExpressApplication) => Promise<void>As it obviously from the function name, called when the HTTPS server created.
- routing-controllers library has not been involved yet.
- The HTTPS server instance, and also the express application instance will be passed via parameters.
onRoutingControllersSetupComplete
(expressApplication: ExpressApplication) => Promise<void>;As it obviously from the function name, called when the routing-controllers functionality has been initialized.
onRoutingControllersSetupComplete
() => Promise<void>As it obviously from the function name, called when the application actually started and ready to accept the HTTP requests. Usually being called to log the application starting.
redirectOnNotFound
redirectOnNotFound(targetRoute: string): (_request: Express.Request, response: Express.Response) => voidRedirects to specified route when response.headersSent value is false.
Intended to be used via expressApplicationUse() after the initialization of routing-controllers application
complete to redirect to "Not found" page.
Although with the plain Express the "Not found" page is being provided by other way, with routing-controllers the
usual ways may not work because of incorrectly
arranged routes.
There is no official recommendation how to redirect to "Not found" page when no route matchings found so
redirectOnNotFound function may be used for such purposes.
Route
The adapter for Method decorator from routing-controllers to HTTP_Methods enumeration from
"fundamental-constants"/"@yamato-daiwa/es-extensions".
QueryParametersProcessor
@QueryParametersProcessor.process is the alternative
@QueryParams from
routing-controllers.
In comparison with @QueryParams,
- Has customizable deserialization (does not use pre-deserialized
request.query) - Validation without additional classes via
RawObjectDataProcessorAPI; - Possible to modify object to which query string has been deserialized as far as
RawObjectDataProcessorsupports.
import { Controller, Get, Reader } from "routing-controllers";
import { QueryParametersProcessor } from "@yamato-daiwa/express-extensions";
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
@Controller()
export default class UserController {
@Get("/componets/users/edtior/new")
@Reader("Components/UserEditor/ForNewUser/EditorForNewUser.mvc.component.hbs")
protected async renderUserEditorFragmentForNewUser(
@QueryParametersProcessor.process({
userType: {
type: String,
required: true,
allowedAlternatives: Object.values(User.Types)
},
willBeFirstAmongOnesOfSameType: {
preValidationModifications: [ destringifyBooleanValue ],
type: Boolean,
required: true
},
currentContOfUsersOfSameType: {
preValidationModifications: [ convertPotentialStringToIntegerIfPossible ],
type: Number,
numbersSet: RawObjectDataProcessor.NumbersSets.naturalNumber,
required: true
}
})
{
userType,
willBeFirstAmongOnesOfSameType,
currentContOfUsersOfSameType
}: Readonly<{
}>
): Promise<EditorForNewUserMVC_FragmentVariables> {
console.log(userType)
console.log(willBeFirstAmongOnesOfSameType)
console.log(currentContOfUsersOfSameType)
// ...
}
}Query parameters deserializing
To set the default deserializer, use setDefaultDeserializer static method of QueryParametersProcessor.
import { QueryParametersProcessor } from "@yamato-daiwa/express-extensions";
import QueryString from "qs";
QueryParametersProcessor.setDefaultDeserializer(QueryString.parse)The parameter must be the functions accepts the serialized (thus the string) query parameters and return JSON-compatible type.
Obvious but frequently missed out: the deserializing algorithm on backend must correspond to serializing
algorithm on frontend.
Well, the QueryString.parse() is predefined deserializer for QueryParametersProcessor but even you are
fine with this one, add above code to your application to explicitly show that you selected this serializer
consciously.
Additionally, you can set the custom deserializer per request what is basically not recommended but inevitably if development of the client part is out your control.
import { Controller, Get, Reader } from "routing-controllers";
import { QueryParametersProcessor } from "@yamato-daiwa/express-extensions";
import { RawObjectDataProcessor, type ReadonlyParsedJSON_Object } from "@yamato-daiwa/es-extensions";
@Controller()
export default class UserController {
@Get("/componets/users/edtior/new")
@Reader("Components/UserEditor/ForNewUser/EditorForNewUser.mvc.component.hbs")
protected async renderUserEditorFragmentForNewUser(
@QueryParametersProcessor.process(
{
userType: {
type: String,
required: true,
allowedAlternatives: Object.values(User.Types)
},
// ...
},
(queryString: string): ReadonlyParsedJSON_Object => {
// ....
}
)
{
userType,
willBeFirstAmongOnesOfSameType,
currentContOfUsersOfSameType
}: Readonly<{
}>
): Promise<EditorForNewUserMVC_FragmentVariables> {
console.log(userType)
console.log(willBeFirstAmongOnesOfSameType)
console.log(currentContOfUsersOfSameType)
// ...
}
}QueryParametersDeserializingError
If error will occur during query parameters deserializing, the QueryParametersProcessor.QueryParametersDeserializingError
will be thrown.
You can detect it via error instanceof QueryParametersProcessor.QueryParametersDeserializingError
or error instanceof HttpError because QueryParametersProcessor.QueryParametersDeserializingError
extended from HttpError of routing-controllers.
Session
saveExpressSession
saveExpressSession(session: Session): Promise<void>The promisfied version of session.save(callback).
The promise will reject if the callback of session.save will receive
neither undefined nor null parameter.
disposeExpressSession
# === [ Overload 1 ] Must wait until completion
(session: Session, options: Readonly<{ mustWaitUntilCompletion: true; }>): Promise<void>;
# === [ Overload 2 ] Do not wait until completion
(session: Session, options: Readonly<{ mustWaitUntilCompletion: false; }>): void;The wrapper for session.destroy(callback).
- If the second parameter has been set to
{ mustWaitUntilCompletion: true }, this function will return the promise which will be resolved when the disposal will successfully complete. In this case, thedisposeExpressSessionis the promisfied version ofsession.destroy(callback). - If there is no need to wait the ending of disposal, set the second parameter to
{ mustWaitUntilCompletion: false }and this function will return nothing.
Request Body
parseAndValidateJSON_RequestBody
<RequestData extends ReadonlyParsedJSON>(
settings: Readonly<{
requestBodySizeLimit__bytesPackageFormat: string | number;
validationAndProcessing: RawObjectDataProcessor.ObjectDataSpecification;
mustLogDataAfterParsing?: boolean;
}>
): ExpressMiddlewareParses expected to be the JSON-type request body, validates it by RawObjectDataProcessor and, if demanded, modifying it by same util. The alternative to class-transformer which is transforming the request body to the instances of classes for the cases when there is no need to turn the objects to the instances of the classes.
- Requirements
- body-parser has not been applied neither globally nor locally, by other words, the request body has not been parsed yet.
- The class-transformer is disabled by
useExpressServer({ classTransformer: false })where theuseExpressServeris the function from routing-controllers
- Intended to be used as one of parameters of routing-controllers middleware.
validateAndProcessJSON_RequestBody
<RequestData extends ReadonlyParsedJSON>(
validationAndProcessing: RawObjectDataProcessor.ObjectDataSpecification,
{ mustLogDataAfterParsing = false }: Readonly<{ mustLogDataAfterParsing?: boolean; }> = { mustLogDataAfterParsing: false }
): ExpressMiddlewareValidates the pre-parsed JSON body by RawObjectDataProcessor and, if demanded, modifying it by same util. The alternative to class-transformer which is transforming the request body to the instances of classes for the cases when there is no need to turn the objects to the instances of the classes.
- Requirements
- The JSON-type request body has been preliminary parsed by body-parser.
- The class-transformer is disabled by
useExpressServer({ classTransformer: false })where theuseExpressServeris the function from routing-controllers
- Intended to be used as one of parameters of routing-controllers middleware.
