From 05ea1cd905541e342751ec6840d63743aceac440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9gane=20Lacheny?= Date: Wed, 5 Nov 2025 14:03:36 +0100 Subject: [PATCH] Add information on controllers & routes + Fix some examples --- .../cms/backend-customization/controllers.md | 39 +++++++++++++++++-- .../docs/cms/backend-customization/routes.md | 23 ++++++++--- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/docusaurus/docs/cms/backend-customization/controllers.md b/docusaurus/docs/cms/backend-customization/controllers.md index d6969725cb..75dc481553 100644 --- a/docusaurus/docs/cms/backend-customization/controllers.md +++ b/docusaurus/docs/cms/backend-customization/controllers.md @@ -164,13 +164,13 @@ module.exports = { { method: 'GET', path: '/hello', - handler: 'hello.index', + handler: 'api::hello.hello.index', } ] } ``` -```js "title="./src/api/hello/controllers/hello.js" +```js title="./src/api/hello/controllers/hello.js" module.exports = { async index(ctx, next) { // called by GET /hello @@ -183,14 +183,14 @@ module.exports = { -```js "title="./src/api/hello/routes/hello.ts" +```js title="./src/api/hello/routes/hello.ts" export default { routes: [ { method: 'GET', path: '/hello', - handler: 'hello.index', + handler: 'api::hello.hello.index', } ] } @@ -219,6 +219,37 @@ When a new [content-type](/cms/backend-customization/models#content-types) is cr To see a possible advanced usage for custom controllers, read the [services and controllers](/cms/backend-customization/examples/services-and-controllers) page of the backend customization examples cookbook. ::: +### Controllers & Routes: How routes reach controller actions + +- Core mapping is automatic: when you generate a content-type, Strapi creates the matching controller and a router file that already targets the standard actions (`find`, `findOne`, `create`, `update`, and `delete`). Overriding any of these actions inside the generated controller does not require touching the router — the route keeps the same handler string and executes your updated logic. +- Adding a route should only be done for new actions or paths. If you introduce a brand-new method such as `exampleAction`, create or update a route entry whose `handler` points to the action so HTTP requests can reach it. Use the fully-qualified handler syntax `::..` (e.g. `api::restaurant.restaurant.exampleAction` for an API controller or `plugin::menus.menu.exampleAction` for a plugin controller). +- Regarding controller and route filenames: the default controller name comes from the filename inside `./src/api/[api-name]/controllers/`. Core routers created with `createCoreRouter` adopt the same name, so the generated handler string matches automatically. Custom routers can follow any file naming scheme, as long as the `handler` string references an exported controller action. + +The example below adds a new controller action and exposes it through a custom route without duplicating the existing CRUD route definitions: + +```js title="./src/api/restaurant/controllers/restaurant.js" +const { createCoreController } = require('@strapi/strapi').factories; + +module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({ + async exampleAction(ctx) { + const specials = await strapi.service('api::restaurant.restaurant').find({ filters: { isSpecial: true } }); + return this.transformResponse(specials.results); + }, +})); +``` + +```js title="./src/api/restaurant/routes/01-custom-restaurant.js" +module.exports = { + routes: [ + { + method: 'GET', + path: '/restaurants/specials', + handler: 'api::restaurant.restaurant.exampleAction', + }, + ], +}; +``` + ### Sanitization and Validation in controllers {#sanitization-and-validation-in-controllers} :::warning diff --git a/docusaurus/docs/cms/backend-customization/routes.md b/docusaurus/docs/cms/backend-customization/routes.md index a72889a199..ef5747a454 100644 --- a/docusaurus/docs/cms/backend-customization/routes.md +++ b/docusaurus/docs/cms/backend-customization/routes.md @@ -29,6 +29,10 @@ Requests sent to Strapi on any URL are handled by routes. By default, Strapi gen Once a route exists, reaching it executes some code handled by a controller (see [controllers documentation](/cms/backend-customization/controllers)). To view all existing routes and their hierarchal order, you can run `yarn strapi routes:list` (see [CLI reference](/cms/cli)). +:::tip +If you only customize the default controller actions (`find`, `findOne`, `create`, `update`, or `delete`) that Strapi generates for a content-type, you can leave the router as-is. Those core routes already target the same handler names and will run your new controller logic. Add or edit a route only when you need a brand-new HTTP path/method or want to expose a custom controller action. +::: +
Simplified Strapi backend diagram with routes highlighted
The diagram represents a simplified version of how a request travels through the Strapi back end, with routes highlighted. The backend customization introduction page includes a complete, interactive diagram.
@@ -174,7 +178,7 @@ Creating custom routers consists in creating a file that exports an array of obj | -------------------------- | -------------------------------------------------------------------------------- | -------- | | `method` | Method associated to the route (i.e. `GET`, `POST`, `PUT`, `DELETE` or `PATCH`) | `String` | | `path` | Path to reach, starting with a forward-leading slash (e.g. `/articles`)| `String` | -| `handler` | Function to execute when the route is reached.
Should follow this syntax: `.` | `String` | +| `handler` | Function to execute when the route is reached.
Use the fully-qualified syntax `api::api-name.controllerName.actionName` (or `plugin::plugin-name.controllerName.actionName`). The short `.` form for legacy projects also works. | `String` | | `config`

_Optional_ | Configuration to handle [policies](#policies), [middlewares](#middlewares) and [public availability](#public-routes) for the route

| `Object` |
@@ -185,6 +189,15 @@ Dynamic routes can be created using parameters and regular expressions. These pa Routes files are loaded in alphabetical order. To load custom routes before core routes, make sure to name custom routes appropriately (e.g. `01-custom-routes.js` and `02-core-routes.js`). ::: +:::info Controller handler naming reference +The `handler` string acts as a pointer to the controller action that should run for the route. Strapi supports the following formats: + +- API controllers: `api::..` (e.g. `api::restaurant.restaurant.exampleAction`). The `` comes from the controller filename inside `./src/api//controllers/`. +- Plugin controllers: `plugin::..` when the controller lives in a plugin. + +For backwards compatibility, Strapi also accepts a short `.` string for API controllers, but using the fully-qualified form makes the route more explicit and avoids naming collisions across APIs and plugins. +::: +
Example of a custom router using URL parameters and regular expressions for routes @@ -202,12 +215,12 @@ module.exports = { { // Path defined with an URL parameter method: 'POST', path: '/restaurants/:id/review', - handler: 'restaurant.review', + handler: 'api::restaurant.restaurant.review', }, { // Path defined with a regular expression method: 'GET', path: '/restaurants/:category([a-z]+)', // Only match when the URL parameter is composed of lowercase letters - handler: 'restaurant.findByCategory', + handler: 'api::restaurant.restaurant.findByCategory', } ] } @@ -224,12 +237,12 @@ export default { { // Path defined with a URL parameter method: 'GET', path: '/restaurants/:category/:id', - handler: 'Restaurant.findOneByCategory', + handler: 'api::restaurant.restaurant.findOneByCategory', }, { // Path defined with a regular expression method: 'GET', path: '/restaurants/:region(\\d{2}|\\d{3})/:id', // Only match when the first parameter contains 2 or 3 digits. - handler: 'Restaurant.findOneByRegion', + handler: 'api::restaurant.restaurant.findOneByRegion', } ] }