Backend Customization of Strapi
- General
Backend Customization of Strapi
The Strapi Design System is fully customizable. We can customize the admin panel as well as the API. We can also extend our content management with custom plugins, in seconds. There are lots of stuff that we can customize and in this blog, I will explain to you how we can customize them.
Each part of Strapi’s back end can be customized:
- the requests received by the Strapi server,
- the routes that handle the requests and trigger the execution of their controller handlers,
- the policies that can block access to a route,
- the middlewares that can control the request flow and the request before moving forward,
- the controllers that execute code once a route has been reached,
- the services that are used to build custom logic reusable by controllers,
- the models that are a representation of the content data structure,
- the responses sent to the application that sent the request,
- and the webhooks that are used to notify other applications of events that occurred.
In this article, we’re going to be working with the Strapi backend and covering a few aspects of customizations to the Strapi backend. We’re touching on controllers, services, policies, webhooks and routes, and others.
Also, I’ll explain to you how we can create our custom plugin.
Let’s start the customization
Custom Controllers and Routes
Controllers are a very important aspect of how Strapi works and play a big role in customizing the backend. So, let’s go ahead and create a custom controller which overrides the find function and a route for it.
Create a Controller
To define a custom controller inside the core controller file for the product endpoint or collection type, we can pass in a function to the createCoreController
method.
Default implementation:
1 2 3 4 5 6 7 8 9 |
'use strict'; /** * product controller */ const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::product.product'); |
Custom code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
'use strict'; /** * product controller */ const { createCoreController } = require('@strapi/strapi').factories; module.exports = createCoreController('api::product.product',() => ({ async countProducts(ctx){ const {data, meta} = await super.find(ctx) return "Total products available: "+data.length } })); |
Here we are creating a countProducts which is a custom controller and it returns the count of products that are available or published. Since we have successfully created our custom controller now let’s move forward to create our custom routes.
Create a Route
We’re going to create custom route definitions for our countProducts controller. If we take a look at the already created product.js
route, we’ll see that the core route has already been created:
1 2 3 4 5 6 7 8 9 |
'use strict'; /** * product router. */ const { createCoreRouter } = require('@strapi/strapi').factories; module.exports = createCoreRouter('api::product.product'); |
We don’t have to make any modifications here in order to create our custom routes, we can create a new file for that. In order to access the controller we just created from the API, we need to add it to a route.
Create a new file that contains our custom route definition in the product/routes
directory – ./src/api/product/routes/count-products.js
with the
1 2 3 4 5 6 7 8 9 |
module.exports = { routes: [ { method: "GET", path: "/products/count", handler: "product.countProducts" } ] } |
What we’re basically doing here is creating an object with a routes
key, which has a value of an array of route objects.
The first object here defines a route with the method
of GET
and a path
– /products/count
. It also defines the handler
, which is the controller that will be used in the route, and in our case, that would be the countProduct
controller we created.
Now let’s add some products from the content manager of Strapi:
Test the Custom Controller and Routes
Let’s run the Strapi application by the
1 |
npm run develop |
or
1 |
yarn develop |
Once the app is running we can start sending requests API platform tool. I’ll be using POSTMAN. It is an API development tool that helps to build, modify and test APIs. It can be downloaded from here.
Once, you’ve gotten your API tester set up, send a GET
request to http://localhost:1337/api/products/count
.
As you can see, we’re getting a 403
forbidden error. This is because Strapi doesn’t return anything for unauthenticated routes by default. We need to modify the Permissions in Strapi admin panel so that it is available to the public.
To do that, go to the Strapi admin dashboard, then go to SETTINGS in our admin dashboard, and then USERS & PERMISSIONS PLUGIN > ROLES > PUBLIC.
As you can see, we have a new action – countProducts
. Enable it and click on SAVE. Now, if we try to send the request again, you should see the screenshot below.
Models
Creating a data structure for the content is one of the most important aspects of using the software. Models define a representation of the data structure.
There are 2 different types of models in Strapi:
- content-types, which can be collection types or single types, depending on how many entries they manage,
- and components that are data structures reusable in multiple content-types.
Model Creation
Content-types and component models are created and stored differently.
# Content-types
In my previous blog, I explained how we can create content types (Single-types, Collections). If you have no idea about it then please go through it first after then you will continue this article.
We could also create these content types using the strapi generate
with Strapi’s interactive CLI tool.
The content types have the following models files:
schema.json
for the model’s schema definition. (generated automatically when creating content-type with either method)lifecycles.js
for lifecycle hooks. This file must be created manually.
# Components
Component models can’t be created with CLI tools. Use the Content-type Builder or create them manually.
Components models are stored in the./src/components
folder. Every component has to be inside a subfolder, named after the category the component belongs to (see project structure).
Model Schema
The schema.json
file of a model consists of:
- settings, such as the kind of content type the model represents or the table name in which the data should be stored,
- information mostly used to display the model in the admin panel and access it through the REST and GraphQL APIs,
- attributes, which describe the data structure of the model,
- and options used to define specific behaviors on the model.
General settings for the model can be configured with the following parameters:
1 2 3 4 5 6 |
// ./api/[api-name]/content-types/restaurant/schema.json { "kind": "collectionType", "tableName": "Restaurants_v1", } |
Model Attributes
The data structure of a model consists of a list of attributes, and each attribute has a type parameter that describes its nature. They are scaler types(e.g. strings, dates, numbers, booleans, etc.), Strapi specific-types such as media
, relation
, component
,dynamiczone
and thelocale
andlocalizations
types. If you have no idea about these terms then you can refer to this article.
Validations
Basic validations can be applied to attributes using the following parameters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// ./src/api/[api-name]/content-types/[api-name]/schema.json { // ... "attributes": { "title": { "type": "string", "minLength": 3, "maxLength": 99, "unique": true }, "description": { "default": "My description", "type": "text", "required": true }, "slug": { "type": "uid", "targetField": "title" } // ... } } |
You can also refer to the official Strapi Models customization documentation for a better understanding of validations, relations, etc.
Policies
Policies are functions that execute specific logic on each request before it reaches the controller. They are mostly used for securing business logic. Each route of a Strapi project can be associated with an array of policies. For example, a policy named is-admin
could check that the request is sent by an admin user, and restrict access to critical routes.
Policies can be global or scoped. Global policies can be associated with any route in the project. Scoped policies only apply to a specific API or Plugin.
# Implementation
A new policy can be implemented with the interactive CLI commandstrapi generate
or manually by creating a JS file in the appropriate folder (see the folder structure)
Global policy implementation example:
1 2 3 4 5 6 7 8 9 10 |
// path: ./src/policies/is-authenticated.js module.exports = (policyContext, config, { strapi }) => { if (policyContext.state.user) { // if a session is open // go to next policy or reach the controller's action return true; } return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass }; |
Using config
object you can configure policies:
1 2 3 4 5 6 7 8 9 |
// path: .src/api/[api-name]/policies/my-policy.js module.exports = (policyContext, config, { strapi }) => { if (policyContext.state.user.role.code === config.role) { // if user's role is the same as the one described in configuration return true; } return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass }; |
You can get more information about policies on this URL.
Plugin Development
Strapi allows the development of local plugins that works exactly like the external plugin available from the marketplace.
Steps to create a plugin
Strapi provides a command line interface(CLI) for creating plugins. To create a plugin:
- Navigate to the root directory of the strapi project.
- Run
yarn strapi generate
ornpm run strapi generate
- Choose “plugin” from the list, and give the plugin a name in kebab-case(e.g my-plugin-name).
- Choose either
JavaScript
orTypeScript
for the plugin language. - Enable the plugin by adding it to the plugins configurations file:
12345678910// path: ./config/plugins.jsmodule.exports = {// ...'my-plugin': {enabled: true,resolve: './src/plugins/my-plugin' // path to plugin folder},// ...}
- Run
yarn build
ornpm run build
to build the plugin.
Plugins created using the preceding directions are located in theplugins
directory of the application (see project structure).
Add features to the plugin
Strapi provides programmatic APIs for plugins to hook into some of Strapi’s features.
Plugins can register with the server and/or the admin panel, by looking for entry point files at the root of the package:
strapi-server.js
for the Server (see Server API),strapi-admin.js
for the admin panel (see Admin Panel API).
Conclusion
Congratulations, you have seen it to the end of this guide. You have seen how to customize controllers, routes, models, and policies, and also we have seen how to create our custom plugin on Strapi. If you have any doubts or queries regarding this then you can also follow the links that I provided below in References.
REFERENCES
Related content
Auriga: Leveling Up for Enterprise Growth!
Auriga’s journey began in 2010 crafting products for India’s