#refineweek: Pilot & refine architecture
Tags: refineweek, webdev, react, javascript
Published: 2023-02-14 19:42:56

  <img  src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-02-04-refine-pixels-1/refine_supabase.png" alt="refine banner" />

  <br/>
  <br/>


This post provides an introduction to [**refine**](https://github.com/refinedev/refine), a React framework used to rapidly build data heavy CRUD apps like dashboards, admin panels and e-commerce storefronts.

It also presents the [AWeekOfRefine](https://refine.dev/week-of-refine/) series - which is a seven part quickfire guide that aims to help developers learn the ins-and-outs of [**refine**](https://github.com/refinedev/refine) and [**Supabase**](https://supabase.com/) powerful capabilities and get going with **refine** within a week.

At the end of this series, you'll be able to build a fully functional CRUD app named "**Pixels**" with **refine** and **Supabase**. 

[The live version of the app is be available here.](https://pixels.refine.dev/)

The final apps source codes are available on GitHub:

  Pixels Client

  [Source Code on GitHub](https://github.com/refinedev/refine/tree/master/examples/pixels)  

  To get completed client source code simply run:
   ```
npm create refine-app@latest -- --example pixels
   ```

 Pixels Admin

  [Source Code on GitHub](https://github.com/refinedev/refine/tree/master/examples/pixels-admin)  

  To get completed admin source code simply run: 
   
   ```
 npm create refine-app@latest -- --example pixels-admin
   ```




## What is **refine** ?

**refine** is a highly customizable **React** based framework for building CRUD apps that comes with a headless core package and supplementary "pick-and-plug" modules for the UI, backend API clients and Internationalization support.

**refine**'s (intentionally decapitalized) core is strongly opinionated about RESTful conventions, HTTPS networking, state management, authentication and authorization. It is, however, unopinionated about the UI and render logic. This makes it customizable according to one's choice of UI library and frameworks.

In a nutshell, you can build rock-solid CRUD apps easily using refine✨.



## refine Architecture

Everything in **refine** is centered around the [`<Refine />`](https://refine.dev/docs/api-reference/core/components/refine-config/) component, which is configured via a set of provider props that each requires a provider object to be passed in. A typical application of providers on the `<Refine />` component looks like this:

```ts
// App.tsx

import { Refine } from "@pankod/refine-core";
import dataProvider from "@pankod/refine-simple-rest";
import routerProvider from "@pankod/refine-react-router-v6";
import { liveProvider } from "@pankod/refine-supabase";
import authProvider from "./authProvider";

<Refine
    dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
    routerProvider={routerProvider}
    liveProvider={liveProvider(supabaseClient)}
    authProvider={authProvider}
    resources={[]}
    ...
/>;
```

The above snippet lists a few of the props and their objects.  

However, rather than precisely being a component, `<Refine />` is largely a monolith of provider configurations backed by a context for each. Hence, inside [`dataProvider`](http://localhost:3000/docs/api-reference/core/providers/data-provider/), we have a standard set of methods for making API requests; inside [`authProvider`](http://localhost:3000/docs/api-reference/core/providers/auth-provider/), we have methods for dealing with authentication and authorization; inside [`routerProvider`](http://localhost:3000/docs/api-reference/core/providers/router-provider/), we have _exact_ definitions of routes and the components to render for that route, etc. And each provider comes with its own set of conventions and type definitions.

For example, a `dataProvider` object has the following signature to which any definition of a data provider conform:


```tsx
// dataProvider.ts

const dataProvider = {
    create: ({ resource, variables, metaData }) => Promise,
    createMany: ({ resource, variables, metaData }) => Promise,
    deleteOne: ({ resource, id, variables, metaData }) => Promise,
    deleteMany: ({ resource, ids, variables, metaData }) => Promise,
    //highlight-start
    getList: ({
        resource,
        pagination,
        hasPagination,
        sort,
        filters,
        metaData,
    }) => Promise,
    //highlight-end
    getMany: ({ resource, ids, metaData }) => Promise,
    getOne: ({ resource, id, metaData }) => Promise,
    update: ({ resource, id, variables, metaData }) => Promise,
    updateMany: ({ resource, ids, variables, metaData }) => Promise,
    custom: ({
        url,
        method,
        sort,
        filters,
        payload,
        query,
        headers,
        metaData,
    }) => Promise,
    getApiUrl: () => "",
};
```



The underlying architecture involves any presentational component passed to `<Refine />` to be able to consume these configured methods via corresponding hooks. Each method in a provider has a corresponding hook via which a consumer component is able to fetch data from the backend, i.e. the [`useList()`](https://refine.dev/docs/api-reference/core/hooks/data/useList/) hook is the corresponding function accessing the `dataProvider.getList()` provider method.

An example hook usage looks like this:

```ts
// Inside a UI component

const { data } = useList<Canvas>({
    resource: "canvases",
    config: {
      hasPagination: false,
      sort: [
        {
          field: "created_at",
          order: "desc",
        },
      ],
    },
});
```

The hooks, in turn, leverage [**React Query**](https://react-query-v3.tanstack.com/) hooks in order to make API calls asked by the provider methods. Here's an early sneak peek into the action under the hood:


```tsx
const queryResponse = useQuery<GetListResponse<TData>, TError>(
  queryKey.list(config),
  ({ queryKey, pageParam, signal }) => {
    const { hasPagination, ...restConfig } = config || {};
    return getList<TData>({
      resource,
      ...restConfig,
      hasPagination,
      metaData: {
        ...metaData,
        queryContext: {
          queryKey,
          pageParam,
          signal,
        },
      },
    });
  },
  {
    ...queryOptions,
    onSuccess: (data) => {
        queryOptions?.onSuccess?.(data);

      const notificationConfig =
        typeof successNotification === "function"
          ? successNotification(
              data,
              { metaData, config },
              resource,
            )
          : successNotification;

      handleNotification(notificationConfig);
    },
    onError: (err: TError) => {
      checkError(err);
      queryOptions?.onError?.(err);

      const notificationConfig =
        typeof errorNotification === "function"
          ? errorNotification(err, { metaData, config }, resource)
          : errorNotification;

      handleNotification(notificationConfig, {
        key: `${resource}-useList-notification`,
        message: translate(
          "common:notifications.error",
          { statusCode: err.statusCode },
          `Error (status code: ${err.statusCode})`,
        ),
        description: err.message,
        type: "error",
      });
    },
  },
);
```

We'll be visiting code like this often, but if you examine closely you can see that **refine** uses **React Query** to handle caching, state management as well as errors out-of-the-box.

The following diagram illustrates the interactions:


  <img  src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-02-04-refine-pixels-1/refine-flow.png" alt="refine banner" />


<br />


## Providers and Hooks

**refine**'s power lies in the abstraction of various app component logic such as authentication, authorization, routing and data fetching - inside individual providers and their corresponding hooks.

Common providers include:

- [`authProvider`](https://refine.dev/docs/api-reference/core/providers/auth-provider/) - for authentication and authorization.
- [`dataProvider`](https://refine.dev/docs/api-reference/core/providers/data-provider/) - for CRUD operations.
- [`routerProvider`](https://refine.dev/docs/api-reference/core/providers/router-provider/) - for defining routes, RESTful and non-RESTful.
- [`liveProvider`](https://refine.dev/docs/api-reference/core/providers/live-provider/) - for implementing real time features.
- [`accessControlProvider`](https://refine.dev/docs/api-reference/core/providers/accessControl-provider/) - for access control management.
- [`auditLogProvider`](https://refine.dev/docs/api-reference/core/providers/audit-log-provider/) - for logging appwide activities.

For an exhaustive list of providers, please visit the **refine** providers documentation from [here](https://refine.dev/docs/api-reference/core/).

Each method in these providers comes with its corresponding hook to be used from inside UI components and pages. For more details, please refer to the **refine** hooks documentation starting [here](https://refine.dev/docs/api-reference/core/hooks/accessControl/useCan/).



## Support Packages

**refine** is inherently headless in its core API and deliberately agnostic about the UI and backend layers. Being so, it is able to provide fantastic support for major UI libraries and frameworks as well as popular backend frameworks and services. To name a few, **refine**'s UI support packages include [**Ant Design**](https://refine.dev/docs/api-reference/antd/), [**Material UI**](https://refine.dev/docs/api-reference/mui/), [**Chakra UI**](https://refine.dev/docs/api-reference/chakra-ui/) and [**Mantine**](https://refine.dev/docs/api-reference/mantine/). Backend supplementary modules include [**Supabase**](https://supabase.com/), [**GraphQL**](https://graphql.org/), and [**NestJS**](https://nestjs.com/)

For a complete list of all these modules, check out [this page](https://refine.dev/docs/packages/list-of-packages/).

## What is Supabase?

[**Supabase**](https://supabase.com/) is an open source alternative to Firebase. It is a hosted backend that provides a realtime database, authentication, storage, and API services. 

refine has a built-in data provider support for Supabase. You can find the advanced tutorial [here](https://refine.dev/docs/advanced-tutorials/data-provider/supabase/).

We'll be using **Supabase** to build our backend for **Pixels** app.


## A week of refine ft. Supabase

  <img  src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-02-04-refine-pixels-1/pixel-logo-background.png" alt="refine banner" />

  <br/>
  <br/>


In this tutorial series, we will be going through most of the core features of [**refine**](https://github.com/refinedev/refine) by building two apps related to drawing pixels on a canvas. This section is intended to provide an overview.

The first one, the client app - **Pixels**, allows a logged in user to create a canvas and draw on it together with other users. It also displays a public gallery of all canvases and a "Featured Canvases" page.

The second app, **Pixels Admin** is an admin dashboard that allows authorized users like `editor`s and `admin`s to view the list of users registered with **Pixels** app and manage user drawn canvases, with actions like promoting, unpromoting and deleting a canvas.

We will be building these two apps day-by-day over a period of 7 days. And while doing so, we will dive deep into the details of related providers, hooks, UI components and how **refine** works behind the scenes.

As far as our features and functionalities go, we will cover most of the providers and some of the related hooks. For the UI side, we will be using the optional [**Ant Design**](https://refine.dev/docs/api-reference/antd/) package supported by **refine**. For the backend, we will use a PostgreSQL database hosted on the [**Supabase**](https://supabase.com/) cloud.

<br />


Here are the detailed outlines split per day:

### Day One - On AWeekOfRefine

This post. Hello! :wave: :wave: **refine** welcomes you! We are here :smile: :smile:



### Day Two - Setting Up the Client App

We  start with setting up the **Pixels** client app using `create refine-app`. We choose **refine**'s optional **Ant Design** and **Supabase** modules as our support packages. After initialization, we explore the boilerplate code created by `create refine-app` and look into the details of the `dataProvider` and `authProvider` objects and briefly discuss their mechanisms.



### Day Three - Adding CRUD Actions & Authentication

On Day Three, we start adding features to our app. We activate the `resources` prop for `<Refine />` and using the `dataProvider` prop, we implement how to create a canvas, show a canvas and draw pixels on a canvas. We add a public gallery to show all canvases in a page and featured canvases in another. We also implement user authentication so that only signed in users can create and draw on a canvas - and while doing so we delve into the `authProvider` object.

Here is a quick sum up of spcifications we cover on Day Three:

1. The **Pixels** app has a public gallery.
2. The public gallery has a home page of featured canvases.
3. The public gallery contains a section for all the canvases.
4. All users can view the public gallery.
5. All users can view a canvas.
6. Only logged in users can create a canvas.
7. Only logged in users can draw pixels on a canvas.
8. A user can sign up to the app using email, Google and GitHub.
9. A user can log in to the app using email, Google and GitHub.


### Day Four - Adding Realtime Collaboration
On Day Four, we add **real time** features to our app using the `liveProvider` prop on `<Refine />`. Real time updates on a canvas will facilitate multiple users to collaborate on it at the same time.

We are going to use **Supabase**'s **Realtime** [PostgreSQL CDC](https://supabase.com/docs/guides/realtime/postgres-cdc) in order to perform row level updates on the PostgreSQL database in real time.


### Day Five - Initialize and Build Pixels Admin App

Basing on the learning from the client app, we quickly implement an admin dashboard app and explore how **refine**'s **Ant Design** support module is geared to rapidly build CRUD pages for a **refine** app.

Here are the requirements for **Pixels Admin**:

1. Allow a user to sign up to the app using email, Google and GitHub.
2. Allow a user to log in to the app using email, Google and GitHub.
3. Build a dashboard that lists `users` and `canvases`.
4. The dashboard shows a list of all users at `/users` endpoint.
5. The dashboard shows a list of all canvases at `/canvases` endpoint.


### Day Six - Add Role Based Authorization

On Day Six, we implement user role based authorization to our admin app. While doing so, we analyze the `authProvider.getPermissions()` method from the standpoint of implementing authorization and customize according to our needs. We use **Casbin** for implementing a Role Based Access Control model and use it to define the `can` method of the `accessControlProvider` provider.

Here are the features we implement on Day Six:

1. There are two authorized roles for admin dashboard: `editor` and `admin`.
2. An editor is able to promote a canvas to "featured" status and demote it back.
3. An admin is able to promote a canvas to "featured" status and demote it back.
4. An admin is able to delete a canvas.


### Day Seven - Add Audit Log to Client App and Admin App

On the last day, with the `auditLogProvider` prop, we implement a log of all pixel drawing activities.Mutations for drawing pixels will be logged and saved in a `logs` table in our **Supabase** database.

We will display these logs inside a modal for each canvas both in the client **Pixels** app and in the **Pixels Admin** dashboard app. So, we will implement audit logging on both our apps.

## Summary

In this post, we introduced the **refine** framework and the [AWeekOfRefine](https://refine.dev/week-of-refine/) series itself. We talked about **refine**'s underlying architecture which consists of providers, hooks and components that help rapidly build internal tools.

We layed out the plans for building a **Pixels** client app and an admin dashboard app in considerable depth.

Tomorrow, on Day Two, we are ready to start "Setting Up the Client App". See you soon!