This article will guide you through using Strapi CMS and next.js to create an application that can register and authenticate users and continue sessions.

Set up the Next. Js application

Let’s start by setting up the Next. Js application. The easiest way to set this up is to use the create-next-app package.

In your terminal, run NPX create-next-app next-app, where next-app is the name of any project you like.

This will generate a folder named Next-app. Then, navigate to the folder on your terminal screen and run NPM run dev. You will see the application running on localhost port 3000.

Set the Strapi

Just like next.js, Strapi has an NPM package that makes it easy to start a project.

Run NPX create-strapi-app strapi-app to create a Strapi project. Select the Quickstart option when prompted.

Once installed, navigate to the strapi-app folder and run NPM run dev. When you access localhost:1337, you will be asked to create an administrator account. After doing so, you should be directed to the Strapi control panel.

Register a user in Strapi

In the Strapi sidebar, under the collection type, you should see Users, which is created by default. Here you can manually create users and see all existing users.

By default, Strapi’s API provides endpoints for creating, acquiring, and authenticating users.

/auth/local/register is the endpoint where we need to send the username, email, and password in order to register a user.

For this example application, we will have /register and /profile paths, where you can each register a user and see a user’s profile. The login screen will appear on the home page.

Let’s start with the
component.

This component returns a very basic form where the user enters their username, email, and password.

The userData object then holds this information and sends it to the/API/Register endpoint, which is what we’ll create in the background section of the Next application. Note that I used the AXIos package for the API calls, so make sure you install it as a dependency.

npm install --save axios

Once the request for/API /register is successful, we route the application to /profile to display user information. We will create the /profile page later.

The following files/components/registerComponent JSX, we will be in the/pages/register. JSX in using it.

import { useState } from 'react'; import { useRouter } from 'next/router'; import axios from 'axios'; const RegisterComponent = () => { const router = useRouter(); const [userData, setUserData] = useState({ username: '', email: '', password: '', }) const handleSubmit = async (e) => { e.preventDefault(); try { await axios.post('/api/register', userData); router.replace('/profile'); } catch (err) { console.log(err.response.data); } } const handleChange = (e) => { const { name, value } = e.target; setUserData({... userData, [name]: value }); } return ( <form onSubmit={handleSubmit}> <label> Username: <input type="text" name="username" onChange={e => handleChange(e)} /> </label> <br /> <label> Email: <input type="text" name="email" onChange={e => handleChange(e)} /> </label> <br /> <label> Password: <input type="password" name="password" onChange={e => handleChange(e)} /> </label> <br /> <button>Register</button> </form> ) } export default RegisterComponent;Copy the code

Now to create register.jsx in the Pages folder, simply return
.

import RegisterComponent from ".. /components/registerComponent"; const Register = () => ( <RegisterComponent /> ) export default Register;Copy the code

The next thing we need to do is create the/API/Register endpoint.

In /pages, you’ll find the/API folder. In that folder, create register.js. This way, when we make a request to/API /register from the Next. Js client, this file will handle it.

import axios from 'axios'; import { setCookie } from 'nookies' export default async (req, res) => { const { username, password, email } = req.body;  try { const response = await axios.post('http://localhost:1337/auth/local/register', { username, email, password, }) setCookie({ res }, 'jwt', response.data.jwt, { httpOnly: true, secure: process.env.NODE_ENV ! == 'development', maxAge: 30 * 24 * 60 * 60, path: '/', }); res.status(200).end(); } catch (e) { res.status(400).send(e.response.data.message[0].messages[0]); }}Copy the code

