Routing
The routing middleware is registered in the last position of the middleware chain. It is in charge of loading the controllers and redirect the execution flow to the corresponding one depending on the request. Therefore, the controllers are part of the router middleware execution flow, and the res.send()
function being called by them is the reason why this middleware must be registered last.
Loading controllers
Controllers are loaded upon initialization and stored in a in-memory map for redirecting to them on each request received by the API. There are two possibilities when it comes to loading controllers:
- Loading them through custom fields in the OpenAPI document.
- Using experimental JSDoc annotations in controllers.
These options can't be combined. For consistency reasons, you must stick to one of them.
Through OpenAPI document
This is the predetermined option, since useAnnotations
is disabled by default in configuration. It uses custom attributes on the OpenAPI document to redirect to the correct controller each time. However, OAS Tools provides an auxiliary function capable of generate names in case those attributes are not set.
Default controller names
When there are no attributes in the OpenAPI document that specify user defined names for controllers or operations, OAS Tools generates a name for them:
- For controllers, the generated has the following format
{endpoint}Controller
removing the endpoint slashes/
- For methods, similarly to controllers, the generated name format is
func{endpoint}
removing endpoint slashes.
For example, assuming the following OpenAPI document:
openapi: 3.0.0
info:
version: 1.0.0
title: Quickstart
description: Quickstart for OAS-Tools
paths:
/api/v1/entity:
get:
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
The generated name for the controller handling /api/v1/entity
endpoint would be apiv1entityController
, and the operation name for the GET would be funcapiv1entity
. That way, when making a GET request to {server}/api/v1/entity
, the execution flow would be redirected to the funcapiv1entity
inside {controllers}/apiv1entityController.js
.
x-router-controller
The x-router-controller
property can be added at the path level or at the operation level of an OpenAPI declaration. When declared at path level, all operations for that path will be redirected to the specified controller unless different is specified through x-router-controller
at the operation level. That means the operation level overrides what is specified on the path level. The following example describes this situation:
openapi: 3.0.0
info:
version: 1.0.0
title: Quickstart
description: Quickstart for OAS-Tools
paths:
/api/v1/entity:
x-router-controller: EntityController # Path level
get:
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
post:
x-router-controller: DifferentController # Operation level
responses:
'201':
description: Success
Using the above declaration, when making a GET request to /api/v1/entity
, the control flow would be redirected to funcapiv1entity
inside EntityController.js
since the default controller name is overriden by the x-router-controller
at the path level. However, when making a POST request to the same endpoint, the router middleware would redirect to DifferentController
because the operation level has priority over the path level x-router-controller
property.
Operation Id
The default names don't work well when having multiple operations on the same path, since the generated name would be the same for all of them and therefore the same function would be handling multiple REST operations in a controller. That's where the operationId
attribute takes action. OAS Tools read the operationId
from each of the operations declared for a path and loads the function with that name in the corresponding controller, preventing a single function handling multiple operations for a path.
openapi: 3.0.0
info:
version: 1.0.0
title: Quickstart
description: Quickstart for OAS-Tools
paths:
/api/v1/entity:
x-router-controller: EntityController
get:
operationId: getEntity
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
post:
operationId: postEntity
responses:
'201':
description: Success
When making a GET request to /api/v1/entity
endpoint of an OAS Tools server implementing the functionality for the above OpenAPI declaration, the Router middleware would redirect the traffic to getEntity
function inside EntityController.js
.
Through JSDoc annotations (Experimental)
This feature is experimental and may suffer changes or be completely removed or replaced by Javascript decorators in future versions. Feedback is appreciated!
In order to use this feature, you first need to enable it through configuration: useAnnotations = true
. Once enabled, you will be able to write JSDoc style annotations to configure your project's operation. Read more about JSDoc annotations in the JSDoc documentation. The next sections describe predefined annotations that can be used to configure routing.
The Controller annotation
This annotation will tell the router middleware that the current file is a controller that handles request for some endpoint path. It must be placed in the first line of the file with the following format: @oastools {Controller} <path>
.
Using this approach, it is no longer necessary to name your controllers in a certain way. However is is still necessary to place them under the {controllers}/
directory set in configuration. For example, the following file would be identified as a controller handling requests to /api/v1/users
:
// controllers/randomName.js
/** @oastools {Controller} /api/v1/users */
const someDependency = require('whatever');
...
The Method annotation
This annotation must be used on top of function declarations. It tells the framework which type of request is handled by the annotated function. Just like with controller annotation, this annotations makes no longer necessary to name the functions a certain way. The format followed by the method annotation is: @oastools {method} <METHOD>
, for example:
// controllers/randomName.js
/** @oastools {Controller} /api/v1/users */
/**
* @oastools {method} GET
*/
module.exports.someFunction = function aGetRequest(req, res) {
/* Do something */
};
The code above would be identified as a GET handler for /api/v1/users
. This way, when making a GET request to that endpoint, the router middleware will execute that function.
The Path annotation
The path annotation allows to attach a subpath to the controllers path. It is useful when the endpoint takes parameters in the path. It is formatted as @oastools {path} <subpath>
, for example:
// controllers/randomName.js
/** @oastools {Controller} /api/v1/users */
/**
* @oastools {method} GET
* @oastools {path} /{id}
*/
module.exports.someFunctionWithId = function aGetRequestWithId(req, res) {
/* Do something */
};
The function declared above would handle GET requests to /api/v1/users/{id}
, since the /{id}
subpath has been specified by the annotation.