How To Build A Contact Manager Application With Next JS, Auth0 and Fauna
Tags: react, nextjs, fauna, javascript
Published: 2021-07-27 00:01:52


*Authored in connection with the [Write with Fauna](https://www2.fauna.com/write-with-fauna) program.*

# Introduction

This article will demonstrate how to build a contact manager with [Next.js](https://nextjs.org/) and [Fauna](https://fauna.com/) by walking you through the process of building a Google contact application clone. 

## What is NextJS?

Next.js is a React.js front-end framework with server-side capabilities, which makes it easy to build full-stack applications with

Some of its features and benefits include:


1. Static Site Generation (SSG)
2. Server-Side Rendering (SSR)
3. Pre-rendering
4. Better SEO 
5. Fast compilation times 
6. Automatic build size optimization

## Prerequisites:

1. Knowledge of [React](https://reactjs.org/) and JSX.
2. Basic knowledge of [Express.js](https://expressjs.com/)
3. Basic knowledge of [Next.js](https://nextjs.org/)
4. [npm](https://www.npmjs.com/) and [npx](https://www.npmjs.com/package/npx) installed
5. Installation of the [create-next-app](https://nextjs.org/docs/api-reference/create-next-app) CLI tool

## What you will learn in this article:

1. Next.js app setup
2. Routing on the client-side
3. Routing on the server-side
4. Authentication with Next.jsand Auth0
5. Creating Fauna databases, collections, and indexes
6. Building a fully functional app

# Setting Up A Next.js Application

To set up a next.js app, all we need to do is to run the following command in the terminal:

```sh
npx create-next-app $relativePathToDir # npx create-next-app 
```

This will create everything we need in the specified directory. You can look at the `package.json` file to check out the dependencies and scripts there.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5ld0jbex2gtd43ksg31.png)

As we can see, the `package.json` file has three scripts and three dependencies.

The `dev` command is used to start the app in development mode, while the `build` command is used to compile it. Meanwhile,the `start` command runs the app in production mode. Note, however, we need to compile our application before running it in production mode. 

The app also has three dependencies: `react`, `react-dom`, and `next` itself.

Now, let's run our app. To do this, we need to type `npm run dev` in the application's root directory. We should see the following:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpnyexhd92rzteiy2epc.png)

As we can see from the diagram above, there is are links to navigate from one site to another. We can also try to go to a random endpoint in the app. You should see the following, which is the default 404 page `Next.js` created for us: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4cd0r9lfs7222j1l2kq.png)

# Routing in NextJS

Unlike React.js, Next.js offers routing support out-of-the-box. In React.js, we need to install React Router dom to have routing abilities. However,with Next.js we do not need to do so. Rather, we just need to follow a particular syntax. Let's look at how we can handle both client-side and server-side routing in next js:

## Client-Side Routing

In your `pages` folder, you can create a file, and that file name will be the route's endpoint. For example, say I want to have a `/login` endpoint; all I need to do is create a `pages/login.js` file. The page will then show a return value of the exported component.

## Server-Side Routing

A folder called `api` should contain a file called `hello.js` with a simple express-like server in your `pages` folder. To test the API, go to the `api/hello` endpoint. You should see the following: `{"name": "John Doe"}`. That is the JSON object, which is sent as a response. Just as we route in the client, we create a file with the name we want to give the endpoint.

## Complex Routes

Say we want to create a route like `api/users/:userId`, where `userId` is dynamic, create a route like `api/users/contacts/follow`, or `api/users/:userId/follow/:friendId`. How can we achieve this?. 

Let's start with a route that is not dynamic – say `api/users/contacts/follow` or `/users/contacts/follow`. We need to chain it down using directories and sub-directories in our `pages` folder. 

To create the `/users/contacts/follow` route, we need to create a `pages/users/contacts/follow.js` file in our application.

We can create a dynamic route, on the other hand, by naming the file with the path parameter enclosed in a square bracket. Say, for example, we want to create a route `api/users/userId`, we need to just create a file `pages/api/users/[userId].js` 

To read more about routing in next.js, click [here](https://nextjs.org/docs/basic-features/pages).

# Authentication In Auth0 and NextJS

Handling authentication ourselves in some cases might not be a good idea because of security breaches. In this application, we'll be using **Auth0** for authentication. 

Let’s install the auth0js library for nextjs; in the terminal, we will have to type the following:

```sh
npm i @auth0/nextjs-auth0
```

If you do not have an auth0 account, create one [here](https://auth0.com/signup?place%3Dheader%26type%3Dbutton%26text%3Dsign%2520up). Head over to your dashboard and go to your applications page, then create a new application.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4rrxsqfmp2hibc25gaw.png)

As we're using NextJS, we need to select regular web applications. After creating the application, we should redirect to its settings page. Scroll down and edit the application URL as shown below, then save your changes. You can check auth0 next.js documentation [here](https://github.com/auth0/nextjs-auth0).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ye4z0r2rc6dql1js11yc.png)

## Connecting Auth0 and NextJS

We need to get the following from our auth0 dashboard:

```
AUTH0_SECRET=#random character
AUTH0_BASE_URL=<http://localhost:3000> #base URL of the application
AUTH0_ISSUER_BASE_URL=#Your domain
AUTH0_CLIENT_ID=#Your client id
AUTH0_CLIENT_SECRET=#Your Client Secret
```

To create environment variables in our next js app during development, we need to create a `.env.local` file in the root directory of our application. We need to create this file and pass in these values. Next js will parse the environment variables for us automatically, which we can use in the node environment of our app. 

If we want to access these variables in the browser, we need to prefix the name with `NEXT_PUBLIC_.` 

Now create a file called `pages/api/auth/[...auth0].js`, which will expose us to four different endpoints due to the fact that we’re destructuring the file: `api/auth/login`, `api/auth/callback`, `api/auth/me` and `api/auth/logout` that we can use in our application.

In the file you created, type the following:

```javascript
import { handleAuth } from '@auth0/nextjs-auth0';

export default handleAuth();
```

Also update your `pages/_app.js` file with the following:

```javascript
import { UserProvider } from '@auth0/nextjs-auth0';
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
    return (
        <UserProvider>
            <Component {...pageProps} />
        </UserProvider>
    );
}

export default MyApp

```

With these two things set up, we can have a login and logout button on our home page just to test the functionality of our app. Change the content of the `pages/index.js` file to the code snippet below:

```javascript
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

export default function Home() {
    return (
        <div className={styles.container}>
            <Head>
                <title>Create Next App</title>
                <meta name="description" content="Generated by create next app" />
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main className={styles.main}>
                <h1 className={styles.title}>
                Welcome to <a href="/">Next.js!</a>
                </h1>
                <p className={styles.description}>
                    <a className={styles.code} href="/api/auth/login">Get started</a> by Creating an account or logging in
                </p>
                <p className={styles.description}>
                    <a className={styles.code} href="/api/auth/logout">Logout</a>
                </p>
                <p className={styles.description}>
                    <a className={styles.code} href="/api/auth/me">Profile</a>
                </p>
                <p className={styles.description}>
                    <a className={styles.code} href="/api/auth/callback">callback</a>
                </p>
            </main>
            <footer className={styles.footer}>
                <a
                href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
                target="_blank"
                rel="noopener noreferrer"
                >
                    Powered by{' '}
                    <span className={styles.logo}>
                        <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
                    </span>
                </a>
            </footer>
        </div>
    )
}

```

The app should now look like this; try to navigate to a different part of the app using the links. Start by creating an account or logging in; you should see the following page:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31i0uptlh33fd5l1cybw.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c3z5uehovnbhbx35xxij.png)

After signing in in, click on the profile link You should get a JSON response showing your profile data:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sk82ykqly9o5zxgrcpt0.png)

Navigate to the callback and logout route to see what happens. 

Note that we won't be using the `api/auth/me` in the client-side of our app as `auth0` provided us with a hook called `useUser` which returns the same thing when the user is logged in, and it returns `null` when the user is logged out.

## Route Protection In Next JS and Auth0

It’s not enough to have an endpoint to log users in and out of the application; we need to be able to protect unauthenticated users from viewing some pages in the application and also restrict access to some APIs. Auth0 provides us with two functions that help ensure only authenticated users have access to a particular resource: `withApiAuthRequired` and `withPageAuthRequired`

These functions take in a callback function, while we use `withApiAuthRequired` in the API part of the app, and we use `withPageAuthRequired` in the components. 

Let's now look at how we can restrict unauthenticated users to get a resource from the endpoint `api/user` and the `dashboard` page.

We'll need to create the following files: `pages/api/user.js` and `pages/dashboard.js` We need to put the following in the `pages/api/user.js` file:

```javascript
import { withApiAuthRequired ,getSession } from "@auth0/nextjs-auth0"

export default withApiAuthRequired(async (req, res) => {
    const user = getSession(req, res).user // the getSession function is used to get the session object that's created in the app. Which is where auth data is kepy
        res.json({user})
    })

```

In our `pages/dashboard.js` file, let’s type the following:

```javascript
import { withPageAuthRequired, useUser } from '@auth0/nextjs-auth0'

const Dashboard = () => {
    const {user} = useUser()
    return (
        <div>
            <main>
                {user && (
                    <div>
                        <div>
                            {user.email} {!user.email_verified && <span>Your account is not verified</span>}
                        </div>
                    </div>
                )}
            </main>
        </div>
    )
}

export const getServerSideProps = withPageAuthRequired()

export default Dashboard

```

If you go to the dashboard endpoint without logging in, it redirects to the login page. Similarly, if you go to the `api/user` endpoint, it will return with an error message. We've successfully protected routes both on the client and server-side.

# Connecting Our Application To Fauna

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/700v0chdeeoy2re75jwm.png)

## Creating A Fauna Database

To create a Fauna database, head to the [dashboard](https://dashboard.fauna.com/).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udr4uat6l2vxbwyl1vu2.png)

Next, click on the `New Database` button, enter the database name, and click enter.

## Creating Fauna Collections

 A collection is a group of documents(rows) with the same or a similar purpose. A collection acts similarly to a table in a traditional SQL database.

In the app we’re creating, we'll have one collection `contacts`. The user collection is where we’ll be storing our contact data. 

To create these, click on the database you created and `New Collection`. Enter only the collection name (`contacts`), then click save.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxped08nw83p0c637ko1.png)

## Creating Fauna Indexes

Use indexes to quickly find data without searching each document in a database collection every time you need to access one. Indexes using one or more fields of a database collection. To create a Fauna index, click on the `indexes` section on the left of your dashboard.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/grpgfpc5ity4wijlw987.png)

In this application, we will be creating the one index which is the `user_contacts` index, this is used to retrieve all passwords created by a particular user.

## Generating Your Fauna Secret

 The Fauna secret key connects an application or script to the database, and it is unique per database. To generate it, go to your dashboard’s security section and click on `New Key.` Enter your key name, and a new key will be generated for you. Paste the key in your `.env.local` file in this format: `REACT_APP_FAUNA_KEY={{ API key }}`

# Building Our Application

First, we need to figure out the structure of our application. Our application will have the following endpoints:


1. `/`: home route
2. `/dashboard`: The dashboard route. Only authenticated users can access this page.
3. `api/contacts`: This is an API. It will support the `GET` HTTP method for getting all the contacts created by the user and the `POST` HTTP method for creating a new contact
4. `api/contacts/:contactId`: This is also an API which will support `GET`, `PUT` and the`DELETE` HTTP method for getting a single contact, updating it, and deleting a contact respectively.

Now we know the routes that we need to create and automatically we know the files we need to create to achieve this, we also need to have some components that will be used in the app. Thus, we will create a `components` folder in the root directory of our app and put each component there:


1. `Navbar`: This is the navbar of the app,. We Will create a file called `components/Navbar.js` for this.
2. `Contact`: This contains details of a single contact detail. We won't have a separate file for this.
3. `Contacts`: This will use the `Contact` component and display all the contacts created by the authenticated user. We will create a file called `components/Contacts` and put both the `Contacts` and `Contact` components there.
4. `BaseModal`: is the component we will build all our modals upon. We will place it in a file called `components/BaseModal.js`.
5. `CreateContact.modal`: is the component that creates a modal for creating a new contact. We will place it in a file called `CreateContact.modal.js`.
6. `EditContact.modal`: This is the component that creates a modal for editing a contact. We will add it to a file called `EditContact.modal.js`

We also need to have a file that handles the logic of database modeling, so we won’t have to be writing queries directly in the `api` folder. This file `models.js` will be in the root directory of our app.

We also need to install the remaining dependencies. Type the following in the root directory of your application:

```sh
npm i faunadb axios @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons @fortawesome/fontawesome-svg-core @fortawesome/fontawesome-free react-bootstrap
```

## Models

In your `models.js`, type the following

```javascript
import faunadb, {query as q} from 'faunadb'

const client = new faunadb.Client({secret: process.env.REACT_APP_FAUNA_KEY})

export const createContact = async (
    firstName, 
    lastName, 
    email, 
    phone,
    user, 
    jobTitle, 
    company,
    address,
    avatar
    ) => {
    const date = new Date()
    const months = [
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
    ]
    let newContact = await client.query(
        q.Create(
            q.Collection('contacts'),
            {
                data: {
                    firstName,
                    lastName,
                    email,
                    phone,
                    company,
                    jobTitle,
                    address,
                    avatar,
                    created__at: `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`,
                    user: {
                        id: user.sub
                    }
                }
            }
        )
    )
    if (newContact.name === 'BadRequest') return
    newContact.data.id = newContact.ref.value.id
    return newContact.data
}

export const getContactsByUserID = async id => {
    try {
        let userContacts = await client.query(
            q.Paginate(
                q.Match(q.Index('user_contacts'), id)
            )
        )
        if (userContacts.name === "NotFound") return
        if (userContacts.name === "BadRequest") return "Something went wrong"
        let contacts = []
        for (let contactId of userContacts.data) {
            let contact = await getContact(contactId.value.id)
            contacts.push(contact)
        }
        return contacts
    } catch (error) {
        if (error.message === 'instance not found') return []
        return
    }
}

export const getContact = async id => {
    let contact = await client.query(
        q.Get(q.Ref(q.Collection('contacts'), id))
    )
    if (contact.name === "NotFound") return
    if (contact.name === "BadRequest") return "Something went wrong"
    contact.data.id = contact.ref.value.id
    return contact.data
}

export const updateContact = async (payload, id) => {
    let contact = await client.query(
    q.Update(
        q.Ref(q.Collection('contacts'), id),
            {data: payload}
        )
    )
    if (contact.name === "NotFound") return
    if (contact.name === "BadRequest") return "Something went wrong"
    contact.data.id = contact.ref.value.id
    return contact.data
}

export const deleteContact = async id => {
    let contact = await client.query(
        q.Delete(
            q.Ref(q.Collection('contacts'), id)
        )
    )
    if (contact.name === "NotFound") return
    if (contact.name === "BadRequest") return "Something went wrong"
    contact.data.id = contact.ref.value.id
    return contact.data
}

```

The logic of this file is pretty straightforward. We have functions to create a new contact, get all contacts created by a user, obtain a single contact, update a single contact and delete a single contact. You might be wondering why we do not handle the user dB; well we do not need to in this case because we do not have a complex dB. We just need to be able to figure out the owner of a particular contact, and auth0 gives us access to the ID and email of the logged-in user, amongst other things.

## Components

### Navbar Component

In your `components/Navbar.js` file, type the following:

```javascript
import {
    Navbar, Nav
} from 'react-bootstrap'
import { useUser } from '@auth0/nextjs-auth0';
import Image from 'next/image';

const NavbarComponent = () => {
    const {user, isLoading, error} = useUser()
    return (
        <Navbar fixed="top" collapseOnSelect expand="lg" bg="dark" variant="dark">
            <Navbar.Brand className="mx-2 mx-md-4" href="/">Contact Manager</Navbar.Brand>
            <Navbar.Toggle aria-controls="responsive-navbar-nav" />
            <Navbar.Collapse className="d-lg-flex justify-content-end" id="responsive-navbar-nav">
                {(!user & !error) ? 
                    <>
                        <Nav.Link className="text-light" href="api/auth/login">Sign In </Nav.Link> : 
                        <Image alt="avatar" loader={myLoader} src={`https://ui-avatars.com/api/?background=random&name=John+Doe`} width="35" height="35" className="rounded-circle" />
                    </> :
                    <>
                        <Nav.Link className="text-light" href="/dashboard">Dashboard</Nav.Link>
                        <Nav.Link className="text-light" href="api/auth/logout">Sign Out</Nav.Link>
                        <Nav.Link href="/profile">
                            <Image alt="avatar" loader={myLoader} src={user.picture || `https://ui-avatars.com/api/?background=random&name=${firstName}+${lastName}`} width="35" height="35" className="rounded-circle" />
                        </Nav.Link>
                    </>
                }
            </Navbar.Collapse>
        </Navbar>
    )
}

