Guide to Building Rest APIs with Strapi

Are you a backend developer looking for a fast and easy way to set up a Node API? Or are you a frontend developer who wants to create and manage a backend using a user-friendly interface instead of coding it from scratch? Look no further, because the Strapi headless content management system is the perfect solution for your needs. With its flexible, framework-agnostic approach, Strapi allows you to easily create, manage, and deploy your backend without being tied to a specific frontend technology.

Whether you're a seasoned developer or a beginner, Strapi makes it easy to get your API up and running in no time.

In this guide, you will begin from scratch (that is, from an empty project directory), and we'll take you through all the steps needed to use Strapi to build a backend for a tech blog website. The API will expose endpoints to make it possible for the website to deliver a blog with tags and posts, as well as a list of courses.

Because we will be focusing on Strapi in this guide, we will not be building any front-end user interface. However, in order to create our backend features, we will often find ourselves thinking from the perspective of a frontend developer; that is, what we want to expose for the frontend developers to make their tasks easier.

Introduction to Strapi

Let us start this guide with an introduction to Strapi.

Strapi was developed as an open-source headless Content Management System (CMS) that provides common backend features out of the box while being totally extensible. It is free to use as a self-hosted application with a community edition that has some limitations but is well-suited for solo developers (especially frontend developers) or small businesses.

Note: "Headless", in this case, means that there is no "presentation" or front-end component, and only the back-end services exist. A headless CMS simply exposes a backend system with endpoints to serve the content, which is agnostic to the front-end, allowing you more flexibility in the front-end.

As a headless CMS, Strapi is an application backend that is not tied to a specific frontend technology. This flexibility allows for greater freedom in choosing the technology that best fits your project. This is a key difference between Strapi and traditional Content Management Systems such as WordPress, which often combine the backend and frontend in a tightly integrated manner, making it difficult to separate or distinguish between the two. However, recent developments in WordPress have allowed for the use of APIs separately.

Headless CMSs are getting more and more popular in modern technologies, so much so that even WordPress and other classical Content Management Systems are moving towards supporting the headless approach.

Out-of-The-Box Features of Strapi

Some out-of-the-box backend application features that Strapi provides for free as a headless CMS include the following:

  • First of all, it has an admin panel that is a very simple React Application that supports user roles, can be easily customized, and even deployed separately from the server application, if need be.

  • The Strapi headless Content Management System makes it possible for developers to create data models from scratch (that are called "content types"). Unlike WordPress, there are no predefined data models which you have to tweak according to your application needs. Rather, with Strapi, you have the possibility to use the provided Content-Type editor in the admin panel to easily shape your application data model based on the application logic that is needed to meet your application's use-case.

  • It automatically generates all CRUD (Create, Read, Update, Delete) functions and a fully-fledged REST / GraphQL API, if you wish to activate it.

  • It has an authorization system for any content type that you design for your application. This means that you can granularly control which content types are public and which are not, and which are used for different kinds of operations.

  • It has a user authentication system that you can leverage on your frontend application, which comes with a fully-fledged media server for uploading photos, documents, and various resources that you can upload on any other backend application.

  • It includes an email server for transactional email sending.

  • Strapi also makes it possible for internationalization, for which your application can have data types that support multiple languages.

  • Strapi is easily deployable with various options for the same, and also supports Docker. It is worth mentioning that since Strapi is based on Node.js, it is basically deployable on any Node.js runtime environment.

  • Along with all the aforementioned rich and powerful features that we have seen above, Strapi is also very extensible. It has a strong plugin API to extend the application logic and there is also the Strapi Market, which is the official plugin marketplace. As a developer, you can also choose to manually extend the core codebase by manually editing it.

Creating the Strapi Project

Now that we have a better idea of what Strapi does and offers, we can move on to initializing the Strapi application.

Be sure to check your Node version to ensure that you are running a supported version for using Strapi on your local machine. You can find the system requirements on the official website.

In order to create a new project with Strapi, run the following command in your terminal:

$ npx create-strapi-app@latest my-strapi-blog --quickstart

or

$ yarn create strapi-app my-strapi-blog --quickstart

You can choose to modify the name of your project with whatever name you want. In my case, I have called the name of my own project my-strapi-blog.

Creating a Strapi project with the --quickstart flag sets your project to make use of a SQLite database. There are other databases and installation options that you can find here.

Now, all needed application packages and dependencies are installed and the application has been successfully bootstrapped for use. The newly created Strapi application has started both the development server and admin panel, which are on localhost:1337 and localhost:1337/admin respectively.

With that, the next thing that we should go ahead to do is to register our first admin user by filling out the simple form that has been provided on localhost:1337/admin/auth/register-admin. Then click on Let's Start.

With the "Let's Start" button clicked, we are now navigated into the Strapi dashboard.