As mentioned earlier, the endpoint for a registered user is /auth/local/register. Since Strapi is running at localhost:1337, So we send a request to [http://localhost:1337/auth/local/register] (http://localhost:1337/auth/local/register) and attach we retrieved from the request body data user name, email and password.

Note that if you are deploying your Strapi application to a remote server, you need to replace the base URL accordingly. It is good practice to store the base URL in an environment variable and then use it instead of hard-coding the URL. However, for the sake of simplicity, I’ll just use localhost:1337 in this article.

The response returned from the POST request contains a JWT token. This token is specific to the user and should be stored securely for automatic authentication of the user.

The Nookies package is a collection of helper functions used to handle cookies in a Next-.js application.

The setCookie function takes the response object as its first argument. The second argument is the name of the cookie, which can be anything you choose.

The third argument is the value of the cookie. In our case, this is the JWT token returned from the request response, and as the last parameter, we can pass an object with options.

The httpOnly flag prevents clients — javascript running on the browser — from accessing cookies, so you can protect cookies from possible cross-site scripting (XSS) attacks.

The secure flag ensures that cookies are transmitted only over a secure HTTPS connection. Since localhost is not an HTTPS connection, we would set it to false if we were running the application in development.

MaxAge determines how many seconds the cookie is valid. In this case, it is set to 30 days.

Path determines the path on which the cookie should be valid. It is set to/to make the cookie valid for all paths.

Notice that in the case of a request failure, we are sending a message. From e.esponse.data.message [0].messages[0], this message contains useful information about the cause of the request failure. This could be an invalid email, or a user name already in use, and so on.

Therefore, this information can be used to display appropriate error messages in the event of a registration failure.

For simplicity, we will not handle this error message on the client side. We just record it and display it in the browser console.

Create a user profile page in Strapi

This is the /profile path where user data, such as username and email, is displayed. This route can be accessed directly, or the user can be directed to it when logging in or registering.

Since this page requires cookies to authenticate the user, and we have set cookies on the server side with the httpsOnly flag, we also need to read cookies on the server.

GetServerSideProps is a next-.js function that runs on the server on each request. In this function, we can parse the cookie to access the JWT token, and then make a request to Strapi to get the user data. We can then return this data from a function and expose it as a prop on the page.

The contents of /pages/profile.jsx are as follows.

import { useRouter } from 'next/router'; import axios from 'axios'; import nookies from 'nookies'; const Profile = (props) => { const router = useRouter(); const { user: { email, username } } = props; const logout = async () => { try { await axios.get('/api/logout'); router.push('/'); } catch (e) { console.log(e); } } return ( <div> <div>Username: {username}</div> <div>Email: {email}</div> <button onClick={logout}>Logout</button> </div> ) } export const getServerSideProps = async (ctx) => { const cookies = nookies.get(ctx) let user = null; if (cookies? .jwt) { try { const { data } = await axios.get('http://localhost:1337/users/me', { headers: { Authorization: `Bearer ${cookies.jwt}`, }, }); user = data; } catch (e) { console.log(e); } } if (! user) { return { redirect: { permanent: false, destination: '/' } } } return { props: { user } } } export default Profile;Copy the code

Let’s focus on the function getServerSideProps for a moment. Again, we use the Nookies package to get cookies from context objects (CTX). The name of the cookie we set is JWT, so check for the presence of cookie.jwt.

If this particular cookie exists, then we send a request to the/Users/ME endpoint of the local Strapi server containing the Authorization header of the JWT token to get the user information.

If the cookie does not exist or the JWT token is invalid, the user variable remains null and we redirect the page to /, which is the home page with the login interface. Otherwise, we return user in the props object, which is then used as a prop in the Profile function we export.

The Profile function returns a very basic tag that displays the user name and E-mail, as well as a logout button that calls the logout function.

This function sends a request to the/API /logout endpoint to delete the cookie and then routes the page to /.

Here is /pages/ API /logout.js.

import { destroyCookie } from 'nookies' export default async (req, res) => { destroyCookie({ res }, 'jwt', { path: '/',}); res.status(200).end(); }Copy the code

The logged in user

Here is the contents of /pages/index.jsx, which is where the home page and

are located, and a register button that routes pages to the/Register route.

import { useRouter } from 'next/router'; import axios from 'axios'; import nookies from 'nookies'; import LoginComponent from '.. /components/loginComponent'; const Home = () => { const router = useRouter(); const goToRegister = () => { router.push('/register'); } return ( <div> <LoginComponent /> <button onClick={goToRegister}>Register</button> </div> ) } export const getServerSideProps = async (ctx) => { const cookies = nookies.get(ctx) let user = null; if (cookies? .jwt) { try { const { data } = await axios.get('http://localhost:1337/users/me', { headers: { Authorization: `Bearer ${cookies.jwt}`, }, }); user = data; } catch (e) { console.log(e); } } if (user) { return { redirect: { permanent: false, destination: '/profile' } } } return { props: {} } } export default Home;Copy the code

As we did in /pages/profile.jsx, we again use the getServerSideProps function to get the cookie and then check for the presence of the user.

If the user does exist, then we redirect to the /profile route. If it doesn’t, we return an empty props object and leave it in the same path to render
, which looks like this.

import { useState } from 'react'; import { useRouter } from 'next/router'; import axios from 'axios'; const LoginComponent = () => { const router = useRouter(); const [userData, setUserData] = useState({ identifier: '', password: '', }); const handleSubmit = async (e) => { e.preventDefault(); try { await axios.post('/api/login', { ... userData }); router.push('/profile'); } catch (err) { console.log(err.response.data); } } const handleChange = (e) => { const { name, value } = e.target; setUserData({... userData, [name]: value }) } return ( <div> <form onSubmit={handleSubmit}> <label> Email: <input type="text" name="identifier" onChange={e => handleChange(e)} /> </label> <br /> <label> Password: <input type="password" name="password" onChange={e => handleChange(e)} /> </label> <br /> <button>Login</button> </form>  </div> ) } export default LoginComponent;Copy the code

This component renders a form to get the email and password. When the form is submitted, a POST request is made to/API /login and the page is redirected to the /profile route.

The next thing you can see is/API /login.js

import axios from 'axios';
import { setCookie } from 'nookies'

export default async (req, res) => {
  const { password, identifier } = req.body;

  try {
    const postRes = await axios.post('http://localhost:1337/auth/local', {
      identifier,
      password,
    })

    setCookie({ res }, 'jwt', postRes.data.jwt, {
      httpOnly: true,
      secure: process.env.NODE_ENV !== 'development',
      maxAge: 30 * 24 * 60 * 60,
      path: '/',
    });

    res.status(200).end();
  } catch (e) {
    res.status(400).send(e.response.data.message[0].messages[0]);
  }
}

Copy the code

/auth/local is a Strapi endpoint for logging in users. Objects sent to this endpoint should have identifier and Password keys.

As we did in/API /register.js, we also set a cookie with the same name and options to keep the user session.

conclusion

In this article, we demonstrated how to use next.js as a full-stack application to create a user interface with multiple routes, and to create API endpoints to communicate with Strapi’s API for registering and retrieving user information. It also demonstrates setting and retrieving cookies on the server side of next.js to keep the user session going.

The postImplementing user registration and authentication with Strapi and Next.jsappeared first onLogRocket Blog.