const myLoader=({src})=>{
    return src;
}

export default NavbarComponent

```

We used the `useUser` hook here to determine if the user is logged in or not since we want to return things from this component dynamically. We also have a `myLoader` function at the bottom of the file, and this is because we are using the `Image` tag with a link.

### BaseModal Component

In your `components/BaseModal.js` file, type the following:

```javascript
import Modal from 'react-bootstrap/Modal'
import Container from "react-bootstrap/Container";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";

const BaseModal = (props) => {
    const onHide = () => {
        if (props.create) {
            props.updateFirstName('')
            props.updateLastName('')
            props.updateEmail('')
            props.updatePhone('' )
            props.updateAddress('')
        }
        props.onHide()
    }
    return (
        <Modal
        {...props}
        size="xlg"
        aria-labelledby="contained-modal-title-vcenter"
        centered
        onHide={onHide}
        >
            <Modal.Header closeButton>
                <Modal.Title id="contained-modal-title-vcenter">
                    {props.header && props.header}
                    {props.title && props.title}
                </Modal.Title>
            </Modal.Header>
            <Modal.Body className="show-grid">
                <Container>
                    <Form>
                        <Row>
                            <Form.Group as={Col} className='form-group'>
                                <Form.Control placeholder="First name" className='form-control' value={props.firstName} onChange={e => {props.updateFirstName(e.target.value)}}/>
                            </Form.Group>
                            <Form.Group as={Col} className='form-group'>
                                <Form.Control placeholder="Last name" className='form-control' value={props.lastName} onChange={e => {props.updateLastName(e.target.value)}}/>
                            </Form.Group>
                        </Row>
                        <Row>
                            <Form.Group as={Col}>
                                <Form.Control type="email" placeholder="Email" value={props.email} onChange={e => {props.updateEmail(e.target.value)}}/>
                            </Form.Group>
                        </Row>
                        <Row>
                            <Form.Group as={Col}>
                                <Form.Control type="phone" placeholder="Phone number(+2348180854296)" value={props.phone} onChange={e => {props.updatePhone(e.target.value)}}/>
                            </Form.Group>
                        </Row>
                        <Row>
                            <Form.Group as={Col}>
                                <Form.Control placeholder="Address" value={props.address} onChange={e => {props.updateAddress(e.target.value)}}/>
                            </Form.Group>
                        </Row>
                    </Form>
                </Container>
            </Modal.Body>
            <Modal.Footer>
                <Button variant="danger" onClick={onHide}>Close</Button>
                <Button variant="success" onClick={props.create ? props.handleCreate: props.handleEdit} disabled={(!props.firstName || !props.lastName || !props.phone) ? true : false}>{props.btnText}</Button>
            </Modal.Footer>
        </Modal>
    );
}

