Authentication is one of the most critical aspects of building web applications. It helps protect your users’ data and provides an extra layer of security by preventing unauthorized access. Next.js 13 handles authentication in the simplest yet most effective way with advanced routing for progressive web applications.
This article will highlight the new Next.js features on authentication and how you can use Appwrite to implement user authentication. The project repository can be found here.
Prerequisites
- Node.js installed - It comes with Node Package Module(
NPM
) that is used to install and manage packages for Next.js and Appwrite web SDK - Docker installed - Docker is used to install Appwrite locally
- Basic knowledge of React
Next.js version 13 brings enhanced features and performance, including new features for implementing authentication and creating protected routes by introducing middleware to enable full flexibility with the Next.js router.
This article will demonstrate how to use Appwrite to authenticate users in a Next.js application.
Authentication using Appwrite
Appwrite is an open-source project that provides an end-to-end solution for managing the entire lifecycle of your app, from development to deployment. It is designed to make it easy to develop, test, and deploy your app with minimal configuration.
Installing Appwrite
First, ensure that Docker is properly installed in your development environment. You can confirm the installation by running the following command on your terminal:
docker
To install Appwrite, use the following commands based on your operating system:
Windows Command Prompt(CMD):
docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.1.1
Unix:
docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.1.1
Once the installation is complete, open a web browser and load the Appwrite server URL from the installation configurations:
localhost:<port_number>
On loading the Appwrite on your browser, you will be redirected to a login page:
Create a new user account by clicking on the Sign Up link. After successful registration, you will be redirected to a new page to create a new project:
Enter the name of the project and proceed to create a project:
Set up a Next.js project
To set up a project using the latest version of Next.js, 13, run the following command on your command line tool:
npx create-next-app@latest
or
yarn create next-app
The new version asks for a few questions; you can answer them as shown below:
Would you like to use TypeScript? <No>
Would you like to use ESLint? <No>
Would you like to use Tailwind CSS? <Yes>
Would you like to use `src/` directory? <Yes>
Would you like to use App Router? (recommended) <Yes>
Would you like to customize the default import alias? <Yes>
What import alias would you like configured? @/* <Press Enter>
Connecting to Appwrite
To connect a Next.js application with Appwrite, install the appwrite
module using:
npm install appwrite
Create a new directory called utils
, and inside add a new appwrite configuration file: appwriteClient.js
Create a library file to establish the connection and use the code snippet below:
import { Client, Account, ID } from 'appwrite';
//create client
const client = new Client();
client
.setEndpoint('http://localhost/v1')
.setProject('YOUR PROJECTID GIES HERE');
//create account
const account = new Account(client);
export const createAccount = (email, password, name) =>
account.create(ID.unique(), email, password, name);
export const createUserSession = (email, password) =>
account.createEmailSession(email, password);
export const getAccount = () => account.get();
export const logout = () => account.deleteSession('current');
Code Explanation:
export const createAccount = async (email, password, name) =>
account.create(ID.unique(), email, password, name);
The createAccount
function takes an email
, password
, and name
parameters. The function calls the create
method on the account
instance to register a new user by passing a unique ID generated using ID.unique()
and the provided email
, password
, and name
values.
export const createUserSession = async (email, password) =>
account.createEmailSession(email, password);
The createUserSession
function takes an email
and password
parameter. The function calls the createEmailSession
method to log in users using the account
instance and passing the provided email
and password
.
export const getAccount = () => account.get();
export const logout = () => account.deleteSession('current');
The getAccount
and logout
function gets details of logged-in users and log-out users, respectively.
Building the application UI
The application will consist of three pages; a sign-in, sign-up, and an authenticated page.
Sign-in page
To do this, first, you will create a signin/page.js
file inside the app
folder and add the snippet below:
'use client';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { createUserSession } from '../../../utils/appwriteClient';
import Link from 'next/link';
export default function SignIn() {
const router = useRouter();
const [value, setValue] = useState({ email: '', password: '' });
const [isLoading, setIsLoading] = useState(false);
const handleChange = (e) => {
setValue({ ...value, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
setIsLoading(true);
const { email, password } = value;
createUserSession(email, password)
.then((res) => {
setIsLoading(false);
router.push('/');
})
.catch((e) => {
setIsLoading(false);
alert('Incorrect email or password!!!');
});
};
return (
//UI code goes here
);
}
The snippet above imports the required dependencies, manage the application state, and creates an handleSubmit
function that uses the createUserSession
to authenticate users.
Lastly, you will update the application UI using the states and function created above.
//import goes here
export default function SignIn() {
//states and function goes here
return (
<div className='w-screen h-screen bg-white'>
<main className='py-4 px-4 lg:py-10 lg:px-10 w-full'>
<div className='flex justify-center mb-8'>
<h1 className='text-2xl font-medium text-gray-700'>
Authentication with Appwrite and Nextjs 13
</h1>
</div>
<section className='flex justify-center'>
<div className='px-4 py-2 border rounded-lg w-full lg:w-2/4'>
<div className='border-b h-8 mb-4'>
<h3 className='text-gray-700'>
Sign in with your details
</h3>
</div>
<form onSubmit={handleSubmit}>
<fieldset>
<label className='text-sm text-gray-400 mb-4 block'>
Email
</label>
<input
name='email'
className='border w-full rounded-sm mb-6 p-2'
required
value={value.email}
onChange={handleChange}
type='email'
/>
</fieldset>
<fieldset>
<label className='text-sm text-gray-400 mb-4 block'>
Password
</label>
<input
name='password'
className='border w-full rounded-sm mb-6 p-2'
required
value={value.password}
onChange={handleChange}
type='password'
/>
</fieldset>
<button
className='text-sm text-white px-8 py-2 rounded-sm bg-blue-600 hover:bg-blue-700'
disabled={isLoading}
>
Sign in
</button>
<div className='flex mt-6'>
<p>Don't have an account?</p>{' '}
<Link
href='/signup'
className='ml-2 text-blue-600 font-medium'
>
Sign up
</Link>
</div>
</form>
</div>
</section>
</main>
</div>
);
}
Sign-up page
In the same app
folder, you will first create a signup/page.js
file and add the snippet below:
'use client';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { createAccount } from '../../../utils/appwriteClient';
import Link from 'next/link';
export default function SignUp() {
const router = useRouter();
const [value, setValue] = useState({ name: '', email: '', password: '' });
const [isLoading, setIsLoading] = useState(false);
const handleChange = (e) => {
setValue({ ...value, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
setIsLoading(true);
const { email, name, password } = value;
createAccount(email, password, name)
.then((res) => {
setIsLoading(false);
alert('Account created successfully! you can now login');
router.push('/signin');
})
.catch((e) => {
setIsLoading(false);
alert('Error creating user');
});
};
return (
//UI code goes here
);
}
The snippet above imports the required dependencies, manage the application state, and creates an handleSubmit
function that uses the createAccount
to sign-up new users.
Lastly, you will update the application UI using the states and function created above.
//import goes here
export default function SignUp() {
//states and function goes here
return (
<div className='w-screen h-screen bg-white'>
<main className='py-4 px-4 lg:py-10 lg:px-10 w-full'>
<div className='flex justify-center mb-8'>
<h1 className='text-2xl font-medium text-gray-700'>
Authentication with Appwrite and Nextjs 13
</h1>
</div>
<section className='flex justify-center'>
<div className='px-4 py-2 border rounded-lg w-full lg:w-2/4'>
<div className='border-b h-8 mb-4'>
<h3 className='text-gray-700'>
Sign up with your details
</h3>
</div>
<form onSubmit={handleSubmit}>
<fieldset>
<label className='text-sm text-gray-400 mb-4 block'>
Name
</label>
<input
name='name'
className='border w-full rounded-sm mb-6 p-2'
required
value={value.name}
onChange={handleChange}
type='text'
/>
</fieldset>
<fieldset>
<label className='text-sm text-gray-400 mb-4 block'>
Email
</label>
<input
name='email'
className='border w-full rounded-sm mb-6 p-2'
required
value={value.email}
onChange={handleChange}
type='email'
/>
</fieldset>
<fieldset>
<label className='text-sm text-gray-400 mb-4 block'>
Password
</label>
<input
name='password'
className='border w-full rounded-sm mb-6 p-2'
required
value={value.password}
onChange={handleChange}
type='password'
/>
</fieldset>
<button
className='text-sm text-white px-8 py-2 rounded-sm bg-blue-600 hover:bg-blue-700'
disabled={isLoading}
>
Sign up
</button>
<div className='flex mt-6'>
<p>Already have an account?</p>{' '}
<Link
href='/signin'
className='ml-2 text-blue-600 font-medium'
>
Sign in
</Link>
</div>
</form>
</div>
</section>
</main>
</div>
);
}
Authenticated page
Lastly, you will modify the page.js
file inside the app
folder as shown below:
'use client';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { getAccount, logout } from '../../utils/appwriteClient';
export default function Home() {
const [session, setSession] = useState(null);
const router = useRouter();
const getSession = () => {
getAccount()
.then((res) => setSession(res))
.catch((err) => {
router.push('/signin');
console.log(err);
});
};
useEffect(() => {
getSession();
}, []);
const signOut = () => {
logout();
alert('logout successful');
router.push('/signin');
};
return (
<div className='w-screen h-screen bg-white'>
<main className='py-4 px-4 lg:py-10 lg:px-10 w-full'>
<div className='flex justify-center mb-8'>
<h1 className='text-2xl font-medium text-gray-700'>
Welcome
</h1>
</div>
<section className='flex justify-center'>
<div className='px-4 py-2 border rounded-lg w-full lg:w-2/4'>
<h3 className='text-gray-700'>
Name: {session && session.name}
</h3>
<h3 className='text-gray-700 mt-4'>
Name: {session && session.email}
</h3>
<button
className='text-sm text-white px-8 py-2 rounded-sm bg-black hover:bg-gray-800 mt-6'
onClick={signOut}
>
Log out
</button>
</div>
</section>
</main>
</div>
);
}
The snippet above does the following:
- Imports the required dependencies.
- Creates states and route variables to manage logged-in user sessions and routes accordingly.
- Creates a
getSession
function to get the logged-in user session and re-route to the login page if the session is empty. - Creates a
signOut
function to log the user out - Updates the UI to show logged-in user’s name and email
With that done, you can test the application by running the command below:
npm run dev
or
yarn dev
You can also validate the authenticated user details on the Appwrite console:
Conclusion
In conclusion, this post highlights how Appwrite provides an easy and secure way to manage authentication in Next.js applications. With Appwrite, developers can quickly and easily establish authentication, manage users, and provide role-based access control to their applications.
Resources
The following resources might be helpful: