kapsys-logo
Contact Us
Back

Custom Controllers and Services in Strapi: Enhancing the Default Behavior

February 21, 2024 by Daria Andrieieva
  • User Experience
custom controllers

Strapi, the open-source headless CMS, provides developers with a powerful platform for creating and managing content. However, you must often go beyond its default behavior to truly harness its capabilities and create tailored solutions. This is where custom controllers and services in Strapi come into play.

Custom controllers and services enable you to extend Strapi's functionality, creating specific endpoints, integrating external services, and executing complex business logic beyond the out-of-the-box capabilities. 

 

Join Kapsys as we explore the world of custom controllers and services in Strapi, providing step-by-step guidance and shedding light on their significance.

 

Sign up to our blog to stay tuned about the latest industry news.

Why Choose Custom Controllers and Services in Strapi?

Before we jump into the how, let's address the why. Why would you want to use custom controllers and services in Strapi?

Tailored endpoints

Strapi comes with a set of predefined REST and GraphQL endpoints. While these are great for general use cases, custom endpoints are often necessary for specific requirements. Custom controllers allow you to create tailored endpoints that fit your application like a glove.
 

Read: Strapi's GraphQL Plugin: Setting Up And Querying Data With GraphQL

Enhanced business logic

Sometimes, your project requires complex business logic beyond what Strapi offers by default. Custom services allow you to implement and execute this logic, ensuring your application behaves strictly as intended.

Integration with external services

If your application needs to interact with third-party APIs or services, custom controllers and services are your gateway to seamless integration. You can handle external requests and responses effortlessly, making your application more powerful and versatile.


Now that we've covered the why let's understand the how. How can you create custom controllers and services in Strapi to enhance your project?

Read: What Is Strapi, And Why Is It A Leading Headless CMS?

Prerequisites

To create custom controllers in Strapi, you'll need the following prerequisites:

  • Strapi Installed: Ensure Strapi is installed on your system.

  • Strapi Project: Have a Strapi project set up.

  • JavaScript Knowledge: Basic understanding of JavaScript.

  • Text Editor: A code editor for writing custom controller files.

  • CLI Skills: Comfort with the command-line interface.

  • Database Configuration: Set up your project's database.

These essentials will enable you to work with custom controllers in Strapi effectively.

customized controllers

Creating Custom Controllers

Let's break the process of creating custom controllers into manageable steps: 

Step 1: Understand the need

Before creating custom controllers, you should clearly understand your project's requirements. What endpoints do you need, and what data should they return or receive? This is the foundational step that will guide the entire process.

Step 2: Generate a controller

In Strapi, creating custom controllers is a breeze. The first thing you need to do is generate a new controller. This can be achieved using the following command:
 

strapi generate:controller <controllerName>


Replace <controllerName> with the name you want for your custom controller. This command will create a new controller file in the /api/<modelName>/controllers directory of your Strapi project.

Step 3: Define Controller Actions

Your custom controllers will have several actions, each corresponding to a different endpoint. These actions are JavaScript functions that define the behavior of the endpoints. Here's an example of a simple controller action:
 

module.exports = {
  find: async (ctx) => {
    // Implement your custom logic here
  },
};

Step 4: Map routes

To make your custom controllers accessible via HTTP, you must map routes to your controller actions. This is done in the /config/routes.json file. Here's an example of how to do it:
 

{
  "routes": [
    {
      "method": "GET",
      "path": "/custom-endpoint",
      "handler": "custom.find"
    }
  ]
}


In this example, a GET request to /custom-endpoint will trigger the find action in your custom controller.

Step 5: Implement your custom logic

It's time to implement your custom logic within the custom controllers actions. You can fetch data from the database, manipulate it, and return the desired response.

Step 6: Test your custom controller

Testing is crucial to ensure your custom controller works as expected. Use tools like Postman or the built-in Strapi Swagger interface to test your custom endpoints.


Now, you can create effective custom controllers by implementing these easy steps. In the next section, let's discuss forming a custom service. 

documentation controller

Creating a Custom Service

Custom services in Strapi are equally crucial as custom controllers, especially when implementing complex business logic that doesn't belong in the controller.

Step 1: Generate a service

Like with custom controllers, to create a custom service, you can use the following command:
 

strapi generate:service <serviceName>


Replace <serviceName> with the name you prefer for your service. This will create a service file in the /api/<modelName>/services directory.

Step 2: Define service functions

Your service file will contain JavaScript functions that encapsulate the custom logic you want to implement. Here's an example of a simple service function:
 