Building the Content Types

We will now start to build out our first content type for our application. It will be a collection type and it is for the posts.

If during this tutorial your development server stops at any point, you can restart it by running yarn develop to start both the server and the admin panel.

To get started, head to the "Content-Type Builder" under "Plugins" on the admin dashboard. You would notice that there are sections for "Collection Types" and "Single Types".

We already have one collection type by default - User. Now, we would create a new collection type with a display name "Post". Notice that the API IDs are automatically generated.

In the "Advanced Settings", you can decide the status of the Collection type to either being a draft or being published, and you can also enable localization, in case you may want to have content in multiple languages.

Next, click the "Continue" button.

Here, you are prompted to select a field for the Post collection type - this is where we design the data structure for a post in our blog. Let us design the data structure in the following way:

  • title with data type "Text" (required).
  • content with data type "Rich Text" (required).
  • coverImage with data type "Media" (Single image) and also (required).
  • slug with data type "UID" (required) with "Attached Title" (title)

By default, every post we create gets a UID, so we don't have to explicitly add that to the list of data types for the Post collection. Slugs are unique for each post and set on the URL of each post, for that reason we set its data type to UID.

When creating slugs, you will come across the Attached Field. This is a possibility that Strapi brings to enable the UID to be auto-generated from another field, and that's exactly what we want for our slugs - we want them to be generated automatically from the post title.

Note: The required setting can be applied in the "Advanced Settings" tab when adding a field. We have set all the fields to "required" because we want to make sure that our posts must have all these fields in place before they can be created. The maximum and minimum length for a field can also be set, if you wish.

Now, we have created the Post collection type with four fields - title, content, coverImage, and slug.

Hit the "Finish" button, then "Save"! The first custom collection type for our tech blog is now created, and you have successfully designed the schema for a blog post on the website.

Understanding Relations in Strapi

Our Post collection type is not yet complete, but this is where Relations in Strapi come in. Suppose you wish to establish a connection between a post and another post. The ideal way this can be achieved is by way of having tags as part of the Post collection type.

Let us go ahead to create a new collection type with a display name Tag.

Next, add a new field, name, with data type Text (required and unique), and a UID field, slug, which is attached to the name and set to "required".

Hit "Finish" and "Save"!

Now that we have created our second collection type - Tag, we should now add it to the Post collection type to establish a relationship between related posts. To achieve this, we will be adding two new fields to the Post collection type - tags and author.

To add the author, we select the relation field type. At this point, we should be presented with the UI shown below:

In the "Relation" field UI, we have content types involved. On the left is the current content type, which in our case is the post, and on the right is the related content type, which is always a collection type, in our case this would be the User, specifically the admin panel users - User (from: admin).

Set the name of the field from within the current content type to author.

Next, we set the type of relationship that should exist between the two content types to a one-to-many relationship, so that posts can have multiple authors.

Next, we establish the relationship that pertains to tags, by clicking the "Add another field" button, then set both the current tag and the related content type to tag and Tag respectively. Finally, we set the relationship to be a one-to-many relationship, so that a post can possibly have more than one tag.

Click the "Finish" button when you are done.

Now the schema for your Post collection type should look like this:

Creating Entries - Populating the Database

In order to populate the database with posts, we have to act as content editors, by heading to the "Content Manager" section of the admin dashboard. Therein, we first create a couple of tags, by clicking Tag from the list of collection types, then clicking the "Create new entry" button. Feel free to create as many tags as you would like.

In this demo, I would create two tags named "strapi" and "react". Make sure to click on the regenerate icon on the slug field to generate a slug for each of the tags before saving and publishing them.

Next, switch to the Post section from the list of collection types on the sidebar in order to create posts. In order to create a new post, click the "Create new entry" button then populate all the fields which you are presented with on the UI with data for the post that you wish to publish.