export default BaseModal

```

### Contacts and Contact component

In your `components/Contacts.js` file, type the following:

```javascript
import Image from 'next/image';
import Button from 'react-bootstrap/Button'
import Table from 'react-bootstrap/Table'
import { useState } from 'react'
import EditContactModal from './EditContact.modal'

const Contact = ({
    id,
    firstName,
    lastName,
    email,
    phone,
    address
    avatar,
    handleDelete,
    handleEdit
    }) => {
    const [editModal, setEditModal] = useState(false)
    const editContact = () => {
        setEditModal(true)
    }

    const deleteContact = () => {
        handleDelete(id)
        alert('Contact deleted successfully')
    }

    return (
        <tr>
            <td>
                <Image alt="avt" loader={myLoader} src={avatar} width="35" height="35" className="rounded-circle" />
            </td>
            <td>{firstName} {lastName}</td>
            <td>
                <a href={`mailto:${email}`}>{email}</a>
            </td>
            <td>
                <a href={`tel:${phone}`}>{phone}</a>
            </td>
            <td>{address}</td>
            <td><Button onClick={editContact}>Edit</Button></td>
            <td><Button onClick={deleteContact}>Delete</Button></td>

            <EditContactModal
            show={editModal}
            firstname={firstName}
            lastname={lastName}
            email={email}
            phone={phone}
            address={address}
            title={"Edit Contact for "+firstName}
            onHide={() => {
                let n = window.confirm("Your changes won't be saved...")
                if (n) setEditModal(false)
            }}
            onEdit ={(contact) => {
                contact.id = id
                handleEdit(contact)
                alert(`Contact for ${firstName} updated successfully`)
                setEditModal(false)
            }}
            />
        </tr>
    )
}