module.exports = {
  customLogic: async () => {
    // Implement your custom business logic here
  },
};

Step 3: Use your custom service

You can use your custom service in custom controller actions or anywhere within your Strapi application. To do this, simply import your service and call its functions. For example:
 

const customService = require("../../services/customService");

// Inside a controller action
module.exports = {
  find: async (ctx) => {
    const result = await customService.customLogic();
    // Use the result in your controller action
  },
};

Step 4: Test your custom service

Just like with custom controllers, it's essential to thoroughly test your custom service to ensure it behaves as expected.
 

You can implement these steps individually to create a custom service that works effectively with your custom controllers. 


Read: How to Optimize Performance of Strapi-Powered Websites and Applications

Documenting Your Custom Controllers and Services

Creating custom controllers and services is just one part of the process. Proper documentation is equally important, especially when you're working in a team or planning to share your Strapi project with others.

1. Use JSDoc comments

Adding JSDoc comments to your controller and service functions is a good practice. These comments provide precise descriptions of your tasks and their parameters. Here's an example:
 

/**
 * Custom logic for fetching and processing data.
 *
 * @returns {Promise<any>} The result of the custom logic.
 */
customLogic: async () => {
  // Implement your custom business logic here
},

2. Update the documentation controller

Strapi provides a built-in documentation controller to help you create and manage API documentation. You can enhance this controller to include your custom endpoints, controller actions, and service functions. This makes it easy for your team to access and understand your code and custom controllers.

3. Include examples

Adding examples to your documentation can be incredibly helpful. You can provide usage examples for your custom endpoints, controller actions, and service functions, making it easier for other developers to implement and integrate your custom code into their projects.
 

Documenting your custom controllers is the key to ensuring that your team and other developers can seamlessly understand, implement, and harness the full potential of your Strapi project's custom functionalities.

Troubleshooting Your Custom Controllers and Services

While creating custom controllers and services in Strapi can significantly enhance your project, you must be prepared for potential challenges and issues during development. 


Here, we'll discuss common troubleshooting steps and best practices to ensure your custom code functions smoothly:

Debugging and logging

Problem: Unexpected behavior or errors in your custom controllers and services.


Solution: Utilize debugging and logging techniques to identify the root cause of issues. You can add console.log statements in your custom controllers and service functions to inspect variable values and execution flow. Additionally, Strapi provides a system that allows you to log specific events and errors, making tracking and troubleshooting issues more accessible.
 

// Example of logging in a controller action
module.exports = {
  find: async (ctx) => {
    console.log('Entering the find action');
    // Your custom logic
  },
};

Validate input data

Problem: Incorrect or unexpected data causing errors.
 

Solution: Always validate and sanitize input data to prevent unexpected behavior. Use data validation libraries or built-in Strapi functions to ensure that the data you receive and process in your custom controllers or service functions adheres to your application's requirements.
 

// Example of input validation in a controller action
module.exports = {
  create: async (ctx) => {
    const { data } = ctx.request.body;
    
    // Validate data
    if (!data || !data.name) {
      return ctx.badRequest('Name is required.');
    }

    // Your custom logic
  },
};

Review dependencies

Problem: Compatibility issues with the Strapi version or third-party packages.
 

Solution: Make sure your custom controllers and services are compatible with the version of Strapi you are using. Verify that any third-party packages you rely on are up-to-date and consistent with your Strapi version. Sometimes, conflicts or deprecated methods can lead to unexpected issues.

Handle errors gracefully

Problem: Unhandled errors leading to application crashes or unexpected behavior.
 

Solution: Implement error handling to ensure that your application gracefully handles errors and provides informative responses to clients. Use try-catch blocks to catch exceptions and return meaningful error messages. Strapi also provides built-in error response functions like ctx.badRequest() and ctx.notFound().
 

// Example of error handling in a controller action
module.exports = {
  find: async (ctx) => {
    try {
      // Your custom logic
    } catch (error) {
      console.error(error);
      ctx.badRequest('An error occurred while processing your request.');
    }
  },
};


It's essential to adhere to best practices if you want to guarantee the seamless operation and enhancement of your Strapi-powered application with custom controllers,


Remember that comprehensive testing and documentation remain pivotal for a successful development journey.

make a custom controller

Conclusion

Custom controllers and services in Strapi open up a world of possibilities. They allow you to tailor your Strapi project to meet your needs, whether creating custom endpoints, implementing complex business logic, or integrating external services. 

Following the steps outlined in this post and documenting your custom code effectively, you can create a more robust and user-friendly Strapi application.

Keep up with Kapsys to take your coding to the next level!