DEV Community

The Creator
The Creator

Posted on

Strapi v4 - Extend core controller and create custom routes

Contents

Extend your controller in strapi v4

Hello everyone! I will be showing you how to extend your controllers and routes in strapi v4.

Get Started

Let's create a project first of all. I think that if you want to customize the api, you probably know how to set up a strapi enviroment so I am going to skip this part.

// npm
npx create-strapi-app@latest my-project --quickstart

// yarn
yarn create strapi-app my-project --quickstart
Enter fullscreen mode Exit fullscreen mode

Awesome after the initial setup we can quickly create a collection type called Hello, You can name this anything you want.

Strapi 01

Now let's add a text field called title

Strapi 02

Strapi 03

Click finish, save your collection type and wait for the strapi app to restart.

Now we should have the following collection type:

Strapi 04

Customizing the api

Alright let's get to work! Just kidding, it's really simple. follow the folder structure:

my-project > src > api > hello

Here we have 4 folders available.

  • [content-types, controllers, routes, services] Open up the javascript files inside the controllers and routes folders. They should both have the same name as your api. In my case Hello.js

Inside the controllers > hello.js replace the code with the following:

// src/api/hello/controllers/hello.js

"use strict";

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

module.exports = createCoreController("api::hello.hello", ({ strapi }) => ({
  async findAll(ctx) {
    const entries = await strapi.db.query("api::hello.hello").findMany();

    ctx.body = entries;
  },
}));
Enter fullscreen mode Exit fullscreen mode

Here we are extending the core controller to accept an extra function called findAll. Of course the core controller already has such a method called find. This is just for simplicity sake.

Now let's move to the routes folder and create a new file called custom-hello.js or something...

Inside we are going to define the routes: method, path and handler.

// src/api/hello/routes/custom-hello.js

module.exports = {
  routes: [
    {
      method: "GET",
      path: "/all-hellos",
      handler: "hello.findAll",
    },
  ],
};

Enter fullscreen mode Exit fullscreen mode

And.. That's it! Now if we start our strapi app we can go to settings > roles > public > hello* and there we will see our new route!

Strapi 05

Check findAll then save the changes. Create some entries inside the hellos collection and go to http://localhost:1337/api/all-hellos to view your collection! 🎉

Response api/hello

Nice! super easy eh?

Conclusion

Customizing your api may seem like a hassle and in some cases unneccesary. But I think that everyone will, at one point run into an issue where something is simply not possible with the core api.

For example, specifically updating the stock of a product by id without passing too many parameters.

// Addition to controlers/apiname.js

async updateStock(ctx) {
  const { id } = ctx.params;
  const { stock } = ctx.request.query;

  const entries = await strapi.db.query("api::product.product").update({
    where: { id },
    data: {
      stock,
    },
  });

  ctx.body = entries;
},
Enter fullscreen mode Exit fullscreen mode

and for the routes

// Addition to the custom routes file

{
  method: "PUT",
  path: "/update-stock/:id",
  handler: "product.updateStock",
},
Enter fullscreen mode Exit fullscreen mode

Final PUT route http://localhost:1337/api/update-stock/2?stock=5

In this example I update the stock field of a product inside the product collection. Sometimes you want some extra control for specific tasks and extending the core controller / routes will help you with this.

Thank you for reading and Happy Coding 🥷‎‍💻

Top comments (3)

Collapse
 
sambhal profile image
Suhail Akhtar

Thanks, It really helped understanding the custom API creation in Strapi. I was struggling with it but your post helped me in getting started.

Collapse
 
dsfaccini profile image
David

Hey m'dude, thanks for the post. I couldn't get it to work though, get following error:
Error creating endpoint GET /products/:slug: Cannot read property 'findSlug' of undefined
TypeError: Error creating endpoint GET /products/:slug: Cannot read property 'findSlug' of undefined
There apparently is a workaround for registering routes (haven't tried it) here forum.strapi.io/t/how-to-add-custo...
I just found it hacky and was hoping your way would work.

It does beg the question though:
The core controller takes the next parameter, say /hello/1, it infers the 1 is the id.
If I now register /hello/:name as a custom route, how's the controller gonna know whether that's the core or the custom route? It's gonna fail on core because name won't be a number but even in the best of cases it'd have to try both, which is inefficient (and I'm sure the logic isn't build like that)
Could you maybe point to the source of your solution? It really seems like something is missing in the core routes file

Regards!

Collapse
 
gangzz profile image
gangzz

It's works for me, Thanks!