How to Create a Custom API Endpoint in Strapi?
Tags: javascript, api, strapi
Published: 2022-04-19 10:11:54

Strapi provides a set of powerful APIs to let the developers create custom API end-points. In this article, we will be creating a custom API endpoint from scratch.

## Prerequisites

Before you can jump into this content, you need to have the following:

1. Basic knowledge of JavaScript
2. Basic understanding of Strapi - [get started here](https://strapi.io/documentation/developer-docs/latest/getting-started/introduction.html).

## Installing Strapi

The following command will install a new Strapi instance with some ready-made configurations and data.

```bash
    npx create-strapi-app plugins-v4-1 --template corporate
```

Please, [check out the create-strapi-app node package page for learning about the different ways to install Strapi.](https://www.npmjs.com/package/create-strapi-app)

With `--template corporate` flag, you need to choose `Custom` as the `installation type`. Choose `sqlite` as the default database.

```
    ? Choose your installation type Custom (manual settings)
    ? Choose your default database client sqlite
    ? Filename: .tmp/data.db
```

Make sure that the project directory is the current directory in the terminal. Start the development server.

```
    cd plugins-v4-1/
    yarn develop --watch-admin
```

The `--watch-admin` flag starts the application with the autoReload enabled and the front-end development server. It allows developers to customize the administration panel.


![starting the application with auto-reload enabled](https://paper-attachments.dropbox.com/s_27799A4C5AE5EB6BE5512B43881D4F101A6ECBA3EFB7A80F19E9F08911D732AE_1645338106443_image.png)


Once the installation is complete, the browser automatically opens a new tab. If not, navigate to `http://localhost:8000/admin/`.

If port `8000` is already used by an another process, please check the alternate port mentioned mentioned in the CLI output. Most of the time, it would be `8080`.

Complete the form to create the first administrator user of this Strapi application.

![form to create the first strapi admin user](https://paper-attachments.dropbox.com/s_27799A4C5AE5EB6BE5512B43881D4F101A6ECBA3EFB7A80F19E9F08911D732AE_1645339274420_image.png)


The --watch-admin flag starts the application with the autoReload enabled and the front-end development server. It allows developers to customize the administration panel.

The corporate template generates components, collection types, and a single type out of the box.

Components:

- User
- Page
- Lead From submission

Single Types:

- Global

Components:

- A lot of components for Elements, Layout, Links, Meta, and Sections

The template also provides us with some pre-filled data to play around with. All of these happen just by providing the `--template corporate` flag at the time of Strapi installation.


## Setting-up a Use Case

We have a collection type `page`, pre-filled with some data.

![exploring the pre-filled data of collection type page](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645869241560_image.png)


On a get request to `http://localhost:1337/api/pages?populate=*`, Strapi returns all the `page`, populated with all of its components and dynamic-zone components data.


![default response of page](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645869546383_image.png)


The requirement is to create a custom API Endpoint `GET /api/pages-report` which will provide a simplified report of the pages.

```
    [
      { "id": 1, "name": "Home", "metaTitle": "Strapi corporate site starter" },
      { "id": 2, "name": "Pricingg", "metaTitle": "Pricing" },
      { "id": 3, "name": "Secret", "metaTitle": "Secret page" },
      { "id": 4, "name": "Contact", "metaTitle": "Contact" }
    ]
```

This is exactly what we will be building in this article.


## Generating a Basic API

The `npx strapi generate` command starts an interactive CLI session. In case you have `yarn` and `strapi` installed globally in your system, feel free to replace the `npx` command with `yarn`.

The following options may be selected. Please, notice that we have named our API `pages-report`.

![using the cli to generate api](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645876653699_image.png)


This command creates a directory called `pages-reports` within `src/api` directory. The `pages-report` contains three directories called `controllers`, `routes`, and `services`.


## Routes

Requests sent to Strapi on any URL are handled by routes. By default, Strapi generates routes for all the content-types (see [REST API documentation](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html)). [Routes can be added](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html#implementation) and configured. Once a route exists, reaching it executes some code handled by a controller.

Replace the code in `src/api/pages-report/routes/pages-report.js` with the following lines of code.

```
    module.exports = {
      routes: [
        {
         method: 'GET',
         path: '/pages-report',
         handler: 'pages-report.findAll',
         config: {
           policies: [],
           middlewares: [],
         },
        },
      ],
    };
```

## Controllers vs Services

Controllers are JavaScript files that contain a set of methods called actions, reached by the client according to the requested [route](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html). Whenever a client requests the route, the action performs the business logic code and sends back the [response](https://docs.strapi.io/developer-docs/latest/development/backend-customization/requests-responses.html#responses). Controllers represent the C in the model-view-controller (MVC) pattern. In most cases, the controllers will contain the bulk of a project's business logic. But as a controller's logic becomes more and more complicated, it's a good practice to use [services](https://docs.strapi.io/developer-docs/latest/development/backend-customization/services.html) to organise the code into re-usable parts.

Services are a set of reusable functions. They are particularly useful to respect the DRY (don’t repeat yourself) programming concept and to simplify [controllers](https://strapi.gitee.io/documentation/v3.x/concepts/controllers.html) logic.


## Creating a Service

Services are basically not aware of the Koa’s `ctx` object(request and the response). It is supposed to be a flexible and reusable function. 

Replace the contents of `/src/api/pages-report/services/pages-report.js` with the following lines of code:

```
    'use strict';
    
    module.exports = {
      pagesReport: async () => {
        try {
          // fetching the data
          // we dont really need contentSections for this example.
          // its kept here, just for your reference
          const entries = await strapi.entityService.findMany('api::page.page', {
            fields: ['id', 'shortName'],
            populate: {
              metadata: {
                fields: ['metaTitle']
              },
              contentSections: {
                populate: '*'
              }
            }
          });
    
          // reducing the data to a simple array
          let entriesReduced;
          if (entries && Array.isArray(entries)) {
            entriesReduced = entries.reduce((acc, item) => {
              acc = acc || [];
              console.log(acc);
              acc.push({
                id: item.id,
                name: item.shortName || '',
                metaTitle: item.metadata?.metaTitle || ''
              });
              return acc;
            }, [])
    
            // returning the reduced data
            return entriesReduced;
          }
        } catch (err) {
          return err;
        }
      }
    }
```

In this code we are using Strapi’s [Entity Service API](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service-api.html) to fetch all pages. To learn more about the flexible ways of populating the response, read the [Populating the Entity Service API](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service/populate.html#advanced-populating) documentation.

The `pagesReport` service will return the simplified (reduced) data: 

```
    [
      { "id": 1, "name": "Home", "metaTitle": "Strapi corporate site starter" },
      { "id": 2, "name": "Pricingg", "metaTitle": "Pricing" },
      { "id": 3, "name": "Secret", "metaTitle": "Secret page" },
      { "id": 4, "name": "Contact", "metaTitle": "Contact" }
    ]
```

Once a service is created, it's accessible from [controllers](https://docs.strapi.io/developer-docs/latest/development/backend-customization/controllers.html) or from other services:

```
    // access an API service
    strapi.service('api::apiName.serviceName');
    // access a plugin service
    strapi.service('plugin::pluginName.serviceName');
```

The list of services available can be logged from controllers and services:

```
    console.log('strapi.services ', strapi.services);
    console.log('pages-report', strapi.service('api::pages-report.pages-report'));
```


## Creating a Controller

Controllers are functions that have direct access to the Koa’s `ctx`, hence a controller function is responsible for invoking our service on `request` and returning the results to the `response`.

Replace the code in `src/api/pages-report/controllers/pages-report.js` with the following lines of code:

```
    'use strict';
    
    module.exports = {
      async findAll(ctx, next) {
        try {
          const data = await strapi.service('api::pages-report.pages-report').pagesReport();
          ctx.body = data;
        } catch (err) {
          ctx.badRequest('Page report controller error', { moreDetails: err })
        }
      }
    };
```


## Setting Permissions for the API

That’s all the code required for the use case. Send a get request to `/api/pages-report`. You will most probably get a 403 error.

![the custom route give a 403 error](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645876203458_image.png)


In order to be able to publicly access the end-point, please go to the Setting > Roles > Public in the Strapi dashboard. Please, check the final for the pages-report route and hit the save button.


![setting the api permissions](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645876512209_image.png)


Now, the custom API is ready for the public:

![the reduced response](https://paper-attachments.dropbox.com/s_436C2F7AAC691CFA749D3403FA4D95C08C382284E905990870C880FF7720CB2C_1645876041461_image.png)

## Conclusion

In this article, we used Strapi’s [Entity Service API](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service-api.html) to fetch and filter the results. It’s not the only way to accomplish this task. In Strapi, the fetching and filtering of results can be done using [Rest API, Query Engine API](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/query-engine-api.html), and [GraphQL API](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/graphql-api.html) as well.

The Entity Service is the layer that handles Strapi's complex data structures like [components](https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#components) and [dynamic zones](https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#dynamic-zones) and uses the Query Engine API under the hood to execute database queries. This makes it a reasonable choice for this use case. Our example can be made as flexible as we need using the [filtering](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service/filter.html#logical-operators) & [advanced](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service/populate.html#advanced-populating) population techniques of the Entity Service API. 

[The source code written for this article can be explored at the git commit.](https://github.com/vivmagarwal/strapi-adventures/pull/1/commits/afdb2dd322cabbcd95a537ac54e6360ea3ded443) Let me know if you have any suggestions and what you will be building with the knowledge.