Text messages have evolved during the past decade, becoming a primary source of electronic communication. As this medium is increasingly chosen as a means of passing essential information, it becomes more necessary to control what information a user can access.
What we will be building
This article is part one of a two-part series that describes how to build an end-to-end encrypted chat with Appwrite. This post discusses authenticating and managing a user's account with the Appwrite account API.
GitHub URL
https://github.com/Iheanacho-ai/chat-app-nextjs
Prerequisites
To get the most out of this article, you’ll need the following:
- A basic understanding of CSS, JavaScript, and React.js.
- Docker Desktop installed on your computer; run the
docker -v
command to verify that we have Docker Desktop installed. If not, install it from the Get Docker documentation. - An Appwrite instance running on your computer. Check out this article to create a local Appwrite instance. We will use Appwrite’s powerful account service to authenticate and manage a user's account.
Setting up our Next.js app
Next.js is an open-source React framework that enables us to build server-side rendered static web applications.
To create our Next.js app, navigate to the preferred directory and run the terminal command below:
npx create-next-app@latest
# or
yarn create next-app
After creating our app, change the directory to the project and start a development server with the command below.
cd <name of our project>
npm run dev
To see our app, go to http://localhost:3000.
Installing Appwrite
Appwrite is an open-source, end-to-end, backend server solution that allows developers to build applications faster.
To use Appwrite in our Next.js application, install the Appwrite client-side SDK for web applications:
npm install appwrite
Creating a new Appwrite project
When creating the Appwrite instance, we specified what hostname and port our console would live in. The default value is localhost:80.
Go to localhost:80 and create a new account to see the console.
On our console, we see a Create Project button. Click on it to start a new project.
The project dashboard appears once we have created the project. At the top of the page, click the Settings bar to access the Project ID and API Endpoint.
Now, copy the Project ID and API Endpoint, which we need to initialize the Appwrite Web SDK.
Next, we’ll create an init.js
file in our project's root directory to initialize the Appwrite Web SDK.
import { Client, Account } from "appwrite";
import Router from 'next/router';
export const client = new Client();
export const account = new Account(client);
client
.setEndpoint('http://localhost/v1') // Your API Endpoint
.setProject('62d9859fd5a5923edf53') // Your project ID
;
Creating the signup interface
Now, we need to create a signup form for our users in our project's pages/
index.jsx
file.
import Link from 'next/link';
const Home = () => {
return (
<div>
<div className="signup">
<h2 className='form-h2'>Sign up</h2>
<form action="">
<label htmlFor="name">Name</label>
<input type="text" className='signup-input' name="name" id="" />
<label htmlFor="email">Email</label>
<input type="email" className='signup-input' name="email" id=""/>
<label htmlFor="password">Password</label>
<input type="password" className='signup-input' name="password" id=""/>
<label htmlFor="password">Confirm Password</label>
<input type="password" className='signup-input' name="confirm password" id=""/>
<button type='button' className= "button">Sign Up</button>
<p>Already have an account, <Link href="/signin" className='link'>Sign in</Link></p>
</form>
</div>
</div>
)
};
export default Home;
Next, we navigate to the styles
folder at our project's root directory, this folder contains a global.css
file. In the styles/global.css
file, we’ll add the styling to our signup form.
https://gist.github.com/Iheanacho-ai/982cc4762bb129965e4a03a23a110256
Here is how our signup form looks.
Creating the sign-in interface
If a user has already created an account with our application, then we need to create a sign-in page to allow users to sign into their account.
First, create a signin.jsx
file in the pages
folder. This pages/signin.jsx
file will contain the piece of code in the code block below.
const Home = () => {
return (
<div>
<div className="signin">
<h2 className='form-h2'>Sign in </h2>
<form action="">
<label htmlFor="email">Email</label>
<input type="email" name= "email" className='signup-input' id=""/>
<label htmlFor="password">Password</label>
<input type="password" className='signup-input' name="password" id=""/>
<button type='button' className= "button">Sign in</button>
</form>
</div>
</div>
)
};
export default Home;
Next, add styling to the sign-in page.
.signup, .signin{
width: 400px;
display: flex;
flex-flow: column;
justify-content: center;
text-align: left;
background: #fff;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
border-radius: 7px;
margin: 10% auto;
padding: 30px;
}
Go to http://localhost:3000/signin to see our sign-in page.
Authenticating users
In this section of the article, we’ll discuss using the form to create, authenticate, and sign in users.
Signing up users
In the pages/index.jsx
file, import the useState
React hook for handling states in our application.
import { useState } from 'react';
import Link from 'next/link';
Next, create state variables to store the form values in the pages/index.jsx
file.
const [name, setName] = useState()
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const [confirmPassword, setConfirmPassword] = useState();
The state variables do the following:
- The
name
variable stores the name of the user creating an account on our application - The email variable stores the email of the user
- The
password
andconfirmPassword
variables store the password to the user’s account
Next, we’ll write a signupWithEmailandPassword
function to create an account for a new user.
const signupWithEmailandPassword = async () => {
if (password.length >= 8) {
if (password === confirmPassword) {
try {
await account.create('unique()', email, password, name)
}catch (error) {
console.log(error)
}
}else {
alert("password do not match")
}
} else {
alert('Password length should be up to 8 characters')
}
}
The signupWithEmailandPassword
function does the following:
- Checks whether the user’s password is equal to or exceeds eight characters
- Checks whether
password
andconfirmPassword
are the same - Creates a new user account with the user’s email, password, and name
- Logs any error encountered to the console
Next, pass the state variables as values to the input fields and the signupWithEmailandPassword
function to the onClick
event listener on our Sign up button.
<form action="">
<label htmlFor="name">Name</label>
<input type="text" className='signup-input' name="name" value={name} onChange={(e) => setName(e.target.value)} id="" />
<label htmlFor="email">Email</label>
<input type="email" className='signup-input' name="email" value={email} onChange={(e) => setEmail(e.target.value)} id=""/>
<label htmlFor="password">Password</label>
<input type="password" className='signup-input' name="password" value={password} onChange={(e) => setPassword(e.target.value)} id=""/>
<label htmlFor="password">Confirm Password</label>
<input type="password" className='signup-input' name="confirm password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} id=""/>
<button type='button' className= "button" onClick={signupWithEmailandPassword}>Sign Up</button>
<p>Already have an account, <Link href="/signin" className='link'>Sign in</Link></p>
</form>
Here is how our index.jsx
file looks.
https://gist.github.com/Iheanacho-ai/72acee318020e43f85de09d17c5d73a9
Fill out the form to create an account with the application.
To see a list of users that have created accounts with our application, we go to the Users tab on the Appwrite Dashboard.
Signing in users
A user needs to sign in to create an account session. This account session allows users to interact with our application.
To sign users in, we’ll create a signinWithEmailandPassword
function in the init.js
file.
import { Client, Account } from "appwrite";
import Router from 'next/router';
export const client = new Client();
export const account = new Account(client);
client
.setEndpoint('http://localhost/v1') // Your API Endpoint
.setProject('62d9859fd5a5923edf53') // Your project ID
;
export const signinWithEmailandPassword = async (email, password) => {
try {
await account.createEmailSession(email, password)
Router.push("/chat")
} catch (error) {
console.log(error)
}
}
The signinWithEmailandPassword
function above does the following:
- Creates a user session with the Appwrite
createEmailSession
function. This function receives email and password parameters. - Navigates to the chat route after a user signs in.
- Logs any error encountered in the console.
Immediately after creating an account, we want a user to sign into our application. To do this, import the signinWithEmailandPassword
function in the pages/
index.jsx
file.
import { account, signinWithEmailandPassword } from '../init';
Next, we’ll call the signinWithEmailandPassword
function in the signupWithEmailandPassword
function.
const signupWithEmailandPassword = async () => {
if (password.length >= 8) {
if (password === confirmPassword) {
try {
await account.create('unique()', email, password, name)
alert("account created successfully")
// signes in the user
signinWithEmailandPassword(email, password)
} catch (error) {
console.log(error)
}
} else {
alert("password do not match")
}
} else {
alert('Password length should be up to 8 characters')
}
}
We then import the signinWithEmailandPassword
function in our pages/signin.jsx
file.
import { useState } from 'react';
import { signinWithEmailandPassword } from '../init';
Next, we’ll create state variables to store our input field values email
and password
.
const [email, setEmail] = useState();
const [password, setPassword] = useState();
After creating the state variables, create a handleSignin
function that calls the signinWithEmailandPassword
function.
const handleSignin = () => {
signinWithEmailandPassword(email, password)
}
We then pass the state variables as values to the input fields and the handleSignin
function to the onClick
event listener on our Sign in button.
<div>
<div className="signin">
<form action="">
<label htmlFor="email">Email</label>
<input type="email" className='signup-input' name="email" value={email} onChange={(e) => setEmail(e.target.value)} id=""/>
<label htmlFor="password">Password</label>
<input type="password" className='signup-input' name="" value={password} onChange={(e) => setPassword(e.target.value)} id=""/>
<button type='button' className= "button" onClick={handleSignin}>Sign in</button>
</form>
</div>
</div>
Here is how our pages/signin.jsx
file should look at this point.
import { useState } from 'react';
import { signinWithEmailandPassword } from '../init';
const Home = () => {
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const handleSignin = () => {
signinWithEmailandPassword(email, password)
}
return (
<div>
<div className="signin">
<form action="">
<label htmlFor="email">Email</label>
<input type="email" className='signup-input' name="email" value={email} onChange={(e) => setEmail(e.target.value)} id=""/>
<label htmlFor="password">Password</label>
<input type="password" className='signup-input' name="" value={password} onChange={(e) => setPassword(e.target.value)} id=""/>
<button type='button' className= "button" onClick={handleSignin}>Sign in</button>
</form>
</div>
</div>
)
};
export default Home;
Creating the chat interface
Let’s create a chat.jsx
file in the pages
folder. This file will contain the piece of code below.
const Chat = () => {
return (
<div className='chat'>
<div className='user-chat'>
<div className="user-chat-header">USER</div>
<div className='messages'>
</div>
<div className='input-area'>
<input type="text" className='message-input'/>
<button className='send' type='button'>send</button>
</div>
</div>
</div>
)
};
export default Chat;
In the global.css
file, we'll add styling to our chat interface.
https://gist.github.com/Iheanacho-ai/93956e8a24effe332efe44e4d15a8347
Here is how our chat app user interface looks.
Conclusion
This article discussed using Appwrite’s Account service to authenticate and sign a user into an application.