Originally published on strongloop.com
Previously, we looked at how to add a global interceptor. In this article, we are going to build an application that validates the incoming request using class level and method level interceptors
For the complete application, you can go to this repo: https://github.com/dhmlau/loopback4-interceptors
Let's Build a Simple Order App
If you want to skip this part, you can check out the orginal-app
branch of this github repo: https://github.com/dhmlau/loopback4-interceptors/tree/original-app.
If you want to follow along, take these steps.
Scaffold the app by calling
lb4 app loopback4-interceptors --yes
In the newly created project, create a file called
order.json
with the following content:{
"name": "Order",
"base": "Entity",
"properties": {
"orderNum": {
"type": "string",
"id": true,
"required": true
},
"customerNum": {
"type": "string",
"required": true
},
"customerEmail": {
"type": "string",
"required": true
},
"total": {
"type": "number",
"required": true
}
}
}Create the Order model by providing the above json file. Run
lb4 model --config order.json --yes
Create a DataSource with the in-memory connector. Run
lb4 datasource ds
. Then select "In-memory db" as the type of connector.Create a Repository and accept all defaults by running
lb4 repository
command.Finally, create a controller. Follow the prompts as below:
$ lb4 controller
? Controller class name: Order
Controller Order will be created in src/controllers/order.controller.ts
? What kind of controller would you like to generate? REST Controller with CRUD functions
? What is the name of the model to use with this CRUD repository? Order
? What is the name of your CRUD repository? OrderRepository
? What is the type of your ID? string
? What is the base HTTP path name of the CRUD operations? /orders
create src/controllers/order.controller.ts
update src/controllers/index.ts
Controller Order was created in src/controllers/
You now have a LoopBack application ready to run.
Creating Interceptor Function for Validation
What we're doing here is validating user inputs at the REST layer. Note that in real world, you should also consider validating data at the Repository level to ensure the validation is applied even when modifying data from places outside of controllers, e.g. tests or services running in background.
For the POST /order
endpoint, we are going to validate the order before actually creating the order. The length of orderNum
has to be 6, otherwise the order is not valid.
Let's define the interceptor function in the OrderController
class just before the class is defined.
In src/controllers/order.controller.ts
:
Add this statement:
import {intercept, Interceptor} from '@loopback/core';
Add the following function to validate order.
const validateOrder: Interceptor = async (invocationCtx, next) => {
console.log('log: before-', invocationCtx.methodName);
const order: Order = new Order();
if (invocationCtx.methodName == 'create')
Object.assign(order, invocationCtx.args[0]);
else if (invocationCtx.methodName == 'updateById')
Object.assign(order, invocationCtx.args[1]);
if (order.orderNum.length !== 6) {
throw new HttpErrors.InternalServerError('Invalid order number');
}
const result = await next();
return result;
};
Apply @intercept
Decorator at the Method Level
After defining the interceptor function, you can now use this as a method-level or class-level decorator. For class-level interceptor, you just apply it on the class, like this.
@intercept(validateOrder)
export class OrderController {
//...
}
However, we want the validation to run only when the order is being created, so the @intercept
decorator will be applied at the method level. To do this, add the @intercept
decorator on the method I want to intercept, i.e. the POST
method:
```ts
@intercept(validateOrder) // <--- add here
@post('/orders', {
responses: {
'200': {
description: 'Order model instance',
content: {'application/json': {schema: {'x-ts-type': Order}}},
},
},
})
async create(@requestBody() order: Order): Promise<Order> {
```
Let's Experiment!
Now that the application is ready to go, start the app by running npm start
command and go to the API Explorer: http://localhost:3000/explorer.
Calling GET /orders/count
Since this method doesn't have the interceptor, when this endpoint is being called, there shouldn't be anything printed to the log.
Calling POST /orders
This is the method where the interceptor is applied to validate the order. Let's give it a try with a valid order.
{
"orderNum": "111111",
"customerNum": "12345",
"customerEmail": "aa@abc.com",
"total": 100
}
You should expect to get the HTTP 200 response code that the order has been created successfully.
Now try another order with orderNum
is 11
.
{
"orderNum": "11",
"customerNum": "67890",
"customerEmail": "bb@abc.com",
"total": 1000
}
You should expect a HTTP 500 error with message saying "Internal Server Error".
Resources
- Interceptor docs page
- Caching enabled via interceptors in Greeter application
- Authorization added using interceptors in this tutorial
Call to Action
LoopBack's success depends on you. We appreciate your continuous support and engagement to make LoopBack even better and meaningful for your API creation experience. Please join us and help the project by:
- Reporting issues.
- Contributing code and documentation.
- Opening a pull request on one of our "good first issues".
- Joining our user group.