Using the Strapi API Endpoints

Overview

Now that we know how to create content-types and content using the Strapi admin panel, it’s time we start using those content types in our frontend so our users can interact with our application. In this lesson, we’ll first learn about automatically generated endpoints and the permissions required to access those endpoints.

Automatically generated endpoints

Whenever we create a collection type in our Content-type Builder, Strapi automatically generates a folder in our source code with the name of that collection. This folder is created in the /api folder—which is already present in the code—and it contains four subfolders: content-types, routes, controllers, and services. Let’s get into some more details about the content and purpose of these folders.

Please use the following widget to locate the files that we’ll discuss in this lesson:

'use strict';

module.exports = {
  /**
   * An asynchronous register function that runs before
   * your application is initialized.
   *
   * This gives you an opportunity to extend the code.
   */
  register(/*{ strapi }*/) {},

  /**
   * An asynchronous bootstrap function that runs before
   * your application gets started.
   *
   * This gives you an opportunity to set up your data model,
   * run jobs, or perform some special logic.
   */
  bootstrap(/*{ strapi }*/) {},
};
The Strapi application for our Recipes application

The content-types folder

This folder contains the schema of the content-type in a .json file. This is all the information Strapi requires about the content that will be saved in this content-type. The file is at the path: /src/api/content-types/recipe/schema.json.

Once the content-type has been created, we can also make edits to our schema from this file without having to use the Strapi admin panel. This is what a schema.json file looks like:

Press + to interact
{
"kind": "collectionType",
"collectionName": "recipes",
"info": {
"singularName": "recipe",
"pluralName": "recipes",
"displayName": "Recipe"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"strMeal": {
"type": "string"
},
"strThumb": {
"type": "string"
},
"strArea": {
"type": "string"
},
"strCategory": {
"type": "string"
},
"strInstructions": {
"type": "text"
},
"strTags": {
"type": "string"
},
"strIngredient1": {
"type": "string"
},
"strIngredient2": {
"type": "string"
},
"strIngredient3": {
"type": "string"
},
"strIngredient4": {
"type": "string"
},
"strIngredient5": {
"type": "string"
},
"strIngredient6": {
"type": "string"
},
"strIngredient7": {
"type": "string"
},
"strIngredient8": {
"type": "string"
},
"strIngredient9": {
"type": "string"
},
"strIngredient10": {
"type": "string"
},
"strIngredient11": {
"type": "string"
},
"strIngredient12": {
"type": "string"
},
"strIngredient13": {
"type": "string"
},
"strIngredient14": {
"type": "string"
},
"strIngredient15": {
"type": "string"
},
"strIngredient16": {
"type": "string"
},
"strIngredient17": {
"type": "string"
},
"strIngredient18": {
"type": "string"
},
"strIngredient19": {
"type": "string"
},
"strIngredient20": {
"type": "string"
},
"strMeasure1": {
"type": "string"
},
"strMeasure2": {
"type": "string"
},
"strMeasure3": {
"type": "string"
},
"strMeasure4": {
"type": "string"
},
"strMeasure5": {
"type": "string"
},
"strMeasure6": {
"type": "string"
},
"strMeasure7": {
"type": "string"
},
"strMeasure8": {
"type": "string"
},
"strMeasure9": {
"type": "string"
},
"strMeasure10": {
"type": "string"
},
"strMeasure11": {
"type": "string"
},
"strMeasure12": {
"type": "string"
},
"strMeasure13": {
"type": "string"
},
"strMeasure14": {
"type": "string"
},
"strMeasure15": {
"type": "string"
},
"strMeasure16": {
"type": "string"
},
"strMeasure17": {
"type": "string"
},
"strMeasure18": {
"type": "string"
},
"strMeasure19": {
"type": "string"
},
"strMeasure20": {
"type": "string"
},
"idMeal": {
"type": "integer",
"unique": true,
"required": true
}
}
}

The routes folder

This folder contains all the route files. By default, it only has one file, :singularApiId.js. This is what the file looks like:

Press + to interact
'use strict';
/**
* recipe router
*/
const { createCoreRouter } = require('@strapi/strapi').factories;
module.exports = createCoreRouter('api::recipe.recipe');

In line 7 of the file, Strapi uses the factories function of its package to create the core routes. With this function, we can pass configuration options to the default routes. We can also disable routes in case we need to use custom routes instead of the core ones.