const Contacts = ({contacts, handleEdit, handleDelete}) => {
    return (
    <>
        {!contacts && 'Fetching contacts...'}
        <Table striped bordered hover responsive>
            <thead>
                <tr>
                <th>avatar</th>
                <th>Name</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Address</th>
                <th>Edit</th>
                <th>Delete</th>
                </tr>
            </thead>
            <tbody>
                {contacts.map(ele => <Contact {...ele} 
                    key={ele.id} 
                    handleEdit={handleEdit} 
                    handleDelete={handleDelete} />)} 
            </tbody>
        </Table>
    </>
    )
}

const myLoader=({src})=>{
    return src;
}

export default Contacts

```

### Create Contact Modal

In your `CreateContact.modal.js` file, type the following:

```javascript
import BaseModal from './BaseModal'
import { useState } from 'react'

const CreateContactModal = (props) => {
    const [firstName, setFirstName] = useState('')
    const [lastName, setLastName] = useState('') 
    const [email, setEmail] = useState('')
    const [phone, setPhone] = useState('') 
    const [address, setAddress] = useState('')

    const handleCreate = () => {
        const payload = {
            firstName,
            lastName,
            email,
            phone, 
            address
        }
        props.onCreate(payload)
    }

    return <BaseModal
        show={props.show}
        onHide={props.onHide}
        firstName={firstName}
        lastName={lastName}
        email={email}
        phone={phone}
        address={address}
        updateFirstName={newInput => setFirstName(newInput)}
        updateLastName={newInput => setLastName(newInput)}
        updateEmail={newInput => setEmail(newInput)}
        updatePhone={newInput => setPhone(newInput)}
        updateAddress={newInput => setAddress(newInput)}
        header="Create New Contact"
        btnText="Create"
        handleCreate={handleCreate}
        create={true}
    />
}

