Build Nextjs app with a Restapify mocked API
Tags: react, nextjs, javascript, css
Published: 2021-03-13 17:24:45

Hey devs :wave:

Today I wanted to share how you can easily develop a Nextjs web application that consumes a REST API with the help of [Restapify](http://restapify.vercel.app/).

> If you didn't heard about Restapify before, I really encourage you to read the article [Quickly and easily mock a REST API with Restapify](https://dev.to/johannchopin/quickly-and-easily-mock-a-rest-api-with-restapify-16om) first.

So for this example, I will build an "Animal shelter" app where the user will be able to check a list of all the shelter residents, read more about a specific animal, be able to adopt one if possible and login/logout. To easily manage my layout and not recreate UI components I will use the [React Bootstrap](https://react-bootstrap.github.io/) library. The app will get the data from a REST API that is not yet created so I will create a mocked version of it with the API mocker library: Restapify. With this one I only need to define some API endpoints with simple JSON files. Theses are stored in the folder `mockedApi` so like the app file structure look like this:

```
📂 app
┣ 📂 components
┣ 📂 lib
┃ ┗ 📜 userContext.js
┣ 📂 mockedApi
┣ 📂 pages
┃ ┗ 📜 _app.js
┃ ┗ 📜 index.js
┣ 📜 next.config.js
```

Next thing to implement is a store for an [axios](https://github.com/axios/axios) instance to make our http request to the API which will be served from `http://localhost:6767/api`. So we create the file `/app/axiosStore.js`:

```js
import axios from 'axios'

const instance = axios.create({
  baseURL: `http://localhost:6767/api`
})

export default instance
```

We can then continue with the implementation of the animals page which is a list of all animals. This page will be served from the route `/animals`:

```
📂 app
┣ 📂 pages
┃ ┣ 📂 animals
┃ ┃ ┗ 📜 index.js
```

This page is going to fetch the endpoint `GET /animals` and render a list of all animals object which contain an `id` and a `name`:

```js
import React from 'react'
import Link from 'next/link'

import { ListGroup, ListGroupItem, Form, Row, Col } from 'react-bootstrap';

import api from '../../axiosStore'

const Animals = () => {
  const [animals, setAnimals] = React.useState(null)

  const fetchAnimals = () => {
    api.get('/animals').then((response) => {
      setAnimals(response.data)
    })
  }

  const renderAnimals = () => {
    return (
      <ListGroup className="px-3">
        {animals.map((animal) => {
          return (
            <Link key={animal.id} href={`/animals/${animal.id}`} style={{cursor: 'pointer'}}>
              <ListGroupItem action>{animal.id}. {animal.name}</ListGroupItem>
            </Link>
          )
        })}
      </ListGroup>
    )
  }

  React.useEffect(() => {
    fetchAnimals()
  }, [])

  return (
    <>
      <Row className='mb-3 p-3'>
        <Col><h1>Our Residents</h1></Col>
      </Row>
      {!animals 
        ? <p>Loading...</p> 
        : renderAnimals()
      }
    </>
    )
}

export default Animals
```

The interface is now ready so lets mock the API to serve `/animals` with Restapify so that we get back an array of 25 animals. For that, we just need to create a file `/mockedApi/animals/_.json` that contains the following JSON:

```json
[
  "#for i in range(25)",
  {
    "id": "n:[i]",
    "name": "[#faker:name:firstName]"
  },
  "#endfor"
]
```

Then we can serve the API using `restapify serve mockedApi/` and we directly see the result:

![Animals list screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/036hi7h8wesw3xkdpdko.png)

It's working fine for 25 animals but what if you want to show more or less animals in the list using the request `GET /animals?limit=x`. With Restapify you can mock this by using the [route's query string variables](https://restapify.vercel.app/docs#consume-routes-query-string-variables) `limit` instead of the hard written `25` number. So replace `"#for i in range(25)"` with `"#for i in range([q:limit])"` or `"#for i in range([q:limit|25])"` to have 25 as the default value. Now if you request `GET /animals?limit=42`, you will get back 42 animals.

We can now easily add a `Select` component to define and update the amount of animals to see in the interface:

```js
const Animals = () => {
  // ...

  const fetchAnimals = (limit) => {
    api.get(`/animals?limit=${limit}`).then((response) => {
      setAnimals(response.data)	
    })
  }

  // ...

  React.useEffect(() => {
    fetchAnimals(15)
  }, [])

  return (
    <>
      <Row className='mb-3 p-3'>
        <Col><h1>Our Residents</h1></Col>
        <Col lg="2" style={{display: 'flex'}}>
          Limit
          <Form.Control 
            className="ml-2 align-items-center" 
            as="select" 
            size="sm" 
            style={{width: 'fit-content'}} 
            onChange={e => { fetchAnimals(e.target.value) }}
          >
            {[15, 25, 50, 100].map(limit => (
              <option key={limit}>{limit}</option>
            ))}
          </Form.Control>
        </Col>
      </Row>
     // ...
```

![Animals list screenshot with select](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrtcikf0di6344mod8si.png)

If you click on one of the list item you are redirect to the detail page of an animal so lets create the corresponding page `/pages/animals/[animalid].js`:

```js
import { useRouter } from 'next/router'
import { Card, Row, Col, Button } from 'react-bootstrap';

import api from '../../axiosStore'

export default function Animal() {
  const router = useRouter()
  const { animalid } = router.query

  const [animal, setAnimal] = React.useState(null)

  const fetchAnimal = (id) => {
    api.get(`/animals/${id}`).then((response) => {
      setAnimal(response.data)
    })
  }

  const renderAnimal = () => {
    return (
      <Card style={{ width: '22rem', margin: 'auto' }}>
        <Card.Img variant="top" src={animal.avatar} />
        <Card.Body>
          <Card.Title>{animal.name}</Card.Title>
          <Card.Text>{animal.description}</Card.Text>
          <Button variant="primary">Adopt me</Button>
        </Card.Body>
      </Card>
    )
  }

  React.useEffect(() => {
    if (animalid) fetchAnimal(animalid)
  }, [animalid])

  return (
    <>
      <Row>
        <Col>
          {!animal 
            ? <p>Loading...</p> 
            : renderAnimal()
          }
        </Col>
      </Row>
    </>
  )
}
```

We just also need to mock the API endpoint `GET /animals/:id` by creating the file `/mockedApi/animals/[animalid]/_.json`:

```json
{
    "id": "n:[animalid]",
    "name": "[#faker:name:firstName]",
    "avatar": "[#faker:image:animals]",
    "description": "[#faker:lorem:paragraph]"
}
```

So when you navigate to `/animals/42` you will see your UI populated with some mocked data:

![Animal detail page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oko6xd4njmd9ptjw7yk9.png)

Lets continue with the adoption feature. When an user click on **Adopt me**, the request `PUT /animals/:id/adopt` should be send. The response can be a **204 No Content** if the adoption succeed or a **404 Not Found** if it's not possible to adopt him. So we can implement it in the frontend by using simple alert messages:

```js
export default function Animal() {

  // ...

  const adopt = () => {
    api.put(`/animals/${animal.id}/adopt`).then((response) => {
      if (response.status === 204) {
        alert(`You have adopted ${animal.name}`)
      }
    }).catch(({ response }) => {
      if (response.status === 404) alert(`Not possible to adopt ${animal.name}`)
    })
  }

  const renderAnimal = () => {
    return (
      <Card style={{ width: '22rem', margin: 'auto' }}>
        <Card.Img variant="top" src={animal.avatar} />
        <Card.Body>
          <Card.Title>{animal.name}</Card.Title>
          <Card.Text>{animal.description}</Card.Text>
          <Button variant="primary" onClick={adopt}>Adopt me</Button>
        </Card.Body>
      </Card>
    )
  }

  // ...
}
```

The again we just need to mock the `PUT /animals/:animalid/adopt` endpoint. So the default response will be a **204	No Content** so we can add the file `/mockedApi/animals/[animalid]/adopt.PUT.204.json` that contain only `[null]` because it doesn't respond anything (see the [no content response](https://restapify.vercel.app/docs#no-content-response) documentation). Then we need to create another [state](https://restapify.vercel.app/docs#routes-state) that mock a response **404 Not Found**. Just create for that the file `/mockedApi/animals/[animalid]/adopt.PUT.404.{ADOPTED}.json` that only contains `[null]`.

So if you click on the **Adopt me** button you will see the alert message **You have adopted <name>**. To now see how the interface handle the case where the animal can't be adopted just go the [restapify dashboard](https://restapify.vercel.app/docs#dashboard) web application (it should open in the browser after serving the API with Restapify) and select the state `ADOPTED` for the route `PUT /animals/:animalid/adopt`:

![State example in restapify dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52cxh567u6f4cdfir4un.gif)

If you now retry to adopt the animal, you should see the alert message **Not possible to adopt <name>**.

Using states with the dashboard is a really nice flow to test your interface for all cases in a snap.

You can find the complete example of it on GitHub if you want to play with it by your own: https://github.com/johannchopin/restapify-examples/tree/main/nextjs-animal-shelter-app. It also implement an user Authentication form that is mocked in the same way than the adopt feature.

If you have some question or some ideas about [Restapify](https://github.com/johannchopin/restapify) feel free to open a discussion or an issue :thumbsup:

If you like the process of mocking an REST API with Restapify and want to support it's development just leave a :star: on the GitHub [repo](https://github.com/johannchopin/restapify/stargazers).

{% github johannchopin/restapify %}