Back-end customization
:::strapi Disambiguation: Strapi back end As a headless CMS, the Strapi software as a whole can be considered as the "back end" of your website or application. But the Strapi software itself includes 2 different parts:
- The back-end part of Strapi is an HTTP server that Strapi runs. Like any HTTP server, the Strapi back end receives requests and send responses. Your content is stored in a database, and the Strapi back end interacts with the database to create, retrieve, update, and delete content.
- The front-end part of Strapi is called the admin panel. The admin panel presents a graphical user interface to help you structure and manage the content.
Throughout this developer documentation, 'back end' refers exclusively to the back-end part of Strapi.
The User Guide explains how to use the admin panel and the admin panel customization section details the various customization options available for the admin panel. :::
The Strapi back end runs an HTTP server based on Koa, a back-end JavaScript framework.
Like any HTTP server, the Strapi back end receives requests and send responses. You can send requests to the Strapi back end to create, retrieve, update, or delete data through the REST or GraphQL APIs.
A request can travel through the Strapi back end as follows:
- The Strapi server receives a request.
- The request hits global middlewares that are run in a sequential order.
- The request hits a route.By default, Strapi generates route files for all the content-types that you create (see REST API documentation), and more routes can be added and configured.
- Route policies act as a read-only validation step that can block access to a route. Route middlewares can control the request flow and mutate the request itself before moving forward.
- Controllers execute code once a route has been reached. Services are optional, additional code that can be used to build custom logic reusable by controllers.
- The code executed by the controllers and services interacts with the models that are a representation of the content data structure stored in the database.Interacting with the data represented by the models is handled by the Entity Service and Query Engine.
- The server returns a response. The response can travel back through route middlewares and global middlewares before being sent.
Both global and route middlewares include an asynchronous callback function, await next()
. Depending on what is returned by the middleware, the request will either go through a shorter or longer path through the back end:
- If a middleware returns nothing, the request will continue travelling through the various core elements of the back end (i.e., controllers, services, and the other layers that interact with the database).
- If a middleware returns before calling
await next()
, a response will be immediately sent, skipping the rest of the core elements. Then it will go back down the same chain it came up.
Please note that all customizations described in the pages of this section are only for the REST API. GraphQL customizations are described in the GraphQL plugin documentation.
Learn by example If you prefer learning by reading examples and understanding how they can be used in real-world use cases, the Examples cookbook section is another way at looking how the Strapi back end customization works. :::
Interactive diagram​
The following diagram represents how requests travel through the Strapi back end. You can click on any shape to jump to the relevant page in the documentation.
graph TB
request[Request] ---> globalMiddlewareA(("Global middlewarebefore await next()"))
globalMiddlewareA --"Call next()"--> routePolicy{Route policy}
globalMiddlewareA --"Returns before next()Goes back up in the middleware chain"-->globalMiddlewareB
routePolicy --Returns true--> routeMiddlewareA(("Route middlewarebefore await next()"))
routePolicy --Returns false or an error-->globalMiddlewareB
routeMiddlewareA --"Returns before next()Goes back up in the middleware chain"-->routeMiddlewareB
routeMiddlewareA --"Call next()"--> controllerA{{Controller}}
controllerA --"Call Service(s)"--> serviceA{{Service}}
controllerA --"Don't call Service(s)" --> routeMiddlewareB
serviceA --"Call Entity Service" --> entityService{{Entity Service}}
serviceA --"Don't call Entity Service" --> controllerB
entityService --"Call Query Engine"--> queryEngine{{Query Engine}}
entityService --"Don't call Query Engine" --> serviceB
queryEngine --> lifecyclesBefore[/Lifecycle beforeX\]
lifecyclesBefore[/Lifecycle beforeX\] --> database[(Database)]
database --> lifecyclesAfter[\Lifecycle afterX/]
lifecyclesAfter --> serviceB{{"Serviceafter Entity Service call"}}
serviceB --> controllerB{{"Controllerafter service call"}}
controllerB --> routeMiddlewareB(("Route middlewareafter await next()"))
routeMiddlewareB --> globalMiddlewareB(("Global middlewareafter await next()"))
globalMiddlewareB --> response[Response]
linkStyle 3 stroke:green,color:green
linkStyle 4 stroke:red,color:red
linkStyle 2 stroke:purple,color:purple
linkStyle 5 stroke:purple,color:purple
click request "/dev-docs/backend-customization/requests-responses"
click globalMiddlewareA "/dev-docs/backend-customization/middlewares"
click globalMiddlewareB "/dev-docs/backend-customization/middlewares"
click routePolicy "/dev-docs/backend-customization/routes"
click routeMiddlewareA "/dev-docs/backend-customization/routes"
click routeMiddlewareB "/dev-docs/backend-customization/routes"
click controllerA "/dev-docs/backend-customization/controllers"
click controllerB "/dev-docs/backend-customization/controllers"
click serviceA "/dev-docs/backend-customization/services"
click serviceB "/dev-docs/backend-customization/services"
click entityService "/dev-docs/api/entity-service/"
click lifecyclesBefore "/dev-docs/backend-customization/models#lifecycle-hooks"
click queryEngine "/dev-docs/api/query-engine/"
click lifecyclesAfter "/dev-docs/backend-customization/models#lifecycle-hooks"
click response "/dev-docs/backend-customization/requests-responses"
click queryEngine "/dev-docs/api/query-engine"