export default CreateContactModal

```

This component uses the `BaseModal.js` file and passes props to the component.

### Edit Contact Modal

In your `components/EditContact.modal.js` file, type the following:

```javascript
import BaseModal from './BaseModal'
import { useState } from 'react'

const EditContactModal = props => {
    const [firstName, setFirstName] = useState(props.firstname)
    const [lastName, setLastName] = useState(props.lastname) 
    const [email, setEmail] = useState(props.email)
    const [phone, setPhone] = useState(props.phone) 
    const [address, setAddress] = useState(props.address)

    const onEdit = () => {
        const payload = {
            firstName
            lastName,
            email,
            phone, 
            address
        }
        props.onEdit(payload)
    }

    return <BaseModal
        show={props.show}
        onHide={props.onHide}
        title={props.title}
        firstName={firstName}
        lastName={lastName}
        email={email}
        phone={phone}
        address={address}
        updateFirstName={newInput => setFirstName(newInput)}
        updateLastName={newInput => setLastName(newInput)}
        updateEmail={newInput => setEmail(newInput)}
        updatePhone={newInput => setPhone(newInput)}
        updateAddress={newInput => setAddress(newInput)}
        btnText="Edit"
        handleEdit={onEdit}
        create={false}
    />
}

export default EditContactModal