While filling the fields, you would notice that the slug is a URL-friendly string and there is a periodic check for the availability of the slug name we enter (the slug name can also be auto-generated if you don't wish to manually edit it). This is because we had set the slug name to be unique, while we were designing the data schema earlier in our project.

Another helpful thing is that for the content field, we are able to preview what the formatting of the markdown editor looks like, to allow us to format our content in a way that we fully wish to.

I have also selected the "react" option from the list of tags for the post.

Now, you can hit the "Save" and "Publish" buttons when you are satisfied with the content of your post.

Building the Content Types for Services and Courses

On our website, we do not only want to display tech blog posts, but we also wish to display the various services that we render, along with various tech courses that can be explored while on the website. For these, we definitely need to add a services content type and a courses content type.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Switch to the "Content-Type Builder" section of the admin dashboard and then create a new collection type named "Service". Let us build the Service collection type, in a way that it has the following data fields:

  • name with data type "Text" (required and unique).
  • description with data type "Rich Text".
  • slug with data type "UID" (attached to the name).
  • coverImage with data type "Media" (required).

Hit the "Finish" and "Save" buttons to complete.

Similarly, let us create another collection type with a display name "Course" that has the following data fields:

  • title with data type "Text" (required and Unique).
  • description with data type "Rich Text".
  • slug with data type "UID" (attached to the name).
  • coverImage with data type "Media" (required).

There is also a need for us to establish a relationship between the tags and a course, and we do that by creating a one-to-many relationship between courses and tags.

Before we move on, make sure to try out yourself to create a course entry in the database. In a later demo within this guide, we will make use of the course entry you create now.

Ta-da! We have now successfully learned how to create various content types that design our database schema.

Before we learn how to implement permissions, authentication, and authorization using Strapi, let's look at the awesome stuff Strapi has done for us behind the scenes; both in terms of code and database entries.

In the src folder of the Strapi project, you will find a folder that holds the default admin panel content. Inside this folder, there is also an api folder, which contains all the content types that have been created in the application. Each content type has its own folder, which includes the definitions for its schema, routes, services, and controllers. In addition to the src folder, there is also a config folder, where various aspects of the application can be configured. For example, the database.js file contains all the configurations related to the database, which can be modified when deploying the application.

Permissions, Authentication, and Authorization using Strapi

At this point where we have created all the content types we need for our application, we are now ready to start using the REST APIs that make it possible for us to access content types through the automatically-generated endpoints.

In Strapi, an endpoint can be accessed through the plural of the collection type name, prefixed by /api/. Thus, if we wish to make a GET request to the Post collection type, which ID/slug of post on the development server, the request URL would be: localhost:1337/api/posts.

When making this request, we find that the response is forbidden. That is because, by default, Strapi has a permission system that prevents access to any operation on the application's data. You would have to explicitly permit whatever operation you wish to be publicly available in the application.

To fix this, we would work with the "Users and Permissions" plugin in Strapi, which is pretty intuitive when it comes to controlling the operations that external applications and users can make within an application's data.

In the admin dashboard, head to "Settings", then under the "Users and Permissions Plugin" section, click "Roles". There, you would see that by default there are two roles: a public role (which is the default role that is given to an unauthenticated user), and an authenticated role (which is the default role that is given to an authenticated user).

With regard to our app, we’ll set the list of posts to be visible to every user (authenticated and unauthenticated). We do that by clicking the edit icon on the public role row, and in the resulting UI that is displayed, you should see that there is a list of all the content types within the application along with other features, which came by default in the application.

From the list of content types, toggle the one for Post to view all the options which can enable us to define operations that can be performed on the posts endpoints by a public user. For now, let's enable the operations for finding all posts and finding one post.

Let's also define the same operations for the Tag content type. Then hit the "Save" button.

Now, if you try to make the API call for fetching all posts, it should yield a successful response this time. It should respond with data that contains an array of posts with its auto-generated ID's and other attributes the post possesses:

We have seen how public access to a resource on Strapi can be granted. Next, we will have a look at how the same can be implemented for authenticated users. In order to demonstrate this, let us make the assumption on our application that we only want the registered users to be able to view courses on the blog. For that, head into the Authenticated Roles screen, and in the Course content type, we'll enable operations for finding all courses and finding one course. Then click "Save".

Strapi has a built-in authentication system that allows for user registration and authentication within the application. Users can register using email and password, or through third-party authentication providers (which can be viewed by clicking on the "Providers" option under the "Users & Permissions Plugin" section). The Strapi authorization system is based on JWT (JavaScript Web Token), a widely used authorization method. Once a user registers in the application, the registration API endpoint sends an authentication token if the sign-up is successful. In subsequent requests to the API from the registered user, the token is included as a header. Each time a request is made to the server, the token is checked for validity, and access to a resource is granted or denied based on the outcome of this check.

Performing User Operations through the APIs

Let's now go hands-on with Strapi and see how the authentication process works using Postman.

First, we'll try to view all of the courses as authenticated users by calling the localhost:1337/api/courses endpoint.

As expected, it does not work and the request is forbidden, because viewing the courses’ endpoint is an operation that is only allowed for authenticated users.

Let's now perform authentication.

First, we create a new user entry for the User content-type. We can register a new user via the localhost:1337/api/auth/local/register endpoint, which must have some information about the user sent as raw JSON. The information includes the email, password, and username. Recall that we are using the email and password authentication provider. When we make this request, we receive a response with two keys: a JWT token and an object that holds the user details. It is this token that is used to separate an authenticated user from an unauthenticated user.

Now that we have successfully created a user, we can use the user's JWT token to make an authenticated request to the course endpoint in a bid to get a successful response, unlike before.

To achieve this using Postman, head over to the "Headers" section and add the following, as shown below:

The key is Authorization and the syntax for the value is Bearer {JWT token}. We do it this way because it's the same way the request would be made when working with an actual frontend.

Now, when the request is made with the Header being set, we get a successful response, and the user is able to view all the courses without restrictions:

Working with REST APIs

In this section, we will learn how to perform CRUD (Create, Read, Update, and Delete) operations using Strapi. We would also make use of the posts endpoint, which does not require an authenticated user to access it for this demo. However, the same process for performing CRUD operations on unauthenticated resources is the same for authenticated resources, aside from the auth token.

Before we get started, head to the Roles sections of the admin panel to edit the roles for a public user. Here, update the operations that can be performed on the posts endpoints by a public user, aside from finding all posts and finding one post, which we had set earlier:

We have already seen how to perform GET operations, so now let's have a look at how other operations can be performed:

Performing a "create" operation: The URL for performing an HTTP POST request in Strapi is /api/:pluralApiId. So to create a new post, we simply send an HTTP POST request to the /api/posts/ endpoints and pass the data for the new post we wish to create.

Performing an "update" operation: The URL for performing a HTTP PUT request in a Strapi application is /api/:pluralApiId/:documentId. In the same way, we call the api/posts/2 endpoint, assuming the entry that you wish to update has an ID of 2.

Performing a "delete" operation: The URL for performing an HTTP DELETE request in a Strapi application is /api/:pluralApiId/:documentId. Again, we call the api/posts/3 endpoint, assuming the entry that we wish to delete has an ID of 3.

More complex operations can be carried out using Strapi, which you can find out more about in the official docs.

Working with GraphQL

At this point in our application, we can now see that we are able to work with the REST APIs in the manner that we would like. This is because, by default, Strapi creates REST endpoints for applications. However, with Strapi, the fun does not stop there. With the GraphQL plugin, you can have the ability to transform our API into GraphQL APIs if need be, so that you can enjoy a more flexible way of interacting with your APIs.

To install the GraphQL plugin, run the following command:

$ yarn strapi install graphql

or

$ npm run strapi install graphql

After the GraphQL plugin has been successfully installed, head over to localhost:1337/graphql, where you'll be navigated to the GraphQL playground for your Strapi application. Here you can test various queries on your GraphQL endpoints:

The default query in GraphQL is similar to a GET request when working with REST. One of the benefits of GraphQL is the ability to select only the specific information needed in each query or mutation, rather than always retrieving the full objects.

To run a query or mutation, simply press the play button. You will notice that the response from the query only includes published posts, as draft post entries are not returned. This is different from a traditional REST API, where all entries would be returned.

GraphQL Queries on Data

As we were able to do with the REST APIs, we can fetch the post data with GraphQL. Although, here we're able to specify the data to return, like the id, title, content, and URL of the coverImage of a post. The corresponding GraphQL query is shown here:

User Authentication using GraphQL

Here we'll try something different with GraphQL, which we have done previously with the REST APIs - fetching content that depends on user authentication.

In order to sign up as a new user who can perform authorized requests on a Strapi GraphQL backend, a mutation is written in the playground.

As input, it is important to provide a username. Here we're using "test", the email [email protected], and a password, testpassword. Obviously these are test parameters, so any real passwords should be more complex and longer than the one we're using.

Run this, which should return a successful response.

Importantly, we not only receive back the user data as a confirmation that the operation has succeeded, but we also get the JWT token, which of course we can then use for authentication in subsequent API calls. This token can then be used for all later calls with our Strapi application until it expires, if an expiration is set.

Similarly, if you are a previously-registered user who wants to log in, login GraphQL operation can be performed by writing a mutation, as shown below:

Conclusion

In this article we've learned how fast, simple, and effective Strapi is for creating APIs in Node.js. The backend setup is arguably much easier than creating a REST API from scratch. Simply design your content types, and Strapi will automatically generate the REST API code for you. As a testament to the flexibility, you can also utilize GraphQL endpoints via the plugin system.

Amazing stuff, for sure!

If you have any suggestions or questions, feel free write me an email, post a comment, or send me a DM on Twitter.

Additional Resources

Last Updated: June 22nd, 2023
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Uchechukwu AzubukoAuthor

Uchechukwu Azubuko is a Software Engineer and STEM Educator passionate about making things that propel sustainable impact and an advocate for women in tech.

He enjoys having specific pursuits and teaching people better ways to live and work - to give them the confidence to succeed and the curiosity required to make the most of life.

Make Clarity from Data - Quickly Learn Data Visualization with Python

Learn the landscape of Data Visualization tools in Python - work with Seaborn, Plotly, and Bokeh, and excel in Matplotlib!

From simple plot types to ridge plots, surface plots and spectrograms - understand your data and learn to draw conclusions from it.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms