============== Route Handlers ============== Route Handlers are a collection of ExpressJS middleware functions whose job it is to process an incoming request for a given RESTful endpoint. Collections of handlers are grouped together by a common name or schema, depending on the OpenAPI specification defined. Composer makes heavy use of both functional and aspect oriented programming techniques. Each Operation defined in the OpenAPI specification is generated to a single function and is decorated with the necessary TypeScript decorators to indicate their function. Anatomy of a Route ================== For example, given the following Operation object defined in an OpenAPI specification file is for a ``GET`` request to retrieve a single object resource of the ``Pet`` schema given a specified unique identifier. .. code-block:: yaml :linenos: /pet/{id}: x-schema: Pet parameters: - $ref: "#/components/parameters/id" get: description: Returns a single Pet from the system that the user has access to x-name: findById responses: "200": description: A Pet object. content: application/json: schema: $ref: "#/components/schemas/Pet" The resulting route handler class and function as generated by Composer will look like the following. .. code-block:: TypeScript :linenos: /** * Handles all REST API requests for the endpoint `/pet`. * * @author AcceleratXR, Inc. */ @Model(Pet) @Route("/pet") class PetRoute extends ModelRoute { ... /** * Returns a single Pet from the system that the user has access to */ @Get("/:id") private async findById(@Param("id") id: string, @AuthUser user?: JWTUser): Promise { return super.doFindById(id, user); } ... } First notice how the class itself is constructed. The Path Item in the OpenAPI specification specifies an ``x-schema`` field with value ``Pet``. This means that all Path Items that also specify ``Pet`` will be grouped into the same ``PetRoute.ts`` file. The ``@Model(Pet)`` decorator is used to identify that this Route handler class is associated with the ``Pet`` data model. When the Composer server starts up and scans this class it therefore uses this information to bind the correct datastore connection. The class also uses the ``@Route`` decorator. This indicates to the Server that the class is responsible for processing RESTful endpoints. The decorator can take either a single string value or an array as its sole parameter corresponding to the root path patterns that the handler will respond to. In this example, this route handler is responsible for processing all incoming requests to the ``/pet`` path. Finally we get to the endpoint handler itself, ``findById``. The name ``findById`` directly corresponds to the ``x-name`` value specified in the Operation object's definition. Further, the HTTP method is denoted by the ``@Get`` decorator and take an optional argument ``/:id``. This path gets appended to the root defined at the top of the class ``/pet/:id`` when the class is processed by the Server and registered with ExpressJS. Each function defined with a corresponding HTTP method decorator is registered to ExpressJS accordingly as a middleware function. Should the handler also make use of the ``@Before`` and ``@After`` decorators, these additional functions will be registered respectively with the route handler function as additional middleware for the given endpoint path. The function itself takes multiple arguments, namely ``@Param("id") id: string, @AuthUser user?: JWTUser``. The First argument with the ``@Param`` decorator indicates that the path contains one or more parameters, one of which is named ``id`` and that the server should pull that value out of the request path and pass its value in directly to this argument. Therefore, if the HTTP request that arrives is for the path ``/pet/scotty`` then the value of the ``id`` argument will be ``scotty``. The second argument is for the authorized user that performed the request as denoted by the ``@AuthUser`` decorator. As this is an optional argument, a value is only passed in when a user has actually authenticated with the service and is valid. In all other cases this value will be ``undefined`` to indicate that no valid user is attempting to perform this action. The final thing to notice about this function is the body and return type. The return type is of type ``Promise``. This indicates to the server that the handler is async and will return a ``Pet`` object if the object was found with the given id or ``undefined`` object if it could not be at some point in the future. The return of the function does not have to be a ``Promise`` and in fact can be a direct value. The server will automatically adjust its behavior accordingly. When one of these values is returned, the server will automatically encode the object as JSON and return it to the client. The body of the function makes a single call to ``super.doFindById(id, user)``. Notice that the class's definition inherits from ``ModelRoute``. This is a base class to which all Model route handlers can extend to provide common built-in behavior such as this. It's purpose is to reduce the amount of code needed for common data oriented REST APIs. In this particular case we are leveraging the built-in function ``doFindById``. This function performs generic logic for retrieving a single record of the desired type from the datastore. The built-in will also handle validating permissions for accessing the object for the authorized user when Access Control Lists are enabled. Route Decorators ================ The following is a list of decorators that can used within a Route handler class to perform various HTTP processing behavior. ``@Route`` ~~~~~~~~~~ The ``@Route`` decorator is used to indicate that a given class contains one or more endpoint handlers for a given set of paths. .. code-block:: TypeScript :linenos: :emphasize-lines: 7 /** * Handles all REST API requests for the endpoint `/pet`. * * @author */ @Model(Pet) @Route("/pet") class PetRoute extends ModelRoute { } ``@Init`` ~~~~~~~~~ The ``@Init`` decorator indicates a function within a Route handler class that should be called at service startup in order to initialize some state. .. code-block:: TypeScript :linenos: :emphasize-lines: 4 /** * Called on server startup to initialize the route with any defaults. */ @Init private async initialize() { // TODO Add business logic here } ``@Auth`` ~~~~~~~~~ The ``@Auth`` decorator is used to indicate that the decorated endpoint handle requires authentication by one of the specified methods. .. code-block:: TypeScript :linenos: :emphasize-lines: 4 /** * Add a new pet to the store */ @Auth(["jwt"]) @Post() private async add(obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); throw new Error("This route is not implemented."); } ``@Before`` ~~~~~~~~~~~ This decorator is used to indicate additional middleware functions that should be executed **before** the main endpoint handler is executed. This is typically used for input pre-processing and validation. .. code-block:: TypeScript :linenos: :emphasize-lines: 5 /** * Add a new pet to the store */ @Auth(["jwt"]) @Before(["validate"]) @After(["prepareOutput"]) @Post() private async add(obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); throw new Error("This route is not implemented."); } ``@After`` ~~~~~~~~~~ The ``@After`` decorator is used to indicate additional middleware functions that should be expected **after** the main endpoint handler is executed. This is typically used for post-processing and data preparation before returning to the client. .. code-block:: TypeScript :linenos: :emphasize-lines: 6 /** * Add a new pet to the store */ @Auth(["jwt"]) @Before(["validate"]) @After(["prepareOutput"]) @Post() private async add(obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); throw new Error("This route is not implemented."); } ``@Delete`` ~~~~~~~~~~~ The ``@Delete`` decorator is used to indicate that the handler function will process HTTP requests with method ``DELETE``. It takes an optional parameter to provide a sub-path. .. code-block:: TypeScript :linenos: :emphasize-lines: 5 /** * Deletes the Pet */ @Auth(["jwt"]) @Delete("/:id") private async delete(@Param("id") id: string, @AuthUser user?: JWTUser): Promise { return super.doDelete(id, user); } ``@Get`` ~~~~~~~~ The ``@Get`` decorator is used to indicate that the handler function will process HTTP requests with method ``GET``. It takes an optional parameter to provide a sub-path. .. code-block:: TypeScript :linenos: :emphasize-lines: 4 /** * Multiple Pet objects */ @Get() private async find(@AuthUser user?: JWTUser): Promise> { throw new Error("This route is not implemented."); } ``@Post`` ~~~~~~~~~ The ``@Post`` decorator is used to indicate that the handler function will process HTTP requests with method ``POST``. It takes an optional parameter to provide a sub-path. .. code-block:: TypeScript :linenos: :emphasize-lines: 5 /** * Add a new pet to the store */ @Auth(["jwt"]) @Post() private async add(obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); throw new Error("This route is not implemented."); } ``@Put`` ~~~~~~~~ The ``@Put`` decorator is used to indicate that the handler function will process HTTP requests with method ``PUT``. It takes an optional parameter to provide a sub-path. .. code-block:: TypeScript :linenos: :emphasize-lines: 5 /** * Updates a single Pet */ @Auth(["jwt"]) @Put("/:id") @Validate("validate") private async update(@Param("id") id: string, obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); return super.doUpdate(id, newObj, user); } ``@Validate`` ~~~~~~~~~~~~~ This decorator is used to indicate a middleware function that will execute **before** the main endpoint handler in order to validate the incoming data. It must be the name of a function within the class itself. .. code-block:: TypeScript :linenos: :emphasize-lines: 15 /** * Determines if the specified request payload is valid and can be accepted. * * @throws When the request payload contains invalid input or data. */ private validate(data: Pet): void { // TODO Validate input data } /** * Updates a single Pet */ @Auth(["jwt"]) @Put("/:id") @Validate("validate") private async update(@Param("id") id: string, obj: Pet, @AuthUser user?: JWTUser): Promise { const newObj: Pet = new Pet(obj); return super.doUpdate(id, newObj, user); } ``@WebSocket`` ~~~~~~~~~~~~~~ The ``@WebSocket`` decorator is used to indicate that the handler function will process HTTP Upgrade requests in order to establish a WebSocket connection with the client. It takes an optional parameter to provide a sub-path. .. code-block:: TypeScript :linenos: :emphasize-lines: 4 /** * Create a WebSocket connection with the client. */ @WebSocket() private async connect(@Socket ws: ws, @AuthUser user?: JWTUser): Promise> { ws.on("message", (msg) => { ws.send(`echo ${msg}`); }); ws.send(`hello ${user ? user.uid : "guest"}`); }