```

You might notice that the `pages,/index.js` file has a `Meta` tag.All pages should have their meta tag for SEO optimization. 

Let’s create a `components/MetaData.js` file:

### MetaData Component

In your `components/MetaData.js` file, type the following:

```javascript
import Head from 'next/head'

const MetaData = ({title}) => {
    return (
        <Head>
            <title>{`Contact Manager App ${title && "| " +title}`}</title>
            <meta name="description" content="A simple Contact Manager" />
            <link rel="icon" href="/favicon.ico" />
        </Head>
    )
}

export default MetaData

```

## API

Before we start creating our screens, it’s ideal for our backend to be complete since we will consume the APIs in the frontend of our app.

We need the following files for our API, excluding the auth endpoint:

1. `api/contacts` - we need to create a `pages/api/contacts.js` file

    a. GET - get all contacts.
    b. POST - create a new contact.

2. `api/contacts/:id` - we need to create a `pages/api/contacts/[id].js` file

    a. GET - get a single contact
    b. PUT - update a single contact
    c. DELETE - delete a single contacts

### Create and Get all contacts

In your `pages/api/contacts.js` file, type the following:

```javascript
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { withApiAuthRequired, getSession } from "@auth0/nextjs-auth0"
import { createContact, deleteContact, getContactsByUserID } from "../../models"

