How to add validation to a custom API in Strapi?
Vivek Agarwal wrote a great tutorial for adding custom API endpoints in Strapi. It didn't include any tips for validating the request input. In this tutorial, I will show how to create a pleasant user-friendly API validation experience in Strapi 4.
Strapi is using Koa. An easy way to add validation for custom API endpoints is by using Koa compatible validation library. I've had a good experience with node-input-validator. It has a very nice validation syntax and good flexibility for defining new validation rules.
This tutorial is tested with
Framework / Library | Version |
Strapi | 4.5.5 |
node-input-validator | 4.5.1 |
Prerequisites
Install Strapi with Typescript option.
$ npm i node-input-validator --save
Steps
1. Create a custom API endpoint
$ npx strapi generate
? Strapi Generators api - Generate a basic API
? API name check-number
? Is this API for a plugin? No
✔ ++ /api/check-number/routes/check-number.ts
✔ ++ /api/check-number/controllers/check-number.ts
✔ ++ /api/check-number/services/check-number.ts
2. Create a route middleware
$ npx strapi generate
? Strapi Generators middleware - Generate a middleware for an API
? Middleware name validate
? Where do you want to add this middleware? Add middleware to an existing API
? Which API is this for? check-number
✔ ++ /api/check-number/middlewares/validate.ts
3. Implement validation middleware
// File: /api/check-number/middlewares/validate.ts.
import niv from "node-input-validator";
export default (config) => {
return niv.koa();
};
4. Implement a route
// File: /api/check-number/routes/check-number.ts
export default {
routes: [
{
method: 'POST',
path: '/is-it-big',
handler: 'check-number.checkBig',
config: {
policies: [],
middlewares: ["api::check-number.validate"],
},
},
],
};
5. Implement a controller
// File: /api/check-number/controllers/check-number.ts
export default {
checkBig: async (ctx, next) => {
try {
await ctx.validate({
number: "required|integer|min:0",
});
const checkNumberService = strapi.service(
"api::check-number.check-number"
);
ctx.body = checkNumberService.confirmBigNumber(
Number(ctx.request.body.number)
);
} catch (err) {
ctx.body = err;
}
},
};
6. Implement a service
// File: /api/check-number/services/check-number.ts
export default {
confirmBigNumber: (number: number): string => {
if (number >= 1000000) {
return `${number} is big! Yay! 👍`;
}
if (number < 1000000) {
return `${number} is too small! Boo! 👎`;
}
},
};
7. Allow access to the API endpoint
Log in to the Strapi admin interface and allow the checkBig endpoint. See the screenshot below for reference.
8. Test the API endpoint
Ensure that Strapi is running on http://localhost:1337. Then try these curl commands.
$ curl -X POST -F 'number=word' localhost:1337/api/is-it-big
{"message":"Unprocessable Entity","body":{"message":"The given data is invalid.","errors":{"number":{"message":"The number must be an integer.","rule":"integer"}}}}
$ curl -X POST -F 'number=-1' localhost:1337/api/is-it-big
{"message":"Unprocessable Entity","body":{"message":"The given data is invalid.","errors":{"number":{"message":"The number must be at least 0.","rule":"min"}}}}
$ curl -X POST -F 'number=10000' localhost:1337/api/is-it-big
10000 is small! 👎
$ curl -X POST -F 'number=11234152' localhost:1337/api/is-it-big
11234152 is big! Yay! 👍
How about GET params?
Here we go.
// File: /api/check-number/routes/check-number.ts
export default {
routes: [
...
{
method: "GET",
path: "/credit-card/:input",
handler: "check-number.checkCreditCard",
config: {
policies: [],
middlewares: ["api::check-number.validate"],
},
},
],
};
// File: /api/check-number/controllers/check-number.ts
export default {
...
checkCreditCard: async (ctx, next) => {
try {
await ctx.validate(
{ input: "required|creditCard" },
ctx.params
);
ctx.body =
"Are you crazy!? You just sent a credit card number inside a GET parameter.";
} catch (err) {
ctx.body = err;
}
},
};
Postfix
Let me know if you found this useful!
Please, comment also if you have any critical notes or some ideas on how to do validation even better! It's always valuable to share ideas and insights so we can all be better developers together. Thanks!
Subscribe to my newsletter
Read articles from Juho Viitasalo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by