Authentication using Appwrite in React - Part 2
Tags: appwritehack, react, javascript, webdev
Published: 2022-05-09 04:25:11

Authenticating users is always a good thing. 
But creating our own authentication rules and functions and implementing them is a lot effort. 

<img src="https://media.giphy.com/media/VMtTNzgBjvlHG/giphy.gif" />

<h4>But why go through all the trouble when Appwrite is just around the corner? </h4>

In my previous post, I talked all about the setup process. But now it's time for the actual stuff!

So start your Favorite IDE (mine's VS Code), sip your coffee and let's get to work.

## Step 1 : Setup Appwrite Service

To start using Appwrite functions, we need to initialize the SDK.
And to do so, you'll need your `projectId` and `endpoint` (otherwise, Appwrite doesn't know who you are).

Since, we are using a  `.env` file to store these, we will have to import them.

> Note : Create a `services` folder and create a file `AppwriteService.js` file here.

Add the following code : 

```javascript
import { Appwrite } from "appwrite";

const config = {
    projectId : process.env.REACT_APP_APPWRITE_PROJECT,
    endpoint :  process.env.REACT_APP_APPWRITE_ENDPOINT,
};


const appwrite = new Appwrite();

class AppwriteService {
    constructor() {
        appwrite.setEndpoint(config.endpoint).setProject(config.projectId);
    }
}

export default AppwriteService;
```

As you can see, we are adding the endpoint and projectId for our project.

Now that we have an identity that Appwrite can recognize, we can proceed.

## Step 2 : Implementing Authentication APIs

When we talk about authenticating users, there are 3 steps involved :
 - Creating a User
 - Logging In a User 
 - Logging out a User

Namely, Signup, Login and Logout.
 - Instantiate an account property responsible for handling Auth API calls
 - define authentication methods for signup, login, and logout.

Update the Appwrite service file with this code.

```javascript
import { Appwrite } from "appwrite";

const config = {
    projectId : process.env.REACT_APP_APPWRITE_PROJECT,
    endpoint :  process.env.REACT_APP_APPWRITE_ENDPOINT,
    bucketId : process.env.REACT_APP_APPWRITE_BUCKET
};


const appwrite = new Appwrite();

class AppwriteService {
    constructor() {
        appwrite.setEndpoint(config.endpoint).setProject(config.projectId);

        this.account = appwrite.account;
    }

    createAccount = (email, name, password) =>{
        return this.account.create("unique()",email, password,name)
    }

    loginUser = (email, password) =>{
        return this.account.createSession(email,password);
    }

    logoutUser = () =>{
        return this.account.deleteSession('current');
    }

}

export default AppwriteService;
```

You can check out more about the functions above here 👉👉 [Accounts API](https://appwrite.io/docs/client/account)

One thing to notice here is the `unique()` string passed in the `account.create()` function above. This is used to specify that :

> You can use Appwrite to auto-generate a Unique `USER_ID` for each new user. You can also pass in your own UniqueId. But then again, _why do something that has already been taken care of_ ?

## Step 3 : Creating Different Components
Now that we have our functions ready, the next step would be to use them. 
Create components for Signup, Login and a Navbar that has the option to Logout. You can create these in your own designs.

The main task is to provide routing for different pages. We will use React router for this. So the first step would be install it in your project.
```bash
npm install react-router-dom@6
```

Then we'll have to specify the routes as follows : 
```javascript
import {Routes , Route } from 'react-router-dom';
import './App.css';
import Home from './Components/Home';
import Login from './Components/Login';
import Signup from './Components/Signup';
import Navbar from './Components/Navbar';

function App() {
  return (
      <div className="App">
      <Routes>
      <Route exact path={'/'} element={<><Navbar/>
        <Home/></>} />
        <ImageFilter/></>} />
      <Route exact path={'/signup'} element={<Signup/>}/>
      <Route exact path={'/login'} element={<Login/>}/>
    </Routes>
    </div>
  );
}

export default App;

```

Now that we are settled with the routes, we can move on to the actual implementation.

## Step 4 : Provide Appwrite In React

To use the Authentication methods, we need to have access to the Appwrite Service Class. 
 
A "simple" approach would be to create an Appwrite instance in every React component that needs to use Appwrite. However, this is a bad approach, for two reasons:

 - It would be difficult to test our components
 - It is more error prone. We will end up with multiple instances. Our Appwrite service class should only be initialized once (Simpleton Pattern)

<img src="https://media.giphy.com/media/LvqP06QcYTP9qOgxks/giphy.gif"/>

A better approach would be to use `React's Context API` to provide an Appwrite instance once at the top level of our component hierarchy. Create a new file src/context/Appwrite/index.js in your React project and add the following:

```javascript
import React from 'react';

const AppwriteContext = React.createContext(null);

export default AppwriteContext;
```

We will then create a well-encapsulated Appwrite module by defining a new file src/components/Appwrite/index.js which exports the `AppwriteService` class and `AppwriteContext`.

```javascript
import AppwriteContext from '../../context/Appwrite';
import Appwrite from '../../services/AppwriteService';

export default Appwrite;

export { AppwriteContext };
```

The React.createContext() method in src/context/Appwrite/index.js creates two components, AppwriteContext.Provider which is used to provide an Appwrite instance once at the top of our component tree and AppwriteContext.Consumer for every component which requires access to Appwrite.

We will use the AppwriteContext.Provider component to provide an Appwrite instance to the entire application by wrapping it around our root component in /src/index.js, like this:

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import Appwrite, {AppwriteContext} from './Components/Appwrite';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <BrowserRouter>
     <AppwriteContext.Provider value={new Appwrite()}>
        <App />
        </AppwriteContext.Provider>
    </BrowserRouter>
);
```

Here, Appwrite is instantiated once and injected into our component tree via React Context API. Now every component which requires access to Appwrite. We can do this using the `useContext` API provided by React. The code looks something like this : 
```javascript
import React, {useContext} from 'react';

import {AppwriteContext} from './components/Appwrite';

const SomeExampleComponent = () => {
    const appwrite = useContext(AppwriteContext);
   return (
    <div>This component has access to Appwrite.</div>
   );
}

export default SomeExampleComponent;
```

## Step 5 : Signup and Login
Now that we know how to use the Appwrite context, it's time to actually use it. 

For the Signup we need 3 things : Name, Email and Password. 
We'll use a form and states to get hold of the data. 

A simple Signup form along with states and `onChange` event listeners, we are ready to use the Appwrite `createAccount` method. Since, it returns a promise, we need to use a then and catch block. 

But for this to work, we have to import the Appwrite context and `consume` it. As discussed above, we implement it using the `useContext` API. 

The code would look something like this : 
```javascript
import React, { useContext, useState } from 'react'
import SignupImg from "../Assets/Signup.png"
import "../Css/Signup.css"
import Pig from "../Assets/pig.png"
import { useNavigate } from 'react-router-dom';
import { AppwriteContext } from './Appwrite';

function Signup() {
    const [name,setName] = useState("");
    const [email,setEmail] = useState("");
    const [password,setPassword] = useState("");

    const navigator = useNavigate();
    const appwrite = useContext(AppwriteContext);

    const handleSignup = (e) => {
        e.preventDefault();
        if(name === '' || email==='' || password === ''){
            alert('All fields are required');
            return;
        }

        appwrite.createAccount(email, name, password).then((res) =>{
            console.log('Success', res);
            window.location="/"
        }).catch((error) =>{
            console.log('Error', error);
        })
    }
   return (
    <div className='Signup'>
        <div className='Signup-left'>
        <div className='signup-home'>
                <img className='signup-home-btn' onClick={()=>{
                    navigator("/");
                }} src={Pig} alt="pigshell"/>
            </div>
            <div className='Signup-head'>
                <div>Sign Up</div>
                <div className='Signup-subhead'>
                    Create account to access images from anywhere
                </div>
            </div>
            <div className='Signup-card'>
                <form className='Signup-form'>
                    {/* <label for="name">Your Name</label> */}
                    <input className='Signup-input' name='name' placeholder='Name' id='signup-name' autoComplete='off' value={name} onChange={(e)=>{
                        setName(e.target.value);
                    }}/>
                    <input className='Signup-input' placeholder='Email' autoComplete='off' id='signup-email' value={email} onChange={(e)=>{
                        setEmail(e.target.value);
                    }}/>
                    <input className='Signup-input' placeholder='Password' type='password' autoComplete='off' id='signup-password' value={password} onChange={(e)=>{
                        setPassword(e.target.value);
                    }}/>
                    <button type="submit" id='signup-btn' 
                    onClick={handleSignup}>Create Account</button>
                </form>
            </div>
            <div className='Signup-footer'>
                <p>Already have account? <a className='login-redirect highlight-text' href='/login'>Login Now</a></p>
            </div>
        </div>
        <div className='Signup-right'>
        <div className='Signup-welcome'>
        <h2>Welcome to PigShell</h2>
            <p>Start your journey full of Piggy Awesomeness!</p>
        </div>
        <div className='Signup-img'>
            <img src={SignupImg} alt='signup'/>
        </div>
        </div>
    </div>
  )
}

export default Signup
```
With some CSS, the page would look like this : 

![Signup Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mipxnmzdfusyx4xqvru0.png)

Do let me know, if you like the design!!

The code for using the Appwrite create account method is as below : 
```javascript
const handleSignup = (e) => {
        e.preventDefault();
        if(name === '' || email==='' || password === ''){
            alert('All fields are required');
            return;
        }

        appwrite.createAccount(email, name, password).then((res) =>{
            console.log('Success', res);
            window.location="/"
        }).catch((error) =>{
            console.log('Error', error);
        })
    }

```

Similarly, we can implement the Login function. 
The component code it something like this : 
```javascript
import React, { useContext, useState } from 'react'
import LoginImg from "../Assets/Signup.png"
import "../Css/Login.css"
import Pig from "../Assets/pig.png"
import { useNavigate } from 'react-router-dom';
import { AppwriteContext } from './Appwrite';

function Login() {
    const [email,setEmail] = useState("");
    const [password,setPassword] = useState("");

    const navigator = useNavigate();
    const appwrite = useContext(AppwriteContext);

    const handleLogin = (e) => {
        e.preventDefault();
        appwrite.loginUser(email,password).then((res) =>{
            console.log("Logged In!", res);
            window.location = "/"
        }).catch((error) =>{
            console.log("Error logging in", error);
        })
        // console.log({ email : email, password : password});
    }

   return (
    <div className='Login'>
        <div className='Login-left'>
            <div className='login-home'>
                <img className='login-home-btn' onClick={()=>{
                    navigator("/");
                }} src={Pig} alt="pigshell"/>
            </div>
            <div className='Login-head'>
                <div>Log In</div>
                <div className='Login-subhead'>
                    Login to view your images
                </div>
            </div>
            <div className='Login-card'>
                <form className='Login-form'>
                    {/* <label for="name">Your Name</label> */}
                    <input className='Login-input' placeholder='Email' autoComplete='off' id='login-email' value={email} onChange={(e)=>{
                        setEmail(e.target.value);
                    }}/>
                    <input className='Login-input'  placeholder='Password' type='password' autoComplete='off' id='login-password' value={password} onChange={(e)=>{
                        setPassword(e.target.value);
                    }}/>
                    <button type="submit" onClick={handleLogin} id='login-btn'>Log In</button>
                </form>
            </div>
            <div className='Login-footer'>
                <p>Don't have account? <a className='login-redirect highlight-text' href='/signup'>Signup Here</a></p>
            </div>
        </div>
        <div className='Login-right'>
        <div className='Login-welcome'>
        <h2>Welcome Back</h2>
            <p>We were missing your Piggy Awesomeness!</p>
        </div>
        <div className='Login-img'>
            <img src={LoginImg} alt='login'/>
        </div>
        </div>
    </div>
  )
}

export default Login
```

![Login Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mcod7tx2zmk3g6u2vai6.png)

Check out the whole code here : [Pigshell 🐷](https://github.com/thevinitgupta/Pigshell) 

Now that we are ready with the Login and Signup functionalities, we can implement the other parts of the Application that we are developing. Like an Image filter, that converts ordinary images into Pixel images. 

Will be available to use really soon 😉😉.