export default withApiAuthRequired(async (req, res) => {
    const user = getSession(req, res).user
    if (req.method === 'POST') {
        let {
            firstName, lastName, email,
            company, jobTitle, phone, address, avatar
        } = req.body
        let newContact = await createContact(
            firstName, lastName, 
            email, phone,
            user, jobTitle, 
            company, address, avatar
        )
        res.status(201).json({ 
            message: "Successfully created contact",
            data: newContact,
            status: 'ok'
    })
    } else if (req.method === 'GET') {
        let contacts = await getContactsByUserID(user.sub)
        if (!contacts) return res.status(400).json({
            message: 'Something went wrong',
            data: null,
            status: false
        })
        res.status(200).json({ 
            message: "Successfully retrieved contacts",
            data: contacts,
            status: 'ok'
        })
    } else {
        res.status(405).json({
            message: 'Method not allowed',
            data: null,
            status: false
        })
    }
})

```

In this file, we used the `getSession` function to get the current user from the request and response object. We then used this to set the contact creator and get contacts created by the user.

### UPDATE, DELETE and GET a single Contact

In your `pages/api/contacts/[id].js` type the following:

```javascript
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { withApiAuthRequired ,getSession } from "@auth0/nextjs-auth0"
import { deleteContact, getContact, updateContact } from "../../../models"

export default withApiAuthRequired(async (req, res) => {
    const user = getSession(req, res).user
    if (req.method === 'PUT') {
        let contact = await updateContact(
            req.body, req.query.id
        )
        res.status(201).json({ 
            message: "Successfully updated contact",
            data: contact,
            status: 'ok'
        })
    } else if (req.method === 'GET') {
        let contact = await getContact(req.query.id)
        res.status(200).json({ 
            message: "Successfully retrieved contact",
            data: contact,
            status: 'ok'
        })
    } else if (req.method === 'DELETE') {
        let contact = await getContact(req.query.id)
        if (contact.user.id !== user.sub) {
            return res.status(403).json({
                message: "Forbidden",
                status: false,
                data: null
            })
        }
        contact = await deleteContact(req.query.id)
        res.status(200).json({ 
            message: "Successfully deleted contact",
            data: contact,
            status: 'ok'
        })
    } else {
        res.status(405).json({
            message: 'Method not allowed',
            data: null,
            status: false
        })
    }
})
 
```

With this we have our API all set up. You can test it by going to different endpoints using an API testing tool, like Postman.

## Pages

Now we finished creating our components and APIs,, we need to create the pages and use the above.

### Index Page

Change the content of your `pages/index.js` file to the following:

```javascript
import Image from 'next/image';
import { useUser } from '@auth0/nextjs-auth0';
import MetaData from '../components/MetaData'
import styles from '../styles/Home.module.css'

export default function Home() {
    const { error, isLoading } = useUser();
    if (isLoading) return <div>Loading...</div>;
    if (error) return <div>{error.message}</div>;

    return (
        <div>
        <MetaData title="" />
        <main className={styles.container}>
            <Image className={styles.img} alt="home" src="/home.jpeg" width="400" height="200" />
        </main>
        </div>
    )
}

```

This page just returns an image as the content of the app. You might be wondering: where will our navbar be? So as not to call the navbar more than once, we’ll place it in our `pages/_app.js` file. Basically, this file is what is served, and it changes based on what is happening on the current page.

### 

### Dashboard Page

In your `pages/dasboard.js` file, type the following:

```javascript
import { useEffect, useState } from 'react'
import { withPageAuthRequired, useUser } from '@auth0/nextjs-auth0'
import Button from 'react-bootstrap/Button'
import axios from 'axios'
import CreateContactModal from '../components/CreateContact.modal'
import Contacts from '../components/Contacts'
import MetaData from '../components/MetaData'
import styles from '../styles/Home.module.css'

