How to add custom routes to core routes in Strapi 4

System Information
  • Strapi Version: 4
  • Operating System: MacOS
  • Database: sqlite / Postgress
  • Node Version: v16.13.1
  • NPM Version:
  • Yarn Version: 3.1.1

Hi, In Strapi 3 all routes were handled in the routes.json. When a new content type was added, all core routes were added to this routes.json automatically. When there was a need for an additional route, I could duplicate on of the existing routes and modify it.

Now with Strapi 4 the routes are not added to a routes file anymore, instead only a createCoreRouter() function is called which dynamically creates all core routes.

For modifying the routes, the Strapi 4 Documentation lists two ways:
First, modifying the core routes by adding a second paramater to this createCoreRouter() function.
Second, using a custom router and adding all routes manually.

However, I would like to use a combination of both. I want to use all core routes but additionally some custom ones e.g. “deleteAllExpired”.

Is there a way to create a custom additional route, without rebuilting the core routes manually? e.g. extending the createCoreRouter() function with some custom routes? I did not find an example on how to do this.

Thank you for your help.

3 Likes

I have acheieved the same by creating a custom route file along with the default route file


In my custom routes files, I have defined the mapping to custom controller

In custom-controller.js, you can defined your controller like this

hope this help.

4 Likes

Thank you, this worked for me. I didn’t think of saving two routes files in parallel in the routes folder.

Quick question:

How would you get the body content (i.e. ctx.request.body) in one of these custom controller functions?

It seems to be undefined for me, for some reason.

EDIT: found my answer here

@Meher_Chandan thanks for the solution. I tried it, and actually for the controller there’s no need to create a new file

This works where the path does not include the content-type, which is not really combining the routes as the OP specifies but creating a separate route, is there a way to combine/extend routes like in v3?
E.g. path: "/the-content-types/my-custom-route" won’t work resulting in a 404 I guess because of the way createCoreRouter creates defined routes whereas path: "/foo/my-custom-route" or path: "/my-custom-route" will resolve. A feature where createCoreRouter could be passed additional routes could be nice

1 Like

createCoreRouter should have an option to add more routes in the same way as createCoreConroller has. Until then this may helps:

const { createCoreRouter } = require("@strapi/strapi").factories;
const defaultRouter = createCoreRouter("api::store.store");

const customRouter = (innerRouter, extraRoutes = []) => {
  let routes;
  return {
    get prefix() {
      return innerRouter.prefix;
    },
    get routes() {
      if (!routes) routes = innerRouter.routes.concat(extraRoutes);
      return routes;
    },
  };
};

const myExtraRoutes = [
  {
    method: "GET",
    path: "/stores/key",
    handler: "api::store.store.key",
  },
  {
    method: "GET",
    path: "/stores/moreStuff",
    handler: "api::store.store.moreStuff",
  },
];

module.exports = customRouter(defaultRouter, myExtraRoutes);

You can add customRouter to you own libs to reuse it.

8 Likes

Very useful, thanks! Just a follow-up question, how will I do the exact same thing but with the user api? That is, I want to add some extra routes to the user api. But I can’t seem to apply your answer because there’s no user folder under api, so there’s also no routes > user.js file where I could put your code snippet.

This was handy cheers, for anyone stumbling on this you’ll prob want to flip the concat around so your custom routes take precedence over the defaults

This was very interesting read. I was wondering if there is a way to use similar pattern to be able to override existing routes vs creating extra ones.

I looked in the docs, but was not able to find a good example of overriding a route, just a way to configure them.

So I tried this and it worked. Not the best implementation, just experimenting based on the above example.

"use strict";

/**
 * post router.
 */

const { createCoreRouter } = require("@strapi/strapi").factories;

const defaultRouter = createCoreRouter("api::post.post");

const customRouter = (innerRouter, routeOveride = [], extraRoutes = []) => {
  let routes;

  return {
    get prefix() {
      return innerRouter.prefix;
    },
    get routes() {
      if (!routes) routes = innerRouter.routes;

      const newRoutes = routes.map((route) => {
        let found = false;

        routeOveride.forEach((overide) => {
          if (
            route.handler === overide.handler &&
            route.method === overide.method
          ) {
            found = overide;
          }
        });

        return found || route;

      });
      
      return newRoutes.concat(extraRoutes);
    },
  };
};

const myExtraRoutes = [];

const myOverideRoute = [
  {
    method: "GET",
    path: "/posts/:slug",
    handler: "api::post.post.findOne",
  },
];

module.exports = customRouter(defaultRouter, myOverideRoute, myExtraRoutes);

any thoughts?

how can i call custom service inside custom controller