Building an Admin Dashboard with React-admin
Tags: react, reactadmin, adminpanel, admindashboard
Published: 2021-04-30 10:31:19

---
title: Building an Admin Dashboard with React-admin
published: true
description: 
tags: react, reactadmin, adminpanel, admindashboard
//cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqk3cap5vtifc4e3y1au.png
---

{% youtube 4y2FFEmPW7I %}

React admin has been one of the holy grail frontend frameworks for building responsive admin panels. It offers a lot of really cool features such as data validation, optimistic rendering, accessibility, and action undo. React-admin is also plug-and-play as it supports standard REST APIs and a handful of GraphQL dialects. Being a Reactjs framework, it also gives you access to thousands of plugins and libraries available in Javascript and the React ecosystem.

In this article, I would like to show you how to build an admin panel using React-admin. 

We’re going to be building a dashboard to manage DVD movie rentals for a local rental store. The first page would have a table listing all registered members of the store. The second page will have a table that holds all rental records. From here, new rental entries can be created and existing rentals can be updated i.e from borrowed to returned. We would also be able to click on a customer from the first page and then be taken to the rentals page to see his rental history.

Here’s a gif and a link to the completed application

![1-completed dashboard gif](https://media.giphy.com/media/0uAcb3eNqZYV8uQnsD/giphy.gif)

You can view the demo app here

**Dashboard link**: https://as-react-admin.netlify.app 

**username**: cokoghenun@appsmith.com 

**password**: 123456


Through building this dashboard, we’re going to cover core React-admin concepts such as
* Resources
* List view
* Edit/Create view
* Reference inputs and
* Authentication

Since React-admin requires an API server we would need to build one on top of the database. Speaking of the database, we’ll be making use of MongoDB and the demo dataset is a modified version of the [Sakila dataset](https://github.com/jOOQ/jOOQ/tree/main/jOOQ-examples/Sakila).

To save time and get to the fun part of building the dashboard with React-admin, we’ll be making use of [Loopback](https://loopback.io/) to generate a Nodejs API over the database. If you are not familiar with Loopback, it is a highly extensible Node.js and TypeScript framework for building APIs and microservices.

>You can skip this if you already have an API to use

We’re almost set. But before we begin, I’d like to give you a mini-map of the entire article. The first part of this article will focus on generating an API server over the database on MongoDB using Loopback. The second part of this article would cover how to use React-admin to build a dashboard from the API generated in the first section.

Alright, everything looks good. Let’s get started!

# Generating an API server
There are many ways to build an API server. You can roll up your sleeves and build one yourself(this takes a lot of time) or you can choose to go with a framework. Loopback is the fastest framework I found to build Nodejs APIs over a database. It supports a host of databases ranging from in-memory to document to relational databases.

The API that would be generated using Loopback will have three resources, the first being the `customer` resource that represents customers who come to rent DVDs from the store. We also have the `film` resource, representing DVDs that are in stock. Lastly, we have the rentals resource, which records each rental.

Here’s the schema for each resource

```js
// Customer resource
{
  "store_id": String,
  "first_name": String,
  "last_name": String,
  "email": String,
  "address_id": String,
  "activebool": Boolean,
  "create_date": Date,
  "last_update": Date,
  "active": Number
}


// Film resource
{
  "title": String,
  "description": String,
  "release_year": Number,
  "language_id": String,
  "rental_duration": Number,
  "rental_rate": Number,
  "length": Number,
  "replacement_cost": Number,
  "rating": String,
  "last_update": Date,
  "special_features": String,
  "fulltext": String
}

// Rental resource
{
  "status": String,
  "rental_date": Date,
  "film_title": String,
  "customer_email": String,
  "return_date": Date,
  "staff_id": String,
  "last_update": Date
}
```
 
Okay! Now let’s get started by install Loopback CLI with npm
```sh
npm i -g @loopback/cli
```

We can easily scaffold the Nodejs server using the Loopback CLI. It configures a Typescript compiler and installs all required dependencies. Let’s run the CLI and answer a few prompts to generate a new app

```sh
lb4 app
```
You should have your app configured as shown below


![2-configuring loopback app.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626405623/ISaHiauAm.png)

Hit enter and give the CLI some time to set up the app. 

## Creating a model
Now that the loopback app has been scaffolded, `cd` (change directory) into the app folder, and let’s start by creating a model for each resource. A model communicates the shape of each document for a particular resource, much like the schema shown earlier.

Let’s create a model for the `customer` resource using the Loopback CLI

```sh
lb4 model
```
As we did when generating the app, answer the CLI prompts. Yours should look like this


![3-configuring the customer model.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626424895/QYxyr1ie4.png)

Great Job! Now, go ahead and do the same for the `film` and `rental` resources. Don’t forget that to create a new model, you’ll need to run the `lb4 model` command.

## Connecting to the database
Next, we’ll need to link the Loopback app to the Mongo database. Loopback provides two entities to help us accomplish this, and they are the `datasource` and `repository` mechanisms.

A datasource represents a database connection that would be used to store and retrieve documents from the database i.e MongoDB or PostgreSQL. On the other hand, a repository links a resource on the Loopback app to a particular table or collection in the database. For example, the `customer` resource is linked to the `Customer` collection in the database using a repository.

Now, let’s add a datasource to the app, and link it to our MongoDB database. We can easily do this using the CLI command below

```sh
lb4 datasource
```
As usual, go ahead and answer the CLI prompts, supplying the database credentials to the CLI


![4-configuring the mongo datasource.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626447684/CCvCB44nx.png)

Awesome! Now we can add a `repository` for each resource. 

Run the command below and let’s set up a repository for the `customer` resource. Notice that we have to link the created resource to the target resource, and in this case, it is the `customer` resource

```sh
lb4 repository
```


![5-creating a repository.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626460569/W-Aq0NH0l.png)

Cool! Go ahead and do the same for the `film` and `rental` repositories. I’m confident you can finish up on your own 😜 

## Adding CRUD functionality
Great Job! That was a lot we just covered. Right now, we have models for each resource, a datasource, and repositories linking each model to its respective collection in the database.

The last piece of the puzzle is to add CRUD functionality for each resource. 

We can do this by creating controllers. Controllers do the grunt work of creating, reading, updating, and deleting documents for each resource. 

As you may have already guessed, we can create a controller using the `controller` command. Now, let’s create a  REST controller for the `customer` resource. Notice we’ll need to use the model and repository created earlier for the `customer` resource. 

```sh
lb4 controller
```

>Note that the Id is a string and is not required when creating a new instance


![6-creating a controller.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626486145/lZWuO2S9M.png)

As usual, go ahead and do the same for the `film` and `rental` resources.

Awesome! We now have a full-blown REST API that was generated in a few minutes. Open up the project folder in your favorite code editor and you’ll see all the code(and folders) generated by Loopback. 


>I recommend you change the default port in the `index.ts` file to something else i.e 4000 because Create React App (used by React-admin) runs by default on port 3000


You can start the server using the `start` script

```sh
npm start
```
You can find a playground and the auto-generated API documentation for your server by visiting the server address on your browser i.e `http://localhost:4000/` 


![7-api explorer.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626511658/Ekd3RtC5Z.png)

Alright! Now we have a REST API server with CRUD functionality, we can move on with creating the admin dashboard for using React-admin.

# Enter React-admin
We’ve finally gotten to the fun part, yay!

As a quick recap, we have a Loopback API generated in the last section that serves the `customer`, `film`, and `rental` resource with the following endpoints and data schema

```js
// /customers endpoint
{
  "store_id": String,
  "first_name": String,
  "last_name": String,
  "email": String,
  "address_id": String,
  "activebool": Boolean,
  "create_date": Date,
  "last_update": Date,
  "active": Number
}


// /films endpoint
{
  "title": String,
  "description": String,
  "release_year": Number,
  "language_id": String,
  "rental_duration": Number,
  "rental_rate": Number,
  "length": Number,
  "replacement_cost": Number,
  "rating": String,
  "last_update": Date,
  "special_features": String,
  "fulltext": String
}

// /rentals endpoint
{
  "status": String,
  "rental_date": Date,
  "film_title": String,
  "customer_email": String,
  "return_date": Date,
  "staff_id": String,
  "last_update": Date
}
```

So here’s the game plan. We’re going to use this API to build a dashboard to manage DVD movie rentals. The first page would be a table showing all customers. Then we can click on a customer and view all his rentals on a new page. We can update the return date and status of each rental i.e from borrowed to returned. Lastly, we can view all rentals on the rentals page and create a new entry or edit an existing one.

Phew! Now we can finally begin with React-admin 😅


React-admin is a powerful front-end framework for building admin panels and dashboards. It is highly customizable and has a host of other great features. Since it is based on Reactjs, it can be used with thousands of other Reactjs and Javascript libraries.

React admin requires a base Reactjs project. We are going to be going with Create-React-App (CRA) in this article. So let’s set up the project with CRA

```sh
npx create-react-app rental-admin-panel
```

Give the CLI some time to install all dependencies and finish setting up the project. Then, `cd` into the project directory and go-ahead to install React-admin and the Loopback dataprovider.

```sh
npm install react-admin react-admin-lb4
```

A `dataProvider` is the mechanism with which React-admin communicates with a REST/GraphQL API. The Loopback provider for React-admin enables it to understand and use Loopback APIs i.e how to paginate or filter requests. If you aren’t using a Loopback generated API, you should look into using [one of these dataProviders for React-admin.](https://marmelab.com/react-admin/DataProviders.html#available-providers)


Open up the project in your favourite code editor and replace everything in the `App.js` file with the below starter code

```js
//src/App.js

import React from 'react';
import lb4Provider from 'react-admin-lb4';
import { Admin, Resource } from 'react-admin';

function App() {
  return (
    // ------------- Replace the below endpoint with your API endpoint -------------
    <Admin dataProvider={lb4Provider(“http://localhost:4000”)} >
      <Resource name='customers' />
    </Admin>
  );
}

export default App;

```
So far so good. But we have some new concepts to clear up. In the starter code above, we supply a dataProvider to React-admin which enables it to query the API. The next thing we did up there is to register a resource from the API that we would like to use in React-admin. This is done simply by supplying the endpoint as a name prop to the `<Resource>` component.

>You don’t need to add the forward-slash “/” to the resource name

Going by this rule, we must register it as a resource whenever we need to query a new API endpoint. In this way, React-admin becomes aware of it. Moving on...

## Creating the Customers' table
The easiest way to view all customers’ info is to have a paginated table displaying all customers’ info. React-admin makes it easy to do this by providing us with a `<List>` component.

The `<List>` component generates a paginated table that lists out all documents in a particular resource. We can choose which fields we want to show up on the table by wrapping them in the appropriate `<Field>` component i.e a date property on a document would be wrapped in a `<DateField>` component. 

The data property on the document is linked to the `<Field>` component using the `source` prop. This prop must contain the exact property name. And the field name showing up on the table can be customized using the `label` prop.

We can also create a `filter` for the table using the `<Filter>` component and specify an action to be triggered whenever an item is clicked on the table using the `rowClick` props on the `<Datagrid>` component. You can learn more about filtering [here](https://marmelab.com/react-admin/List.html#filtering-the-list) and row actions [here](https://marmelab.com/react-admin/List.html#rowclick)
 
Alright! So we want a `customer` table to show all the customers. We also want this table to be filterable by customer email. Lastly, we want to be able to click on a customer and see all his rentals (we haven’t created the rentals page yet, but we will shortly).

Let’s see all of this in action. Go ahead to create a `customer` list component with the following content

```js
//src/components/CustomerList.js

import React from 'react';
import { List, Filter, Datagrid, TextField, SearchInput, } from 'react-admin';

// ------------- filter component which filters by customer email -------------
const CustomerFilter = (props) => (
  <Filter {...props}>
    <SearchInput placeholder='Customer Email' source='email' resettable alwaysOn />
  </Filter>
);

const CustomerList = (props) => (
  <List {...props} filters={<CustomerFilter />} title='List of Customers'>
// ------------- rowclick action that redirects to the rentals of the selected customer using the customer id -------------
    <Datagrid
      rowClick={(id, basePath, record) => {
        return `/rentals?filter=%7B%22customer_email%22%3A%22${record.email}%22%7D&order=ASC&page=1&perPage=10&sort=film_title`;
      }}
    >
      <TextField disabled source='id' />
      <TextField source='first_name' />
      <TextField source='last_name' />
      <TextField source='email' />
      <TextField source='activebool' label='Active' />
    </Datagrid>
  </List>
);

export default CustomerList;

```

Next, we need to link the `<CustomerList>` component with the `customer` resource component.

```js
//src/App.js

// ------------- import CustomerList -------------
import CustomerList from './components/CustomerList'; 

//…

// ------------- use CustomerList as the list view on the customer resource -------------
<Resource name='customers' list={CustomerList} />
```

Save your code and let’s head over to the browser. You can see we have a nice paginated, and filterable `customer` table that has been automatically generated and is rendering customer information from the API. Cool right? 😎 


![8-customer table ui.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626613452/H-Pi6iwMC.png)

Not so fast! Go ahead and create a similar list table for the `rental` resource. You can name this component `RentalList`. If you are curious or get stock, feel free to fall back on the [code here](https://github.com/cokoghenun/react-admin-dashboard/blob/main/src/components/RentalList.js).

## Creating and Editing a Rental
We have two more views to create and they are the edit and create view for the `rental` resource. They are quite similar to each other and are both similar to the list view but with a few differences.

The edit view would be used to edit an item clicked on the `rental` table. 

>To wire up this behaviour ensure that you have `rowClick='edit'` on the `<Datagrid>` component in `<RentalList>`

An edit view uses a `<SimpleForm>` component, which in reality is a simple form with nested `<Input>` components. Like with the `<Field>` components, each `<Input>` component used is based on the data type of the property to be edited i.e a `<TextInput>` component is used on a text property. Inputs also require the `source` props and optional `label` props as we’ve already seen with the `<Field>` component.

Bringing it all together, the edit view for the `rental` resource would look like this:

>Notice that some inputs have been disabled using the `disabled` props

```js
// src/components/RentalEdit.sj
import React from 'react';
import {
  Edit,
  SimpleForm,
  TextInput,
  DateTimeInput,
  SelectInput,
} from 'react-admin';

const RentalEdit = (props) => (
  <Edit {...props} title='Edit of Rentals'>
    <SimpleForm>
      <TextInput disabled source='id' />
      <TextInput disabled source='film_title' />
      <TextInput disabled source='customer_email' />
      <DateTimeInput disabled source='rental_date' />

      <SelectInput
        source='status'
        choices={[
          { id: 'borrowed', name: 'borrowed' },
          { id: 'delayed', name: 'delayed' },
          { id: 'lost', name: 'lost' },
          { id: 'returned', name: 'returned' },
        ]}
      />
      <DateTimeInput source='return_date' />
    </SimpleForm>
  </Edit>
);

export default RentalEdit;
```

Don’t forget to import and use the edit view in the `rental` resource component in your `App.js` file.

```js
//src/App.js

// ------------- import RentalEdit' -------------
import RentalEdit from './components/RentalEdit'; 

//…

// ------------- use RentalEdit as the edit view on the rental resource -------------
 <Resource name='rentals' list={RentalList} edit={RentalEdit}/>
```

Save your files and let’s head to the browser. Click on an order to see the magic!


![9-edit demo gif.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626689040/pJlWe-8UC.gif)

Okay, so we’ve completed the edit view. Now moving on to make the create view.

The create view is quite similar to the edit view. It’s so similar that I’m just going to paste the code right here and you wouldn’t be able to tell the difference. Just kidding 😜. Anyway, here’s the code for the create view

```js
// src/components/RentalCreate.js
import React, { useState, useEffect } from 'react';
import {
  Create,
  SimpleForm,
  DateTimeInput,
  SelectInput,
  useNotify,
  useRefresh,
  useRedirect,
  useQuery,
  TextInput,
} from 'react-admin';

const RentalCreate = (props) => {
  const notify = useNotify();
  const refresh = useRefresh();
  const redirect = useRedirect();

  const onSuccess = ({ data }) => {
    notify(`New Rental created `);
    redirect(`/rentals?filter=%7B"id"%3A"${data.id}"%7D`);
    refresh();
  };

  const [customers, setCustomers] = useState([]);
  const { data: customer } = useQuery({
    type: 'getList',
    resource: 'customers',
    payload: {
      pagination: { page: 1, perPage: 600 },
      sort: { field: 'email', order: 'ASC' },
      filter: {},
    },
  });

  const [films, setFilms] = useState([]);
  const { data: film } = useQuery({
    type: 'getList',
    resource: 'films',
    payload: {
      pagination: { page: 1, perPage: 1000 },
      sort: { field: 'title', order: 'ASC' },
      filter: {},
    },
  });


  useEffect(() => {
    if (film) setFilms(film.map((d) => ({ id: d.title, name: d.title })));
    if (customer)
      setCustomers(customer.map((d) => ({ id: d.email, name: d.email })));
  }, [film, customer]);

  return (
    <Create {...props} title='Create new Rental' onSuccess={onSuccess}>
      <SimpleForm>
        <TextInput disabled source='staff_id' defaultValue='1' />
        <SelectInput source='customer_email' choices={customers} />
        <SelectInput source='film_title' choices={films} />
        <SelectInput
          source='status'
          defaultValue='borrowed'
          disabled
          choices={[
            { id: 'borrowed', name: 'borrowed' },
            { id: 'delayed', name: 'delayed' },
            { id: 'lost', name: 'lost' },
            { id: 'returned', name: 'returned' },
          ]}
        />

        <DateTimeInput source='rental_date' />

        <DateTimeInput source='return_date' />
      </SimpleForm>
    </Create>
  );
};

export default RentalCreate;
```
The only difference here is that we have two select inputs that display a list of all customers and films by manually querying those resources.

Instead of writing custom logic to query the `customer` and `film` resources, we could have easily use the [built-in `<ReferenceInput>` component](https://marmelab.com/react-admin/Inputs.html#referenceinput). But currently, there's no way to set the selected value from the `<SelectInput>` component to something other than the document id. In the create form, we require the `email` field from the `customer` resource and the `title` field from the `film` resource. That is why we are manually querying, else the `<ReferenceInput>` component would have been awesome.

Do not forget to import and use the create view we just made. Also, register the `film` resource in `App.js`

```js
//src/App.js

// ------------- import RentalCreate -------------
import RentalCreate from './components/RentalCreate';


//…

// ------------- use RentalCreate as the create view on the rental resource -------------
 <Resource name='rentals' create={RentalCreate} list={RentalList} edit={RentalEdit}/>
// ------------- register the film resource -------------
 <Resource name='films'/>
```
>If a resource is registered and no list view is passed to it, React-admin hides it from the navbar. But the resource is still useful for querying as we did for the `film` select input in the `<RentalCreate>` component.

This is the moment you’ve been waiting for! Save your files and head over to the browser. You’ll notice that we now have a create button on the rentals table, and clicking on a rental takes you to edit that rental. Sweet!


![10-create rental gif.gif](https://media.giphy.com/media/lNeKGL6E3C6o9Ukb2D/giphy.gif)

We’ve finally completed the dashboard!  🥳 🎉 🎊


We have a complete admin panel to manage rentals. We can see a list of customers, select a customer and view all his orders and lastly, we can create new rental entries or edit existing ones. Awesome!

For some extra credit, let's add some authentication.

# Extra credit: Authentication
You must add some authentication to your apps, else anyone would be able to use it, even malicious individuals! Thankfully, adding authentication to our API and admin dashboard is not too difficult.

The first part of this section will focus on adding authentication to the Loopback API. You can skip this if you’ve been following along with your API. Next, we’ll look at implementing auth on the React-admin panel.

## Securing the API
Loopback has various authentication strategies that we can implore to secure the API. We are going to be going with the JWT authentication strategy, mostly because it’s super easy to set up and is fully supported by React-admin.

Enough talk, let's get started by installing the JWT auth extension library and Validatorjs on the Loopback API server.

```sh
npm i --save @loopback/authentication @loopback/authentication-jwt @types/validator
```
Next, bind the authentication components to the application class in `src/application.ts`

```js
// src/appliation.ts

// ----------- Add imports -------------
import {AuthenticationComponent} from '@loopback/authentication';
import {
  JWTAuthenticationComponent,
  SECURITY_SCHEME_SPEC,
  UserServiceBindings,
} from '@loopback/authentication-jwt';
import {MongoDataSource} from './datasources';
// ------------------------------------

export class TodoListApplication extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
  constructor(options: ApplicationConfig = {}) {

    //…

    // ------ Add snippet at the bottom ---------

    // Mount authentication system
    this.component(AuthenticationComponent);
    // Mount jwt component
    this.component(JWTAuthenticationComponent);
    // Bind datasource
    this.dataSource(MongoDataSource, UserServiceBindings.DATASOURCE_NAME);
    // ------------- End of snippet -------------
  }
}
```
Great job! We now have a foundation for auth.

Authentication usually works by validating the credentials of the user attempting to sign in and allowing him to go through if valid credentials are supplied. Thus, we’ll then need to create a `user` resource to represent a user. For our purposes, a user only has an id and an email field. 

Alright, let’s create the `user` model using the Loopback CLI. Answer the CLI prompts as usual

```sh
lb4 model
```

![11-user model.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626801174/lTSYfDdH-.png)

We’ll also need to create a controller for the `user` resource that handles all authentication logic. You can use the CLI to generate an empty controller.

>Note that this controller would need to be an empty controller and not a REST controller

```sh
lb4 controller
```

![12-user controller.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626827054/Z7B-hBLne.png)

The generated empty controller file can be found in `src/controllers/user.controller.ts`. Copy the contents of the file linked here into your controller file. It contains all the authentication logic. [You can find the file here](https://github.com/appsmithorg/example-apps-movie-store-api/blob/main/src/controllers/user.controller.ts?utm_source=devto&utm_medium=blog&utm_content=react_admin_panel&utm_campaign=weeklyblog&utm_term=react_admin_panel)

>Visit the link above and copy its contents into the `user.controller.ts` file

Finally, we can secure the `customer` resource by adding the authentication strategy we just implemented to its controller. Here’s how to do it:
 
```js
// src/controllers/order.controller.ts

// ---------- Add imports -------------
import {authenticate} from '@loopback/authentication';

// ------------------ Add auth decorator -----------
@authenticate('jwt') // <---- Apply the @authenticate decorator at the class level
export class CustomerController {
  //...
}
```

Do the same for the `film` and `rental` resources by adding the authentication strategy to their respective controller files.

And that’s it! Visiting the API playground on the browser `http://localhost:4000/explorer/` you’ll notice we have a nice green Authorize button at the top of the page. We also now have `signup` and `login` routes to create user accounts and log in.

>You’ll need to use this playground/explorer to create a new user

Now, let’s use this authentication on the React-admin dashboard.


## Adding authentication to React-admin
Implementing authentication on the React-admin dashboard is fairly straightforward. We need an `authProvider` which is an object that contains methods for the authentication logic, and also a `httpClient` that adds the authorization header to every request made by the dashboard.

Create an `Auth.js` file in `src/Auth.js` that contains the `authProvider` method, and the `httpClient` function. Here’s what the content of the file should be

```js
// src/Auth.js

export const httpClient = () => {
  const { token } = JSON.parse(localStorage.getItem('auth')) || {};
  return { Authorization: `Bearer ${token}` };
};

export const authProvider = {
  // authentication
  login: ({ username, password }) => {
    const request = new Request(
      process.env.REACT_APP_API_URL + '/users/login',
      {
        method: 'POST',
        body: JSON.stringify({ email: username, password }),
        headers: new Headers({ 'Content-Type': 'application/json' }),
      }
    );
    return fetch(request)
      .then((response) => {
        if (response.status < 200 || response.status >= 300) {
          throw new Error(response.statusText);
        }
        return response.json();
      })
      .then((auth) => {
        localStorage.setItem(
          'auth',
          JSON.stringify({ ...auth, fullName: username })
        );
      })
      .catch(() => {
        throw new Error('Network error');
      });
  },
  checkError: (error) => {
    const status = error.status;
    if (status === 401 || status === 403) {
      localStorage.removeItem('auth');
      return Promise.reject();
    }
    // other error code (404, 500, etc): no need to log out
    return Promise.resolve();
  },
  checkAuth: () =>
    localStorage.getItem('auth')
      ? Promise.resolve()
      : Promise.reject({ message: 'login required' }),
  logout: () => {
    localStorage.removeItem('auth');
    return Promise.resolve();
  },
  getIdentity: () => {
    try {
      const { id, fullName, avatar } = JSON.parse(localStorage.getItem('auth'));
      return Promise.resolve({ id, fullName, avatar });
    } catch (error) {
      return Promise.reject(error);
    }
  },
  getPermissions: (params) => Promise.resolve(),
};

```

Alright! Now let’s make use of the `authProvider` and `httpClient` in our app. Import 	`authProvider` and `httpClient` from ‘Auth.js` into `App.js` and pass `httpClient` as a second parameter to `lb4Provider`. Then add an authProvider prop to the `Admin` component and pass in `authProvider` as its value.

Simple and easy!

```js

// ----------- Import Auth -------------
import { authProvider, httpClient } from './Auth';

//…

// ------------ Use httpClient and authProvider in the Admin component ---------
 <Admin
      dataProvider={lb4Provider(‘http://localhost:4000’, httpClient)}
      authProvider={authProvider}
    >
    
//...

```

Save the files and head back to the browser and you’ll be greeted with a login screen. Fill in the email and password of your registered user and you’ll be taken to the customers’ table like before.


![13-login flow gif.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1619626865283/uZdD_8Pzh.gif)

And that’s it! We now have a super-secured app! 💪

# Deploy 🚀
We now have a fully functional admin dashboard with authentication. Lastly, I’ll like to walk you through deployment to your favourite cloud provider.

Since the API generated using Loopback is a standard Nodejs server, you can deploy your app to any Nodejs hosting provider i.e Heroku or Glitch. But note that you will need to move all packages under `devDependencies` to the `dependencies` section in your `package.json` file.

And for the React-admin dashboard, you can deploy it on any static hosting service i.e Netlify or Vercel. Don’t forget to replace the `lb4Provider` URL with that of your hosted backend.