const Dashboard = () => {
    const {user} = useUser()
    const [contacts, setContacts] = useState([])
    const [createModalShow, setCreateModalShow] = useState(false);

    const handleHide = () => {
        let n = window.confirm("Your changes won't be saved...")
        if (n) setCreateModalShow(false)
    }

    useEffect(async () => {
        let res = (await axios.get(`/api/contacts`)).data
        res = await res.data
        setContacts(res.reverse())
    }, [])

    const createContact = async payload => {
        payload.avatar = `https://ui-avatars.com/api/?background=random&name=${payload.firstName}+${payload.lastName}`
        let newContact = (await axios.post(`/api/contacts`, payload)).data
        setContacts([newContact.data, ...contacts])
    }

    const editContact = async payload => {
        let id = payload.id
        delete payload.id
        let replacedContact = (await axios.put(`/api/contacts/${id}`, payload)).data
        setContacts(contacts.map(contact => contact.id === id? replacedContact.data : contact))
    }

    const deleteContact = async id => {
        (await axios.delete(`/api/contacts/${id}`)).data
        setContacts(contacts.filter(contact => contact.id !== id))
    }

    return (
        <div>
            <MetaData title="Dashboard" />
            <main>
                {user && (
                    <div className={styles.dashboardContainer}>
                        <div>
                            <img alt="avatar" src={user.picture} className="rounded-circle m-3" width="100" height="100"/> 
                            <span>Welcome {user.nickname.toLowerCase().charAt(0).toUpperCase()+user.nickname.toLowerCase().slice(1)}</span> 
                            {!user.email_verified && <div>Your account is not verified</div>}
                    </div>
                    <div>
                        <Button variant="primary" onClick={() => setCreateModalShow(true)}>
                            Create New Contact
                        </Button>
                        <CreateContactModal
                            show={createModalShow}
                            onHide={handleHide}
                            onCreate ={(payload) => {createContact(payload); setCreateModalShow(false)}}
                        />
                    </div>
                </div>
                )}
            </main>
            <Contacts 
                contacts={contacts}
                handleEdit={(id) => editContact(id)}
                handleDelete={(id) => deleteContact(id)} 
            />
        </div>
    )
}

export const getServerSideProps = withPageAuthRequired()

export default Dashboard

```

What is happening here is pretty straightforward: We are getting contacts the user created when the page loads, and we render it. We also show some details about the logged-in user, and we have a create contact button.

Before we can run our application, we need to make one change: we need to add the navbar to the `pages/_app.js` file.

### Root Component

Update the content of your `pages/_app.js` file with the following:

```javascript
import React, { useEffect, useState } from 'react'
import { UserProvider } from '@auth0/nextjs-auth0';
import axios from 'axios'
import MetaData from '../components/MetaData'
import NavbarComponent from '../components/Navbar'
import 'bootstrap/dist/css/bootstrap.min.css';
import '../styles/globals.css'

export default function App({ Component, pageProps }) {
    return (
        <UserProvider>
            <NavbarComponent />
            <Component {...pageProps} />
        </UserProvider>
    );
}

```

# Running Our Application

We have successfully built our application. Next, we need to run it in development mode. If you run your application, you should see the following:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s9brgzww94j86eicigdq.png)

After signing in, you should get redirected to the home page after signing in with the navbar being different.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ktkmpbmvfxnb6ejxbvm.png)

Go to the dashboard endpoint and create some contacts. Also, edit some of them and watch how the dashboard component is changing. You can also check the network request. You’ll notice that our Fauna secret key isn’t present as we handle this from the server.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ac73oqa8v8yyf3j3g15c.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkz3ijbyhwvwipq8ou6f.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2v0t2wm28zsvjwfpkqwj.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/086552jj97i5nqeht98m.png)

We’ve successfully tested our application.

# Next Steps

We have now built our application, but we're never really done developing it, as there's always room for improvement.. These are some of the things we can add to this application to make it look better:


1. We can improve the appearance of the UI 
2. We can add a PWA feature to our application
3. We can also create a profile page for the logged-in user, where they can update their profile.

# Conclusion

This article has offered a deep dive into Next.js and why we should use it in our projects. It also explains how to build a fully functional application with authentication features using NextJS and auth0 for authentication, and Fauna as our database provider. 

Do you have something to add to this project? kindly let me know. You can reach out to me via [Twitter](https://twitter.com/bkoiki950). If you like this project, kindly give it a star on [GitHub](https://github.com/Babatunde13/contact-manager-app). You can also check out the deployed app [here](https://contactify123.herokuapp.com/).