Strapi is an awesome technology that can be included as part of a tech stack for creating full-fledged applications. It is known as a headless CMS because it serves only as the backend infrastructure for your application. Therefore, it can handle the entire backend for your application, leaving you with the flexibility to choose whatever frontend technology you’d like to use for your application.
Since Strapi only takes care of the backend part of your application, data stored in the database using Strapi are turned into an API—either REST or GraphQL—that can then be consumed by the frontend part of your application. This frontend could be built by any technology of your choice, though this guide will make use of Next.js. In most cases, you wouldn't want just any user accessing your application to be able to interact with your entire backend infrastructure and modify data in your database. In other words, there should be a way to make sure that only authenticated users can access and interact with Strapi.
Authenticating requests is important for any application because not every endpoint provided by your Strapi backend should be publicly available to be consumed by any frontend client. This is especially true for requests that modify part of the database. For example, a POST
or DELETE
request should be somehow protected from public modification and only allow users with the right credentials and permission access.
In this tutorial, you'll be guided through how to implement authenticating requests in Strapi using Next.js as the example frontend to ensure users are properly authenticated before allowing them access to Strapi.
Since you'll be using Next.js to build up the frontend for this tutorial, a basic knowledge of Next.js fundamentals is required. In addition, make sure you have the latest version of Node.js installed, preferably version 12 or 14, as other versions might not be supported by Strapi. Also, it would be helpful to have a basic knowledge of how Strapi works, although this is not strictly required for this guide’s purposes.
Let's start by creating a new Strapi application. In your terminal, open a new folder:
$ mkdir authenticated_requests && cd authenticated_requests
Inside that folder, install Strapi
:
$ npx create-strapi-app@latest backend --quickstart
Note that this is very similar to using create-react-app
if you have worked with React before. The name of the Strapi application is called backend
since Strapi predominantly handles the backend. You also added the --quickstart
flag here, which helps to quickly bootstrap a new Strapi app using Sqlite-3
, permitting a simplified means of configuring the database. Once done, Strapi should be automatically launched in a new window in your default browser, and you should see a screen similar to the screenshot below. By default, Strapi runs on http://localhost:1337/.
However, to launch the server manually, use this command:
$ cd backend
$ npm run develop
Next, fill in the needed credentials to successfully log in to the Strapi admin panel. Once you're logged in, you should see this screen:
Strapi is well known for having a very clean admin area, with very streamlined functionality for you to get started quickly. Note that Strapi automatically creates a User
collection type, which you will come back to later on. Collection types allow you to create a new model in your database with the required fields and data types to hold the actual data. This is created using the Content-Type builder library.
Create a new collection type of your own by clicking on the Create new collection type button and give it a display name of Post
. It is recommended to keep the display name singular, as Strapi will by default pluralize the display name. Next, click on the Continue button, and add a new field with a text field
type. Give it a name title
and click on the Finish button. Finally, click Save to successfully add this new collection type. After clicking on the Save button, the server needs to restart for changes to be reflected.
Before you proceed, add at least one title
to the Posts
collection. The Posts
collection you just created earlier can be seen under the Content Manager page. Make sure you publish each new entry after saving them, by clicking on the publish button:
For the most part, you wouldn't be touching the actual Strapi code. Instead, you would be modifying content using the admin panel. Now, you’ve successfully set up the Strapi backend; in the next section, you’ll be setting up the Next.js frontend.
Before we dive into making authenticated requests, you’ll first need to bootstrap your Next.js frontend. Inside the authenticated_requests
folder, run the following command to create a new Next.js application:
$ npx create-next-app@latest frontend --use-npm
Here, you gave the application the name frontend
since this would be where the frontend code will reside. You also added the --use-npm
flag, to strictly use npm
to install Next.js, though yarn
could also be used.
Once you have Next.js installed, change the directory to the frontend folder and then run the development server like so:
$ cd frontend
$ npm run dev
Then, navigate to http://localhost:3000/ and you should see this:
Next, open up the frontend folder using a text editor and replace the content inside the pages/index.js
file with the following code:
1export default function Home() {
2 return (
3 <div>
4 <h1>Implementing Authenticated API Requests to Strapi</h1>
5 </div>
6 );
7
8}
The server will automatically reload and reflect the changes. Now you’ve successfully bootstrapped a new Next.js project. In the next section, you’ll be implementing authenticated requests.
In your Next.js application, update the pages/index.js
file like so:
1export default function Home({ posts }) {
2 console.log(posts);
3 return (
4 <div>
5 <h1>Implementing Authenticated API Requests to Strapi</h1>
6 </div>
7 );
8}
9
10export async function getServerSideProps(ctx) {
11
12 // get posts from strapi REST API
13 const res = await fetch('http://localhost:1337/api/posts');
14 let posts = await res.json();
15 return {
16 props: {
17 posts: posts,
18 },
19 };
20}
Here, you're fetching all the posts and loading each post object in your console. Note that if you navigate back to http://localhost:3000/ after saving the file, you will see a status code of 403
in your console. This status stands for Forbidden, because by default, Strapi prevents the public from accessing data from your Strapi backend.
If you’d like, you can change the default setting and allow all public access. However, for this guide, you will only allow authenticated users access, using the steps outlined below.
Note that for a production environment you wouldn’t want to expose your
URL
like thishttp://localhost:3000/
. Instead, you can store them inside an environmental variable.
Remember that Strapi automatically creates a User
collection type. Therefore, create a new user by clicking on the Add new entry button on the Users page. Make sure you set the Confirmed button to On for that user, as well as the Role to Authenticated.
Now, on the Settings page, access the Users & Permissions Plugin and click on Roles.
Here, you will see two roles, Authenticated and Public. Click on Authenticated, and you will be taken to a new screen where you can set the actions of what authenticated users can perform on the Posts
collection type. For this example, click on Select all in the Permissions section.
This setting will allow any authenticated user access to see and modify content in the Strapi backend. Note that you can also customize this to the way you see fit by selecting the exact role you want the authenticated user to have. For example, you might not want to allow the authenticated user to be able to delete content, so you could disable this.
Whichever set of permissions you decide on, be sure to Save your changes.
Next, you will send an authenticated request in Next.js to Strapi to authenticate the user you just created.
Inside the pages/index.js
file, update it with the following code:
1export default function Home({ posts, loginResponseData }) {
2 console.log(posts);
3 console.log(loginResponseData);
4 return (
5 <div>
6 <h1>Implementing Authenticated API Requests to Strapi</h1>
7 {posts && posts.map((_post_) => (
8 <div _key_={post.id}>
9 <h3>{post.attributes.title}</h3>
10 </div>
11 ))}
12 </div>
13 );
14}
15
16export async function getServerSideProps(ctx) {
17 const loginData = {
18 identifier: '<User Email>',
19 password: '<User Password>',
20 };
21
22 const login = await fetch(`http://localhost:1337/api/auth/local`, {
23 method: 'POST',
24 headers: {
25 Accept: 'application/json',
26 'Content-Type': 'application/json',
27 },
28 body: JSON.stringify(loginData),
29 });
30
31 const loginResponseData = await login.json();
32 // get posts from strapi REST API
33 const res = await fetch(`http://localhost:1337/api/posts`);
34 let posts = await res.json();
35 posts = posts.data
36 return {
37 props: {
38 posts: posts,
39 loginResponseData: loginResponseData,
40 },
41 };
42}
Here, you're making a POST
request on server reload, passing on the details of the user you created (email and password), and logging the JSON response to the console. Save this file and refresh your browser.
Don’t forget to replace <User Email>
and <User Password>
with the email and password of the user you created.
Here, you will get back a JSON Web Token (JWT) attached to the user, and by using this JWT you can have access to the authenticated routes.
Update the res
constant in the pages/index.js
file by adding the JWT
to the GET
requests like so:
1...
2
3const res = await fetch(`http://localhost:1337/api/posts`, {
4 headers: {
5 Authorization: `Bearer ${loginResponseData.jwt}`
6 }
7 });
8
9...
This will successfully return all the posts stored in the Strapi backend.
Additionally, you can take this a step further and store the JWT inside of a cookie. With this, instead of authenticating users on each request made on every Next.js page, you can first check if this JWT cookie is present. If it is not present, then you can proceed to authenticate the user. This is quite similar when building a login functionality. You will be using a third-party package called Nookies to implement this functionality.
Inside the frontend
folder, install Nookies:
$ npm install nookies
Next, update the pages/index.js
file:
1import { parseCookies, setCookie } from 'nookies'
2export default function Home({posts}) {
3 return (
4 <div>
5 <h1>Implementing Authenticated API Requests to Strapi</h1>
6 {posts && posts.map((_post_) => (
7 <div _key_={post.id}>
8 <h3>{post.attributes.title}</h3>
9 </div>
10 ))}
11 </div>
12
13 )
14
15}
16
17export async function getServerSideProps(ctx) {
18 const jwt = parseCookies(ctx).jwt
19
20 // if there is a jwt token don’t authenticate the user again
21 if (jwt) {
22 // get posts from strapi REST API
23
24 const res = await fetch(`http://localhost:1337/api/posts`, {
25 headers: {
26 Authorization: `Bearer ${jwt}`
27 }
28 });
29
30 let posts = await res.json();
31 posts = posts.data
32 return {
33 props: {
34 posts:posts
35 }
36 }
37}
38
39 // if there isn’t a jwt token authenticate the user
40
41 const loginData = {
42 identifier: '<User Email>',
43 password: '<User Password>',
44 }
45
46 const login = await fetch(`http://localhost:1337/api/auth/local`, {
47 method: "POST",
48 headers: {
49 'Accept': 'application/json',
50 'Content-Type': 'application/json'
51 },
52 body: JSON.stringify(loginData)
53 })
54
55 const loginResponseData = await login.json();
56 setCookie(ctx, 'jwt', loginResponseData.jwt, {
57 maxAge: 30 * 24 * 60 * 60,
58 path: '/'
59 })
60
61 // get posts from strapi REST API
62
63 const res = await fetch(`http://localhost:1337/api/posts`, {
64 headers: {
65 Authorization: `Bearer ${loginResponseData.jwt}`
66 }
67 });
68
69 let posts = await res.json();
70
71 posts = posts.data
72 return {
73 props: {
74 posts:posts
75 }
76 }
77}
Here, you're setting a cookie called jwt
using the setCookie
method. Therefore, when the page initially loads, the cookie will be set, and you can then have access to this cookie using the parseCookies
method in every other Next.js page you build. You can see the cookie under the Application
tab using the development tools in your browser.
In this guide, you were introduced to the Strapi backend and how to use Next.js to interact with it. You learned how to create a new user and how to set roles and permissions in Strapi to restrict public access to the backend. Finally, you saw how to make authenticated requests in Next.js and store a cookie to make future authenticated requests easier.
If you want to continue learning more about this, a good challenge could be to build up a full login and logout functionality in Strapi using the Next.js frontend and make a protected route that only allows users that have logged in to have access to the page. In addition, this page should now allow authenticated users to make authenticated requests of some sort to your Strapi backend.
Samuel Torimiro is enthusiastic about software engineering, teaching and entrepreneurship. His goal is to build web and mobile applications to better serve hundreds of millions of customers.