In line 9, they are exported so we can use them. The createCoreRouter is returned from the factories method and contains the default routes. Let’s take a quick look at the core routes of the API:

  • find: This is a GET route with the path /api/:pluralApiId. It gets all the entries of the collection.

  • findOne: This is also a GET route, but its path is /api/:pluralApiId/:id. It gets the entry that has the ID mentioned in the route. For example, a request to api/tests/1 returns the entry with id=1 or null (if the entry doesn’t exist).

  • create: This is a POST route with the path /api/:pluralApiId. It creates a new entry in the collection.

  • update: This is a PUT route with the path /api/:pluralApiId/:id. It updates the entry that has the ID mentioned in the route.

  • delete: This is a DELETE route with the path /api/:pluralApiId/:id. It deletes the entry that has the ID mentioned in the route.

These are the five basic REST API routes that the Strapi application creates. We can disable these basic routes as well as add custom ones in order to create our custom functionality.

The controllers folder

This folder contains our controller file named :singularApiId.js. It looks very similar to the route files, with the one difference being that it uses createCoreController function and not the createCoreRouter function. This is what the file looks like:

Press + to interact
'use strict';
/**
* recipe controller
*/
const { createCoreController } = require('@strapi/strapi').factories;
module.exports = createCoreController('api::recipe.recipe');

This file contains the set of functions that are called when a route is accessed. Each route is assigned to a controller function, which are called actions. The logic that the route is meant to perform for the user is present here. The action is also responsible for generating an appropriate response for the route.

The createCoreController function automatically generates actions for the automatically generated routes. It also allows us to create new controllers as well as extend or replace the default-generated controllers.

The services folder

The purpose of the services folder is to help developers have an easy-to-maintain codebase. The /services/:singularApiId file is home to reusable functions that are being used in controllers. The file looks like this:

Press + to interact
'use strict';
/**
* recipe service
*/
const { createCoreService } = require('@strapi/strapi').factories;
module.exports = createCoreService('api::recipe.recipe');

The createCoreService function contains all the services that are used by the created controllers.

Now that we know about the default API endpoints that Strapi automatically generates for us, we can easily use them in our applications. However, we also need to allow access to these endpoints.

Allowing access to endpoints

The access rights to the API can be changed from the “Settings” panel of the Strapi Dashboard. The “Settings” can be accessed from the main navigation bar on the left side of the admin panel screen. Here is how we can change access to the application’s API:

  • Click the “Settings” button at the bottom of the navigation menu.

  • Click the “Roles” option under the “Users and Permissions Plugin” section in the subnavigation of “Settings.”

  • Click the the “Edit” icon of the “Public” or the “Authenticated” role, depending on the requirements.

  • Select all the routes that are required for the application.

With these steps, our application’s endpoints will become accessible to the user. We can test out the find and findOne routes in a browser because they’re GET methods.

Creating custom routes

First, let’s discuss why we need custom routes in our application. The frontend of the application that we have created in the previous section uses TheMealDB API. That API uses the idMeal field as its unique identifier, and we need to do the same here. Let’s use the code widget below to discuss how to do that:

Note: A Strapi administrator user has already been created for this course. The details of the user are as follows:

  • Email: jane.doe@email.com

  • Password: Password123

'use strict';

module.exports = {
  /**
   * An asynchronous register function that runs before
   * your application is initialized.
   *
   * This gives you an opportunity to extend code.
   */
  register(/*{ strapi }*/) {},

  /**
   * An asynchronous bootstrap function that runs before
   * your application gets started.
   *
   * This gives you an opportunity to set up your data model,
   * run jobs, or perform some special logic.
   */
  bootstrap(/*{ strapi }*/) {},
};
The Strapi application for our Meals app

In the code above, we’re making some changes to make our application compatible with TheMealDB API that we’re using in our application. Let’s take a look at those files:

  • The /src/api/recipe/routes/custom.js file: Here, we create a custom route for our Recipe content-type. We require this because our TheMealDB API uses idMeal as the unique identifier, but Strapi defaults to the id field; therefore, we need to override the id field with the idMeal field. This route will only be used to find meals using the idMeal field, not the id field.

  • The /src/api/recipe/controllers/recipe.js file: This field is home to our default controllers, but we can also override the findOne controller to make idMeal our unique identifier. We create a new findOne function in this file, which will be the handler for our /recipe/:idMeal route that we created in the /src/api/recipe/routes/custom.js file.