Writing Handler Middleware
Similar to express middleware, @simply-openapi/controllers provides a middleware system to process requests. All of the library's functionality is built out in this middleware system, allowing you to provide your own middleware to override or alter behavior as-needed.
Middleware in @simply-openapi/controllers has multiple purposes:
Validate that the request matches the OpenAPI specification
Extract, preprocess, and transform data from the request object for consumption by the operation handlers.
Handle operation handler return values, serializing them, and sending them back with the response
Trapping and handling thrown errors.
The basics of handler middleware
Handler middleware takes the form of an async function that is called with the context of a request. As with express, it is responsible for calling the next handler in the chain. It can then reinterpret the results of that call, either returning it to the next handler up the chain, or handling it directly and returning undefined
to indicate that no further processing is needed.
For example, @simply-openapi/controllers provides this default handler, which is responsible for sending json object responses.
Middleware basics
Middleware functions have a few traits they must abide by:
A middleware function must be declared to take 2 arguments: A context, and a next function.
A middleware function must return a promise.
In general, a typical middleware will do prepratory work before the call, extract and register data for the controller method arguments, call and await next(), then process the returned result. It will then either forward that result as its own return value, or return undefined
to communicate to up-chain middleware that the result has been handled.
The Middleware Context
The first argument to the middleware is its context, which contains all the information needed to proces the response. Some useful properties are:
req
- The express request.res
- The express response.spec
- The top level OpenAPI specification.method
- The request / operation method.path
- The request / operation path (as it exists in the OpenAPI spec).
Middleware factories
Sometimes, middleware might want to do prepratory work on the method it is bound to. This can involve verifying the validity of the OpenAPI schema or pre-compiling json-schema validators so that the actual request is as fast as possible.
In cases like this, you can provide a middleware factory. A middleware factory is called for every operation that uses it at the time of router creation, and returns a middleware function to use for that operation.
Writing middleware for request validation
Basic request validation
Request validation can be performed through handler middleware. If a middleware determines that a request is invalid, it should throw an error derived from the http-error
library to propogate the failure up the middleware stack for eventual transmission.
Schema based validation
As AJV performs code generation to 'compile' the schemas, middleware that wants to perform validation with it needs to be able to pre-process these schemas to perform the heavy compilation step on startup rather than re-compiling the schema on every request. However, often the schema required is dependent on the particular operation, which must be derived seperately for every operation.
Middleware Factories make this possible.
Middleware for data extraction
Middleware can be used to extract data from the requests to be presented in a processed form to the controller method.
This is done with Request Data:
Middleware for result transmission
Middleware can be used to intercept the result of a controller method. It can transform the results to pass upstream, or it can serialize it directly to the express response.
Default middleware
All of the default behavior of @simply-openapi/controllers is implemented through middleware and middleware factories. The default middleware performs the following in order:
Processes the global and operation-specific
security
OpenAPI directives. Improper requests are denied, and proper requests have their authentication result registered as request data for the@RequireAuthentication
and@BindSecurity
decorators.Processes the path and operation
parameters
OpenAPI directives. Invalid requests are rejected, and values are coerced and registered for the various parameter-centric decorators (@QueryParam
,@PathParam
, and similar).Process the
requestBody
OpenAPI directive. Data is validated and coerced according to its media type, and registered for the@Body
decorator and its variants.If no further middleware has processed the handler result, and the handler result is an instance of the
HandlerResult
class, handling of the result is delegated to that class.If no further middleware has processed the handler result, the handler result is checked to see if it is a plain (non class instance) JSON object. If so, it is trasnformed into a HandlerResult, specifying a content type of "application/json" and setting the status code to 200. This handler result is then passed up the middleware chain.
Last updated