Build A CRUD App with ExpressJS And Supabase
Tags: supabase, express, backend, node
Published: 2023-05-08 19:58:25

Even though I'm a Frontend Developer, I've always wanted to know how the backend (BE) works and I've over time tried a number of server-side technologies like Express with MongoDB, [ASP.NET](http://ASP.NET) (C#) and Laravel (PHP). But my most recent contact was when I got a take-home test from an interview: I was tasked to build a simple blog API with a relational database like Postgres. So I decided to use Supabase since it runs on Postgres.

In this tutorial, you'll learn how to create a CRUD API with Express and Supabase. As described in the [official documentation](https://expressjs.com/), Express is one of the most popular web frameworks built on top of Node.js for web and mobile applications. It simplifies creating a server, routing and middleware already available in Nodejs.

[CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) is a common way of referring to a set of operations that is executed by the server. The acronym stands for create, read, update and delete. The following table below summarizes recommended return values of their corresponding HTTP methods with resource URLs: 


![CRUD Table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zcrq5gbczleum56frj8q.PNG)

### What Is Supabase and Why Should You Choose It?

[Supabase](https://supabase.com/) is an open-source backend service that provides a suite of tools to help developers build and scale applications quickly which includes:

- Database: every project uses Postgres under the hood, which is a relational database.
- Authentication: you can add login and sign-up to your app.
- Storage: you can store, organise and serve large files. 
- Edge functions: these help execute the closest code to your users for a fast experience.

We'll be using Supabase with the Express app.

### Prerequisites

To get started with this tutorial, you'll need to have the following set up/installed:

- [Node.js](https://nodejs.org/en/download)
- [Supabse account](https://supabase.com/)
- [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) or [Postman](https://www.postman.com/)

Express and Supabase will be installed as dependencies as after setting up the project. So Let's get started.

### Project Setup

First, create a new folder and initialize a new Node.js project using the command below.

```bash
mkdir supabase-crud && cd supabase-crud 
npm init -y
```

The command creates a folder called **supabase-crud** and initializes a Node.js projects with a **package.json** file which should look like this:

```json
{
  "name": "supabase-crud",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

You don't have to, but you could update the json file like the description and author. Something like:

```json
{
  "description": "Express app with Supabase",
  "author": "John Ademoye",
}
```

Create a new file in the folder and name it **server.js**. This will serve as the entry point to your Node.js application.

### Install Express and Other Dependencies

Now install the [express library](https://www.npmjs.com/package/express) and spin up a server with the following code:

```javascript
import express from "express";

const app = express();

app.get("/", (_, response) =>
  response.json({ info: "Express app with Supabase" })
);

app.listen(3000, () =>
  console.log(
    new Date().toLocaleTimeString() + `: Server is running on port ${3000}...`
  )
);
```

The above code returns `{ info: "Express app with Supabase" }` under the "/" route and logs `Server is running on port 3000...` on the terminal with a timestamp.

Now let's see if that's the case. Run the app with:

```bash
node server.js
```

If everything goes well, you should get something like this with REST client:

![Get Home Endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mrx63rs1u7cou7gywh84.PNG)

You could also go to the URL in your browser and you should get the same response:

![Express browser home](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3mkks0kk6q4kucu8de7.png)

But I prefer using REST Client because I won't have to switch windows.

Every time I change `server.js`, I'll have to to rerun the file. So to avoid having to do that, install [nodemon](https://www.npmjs.com/package/nodemon) to watch the file for changes. Although Node v18.11.0 has watch mode built in, it is still experimental.

I'd also like to save the port number as an environment variable in a dotenv file also well as other constants like the database name and key. [DotEnv](https://www.npmjs.com/package/dotenv) is a package that automatically loads environment variables from the `.env` file into the `process.env` object.

So install DotEnv it using the command: `npm i dotenv`. Then in your app, require and configure the package like this: 
```javascript
import express from "express";
import env from "dotenv";

env.config();

const app = express();

app.get("/", (_, response) =>
  response.json({ info: "Express app with Supabase" })
);
  
app.listen(process.env.PORT, () =>
  console.log(
    new Date().toLocaleTimeString() +
      `: Server is running on port ${process.env.PORT}...`
  )
);
```

To use nodemon, open the `package.json` file and modify the scripts with following configuration:

```json
"scripts": {
    "dev": "nodemon server.js"
  },
```

And this is how the project folder structure looks:

```
📁 SUPABASE-CRUD
∟📁 node_modules
∟📄 .env
∟📄 package-lock.json
∟📄 package.json
∟📄 requests.http
∟📄 server.js
```

### Setting Up Supabase

After creating a Supabase account, you prompted to a dashboard upon signing in, which contains all projects. If it's your first time using Supabase, you shouldn't see any projects there. So create a new project:


![New project supabqse](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tyoqepsh2bhlrh1x8d2l.PNG)

 Projects are groupped under organizations. Create an org if you don't have one yet; I already have an org.


![New project org](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsgfphy7vkcjultgzvgk.PNG)

Then create a new project and name it **supabase-crud**. The password is automatically generated, so just submit the form after creating the project.


![New project form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwo38y5ms2qtng3qk8sx.PNG)

Now **supabase-crud** has its page. Scroll down to the Project API section where the URL and API key can be found. Copy the URL and Key and save them as DATABASE_URL and DATABASE_KEY respectively in the `.env` file.

![New project and API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c22jkpi646707gm3xtru.PNG)

Now go back to the top of the project page. Go to the Table though the `Table editor` button and create a new table:

![Table editor button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8jnxuenruypune2ic6su.PNG)

Name the table **posts** and a description like **A blog database** is fine.

![Create a new table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1ljtzhn4p9c0kdvmimd.PNG)

Insert a row with some dummy data for title and body manually and then save.

![Insert a row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcttcz1l61a1dv3d8gou.PNG)

### Setting Up Supabase In The Application

First of all, Install the library:

```bash
npm install @supabase/supabase-js
```

Then import  `createCleint` to establish the connection with the database. `createClient` takes the database URL and Key as arguments:

```javascript
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(process.env.DATABASE_URL,process.env.DATABASE_KEY);
```

And the server file looks like this so far:

```javascript
import express from "express";
import env from "dotenv";

import { createClient } from "@supabase/supabase-js";

env.config();

const supabase = createClient(process.env.DATABASE_URL,process.env.DATABASE_KEY);

// Services 

app.listen(process.env.PORT, () =>
  console.log(
    new Date().toLocaleTimeString() +
      `: Server is running on port ${process.env.PORT}...`
  )
);
```

### Creating the Routes

We'll be defining the routes for these handling CRUD operations:

 - Get all articles
 - Get an article
 - Post an article
 - Update an article
 - Delete an article

We'll be using the [Supabase documentation](https://supabase.com/docs/reference/dart/select) as a guide here:

- To get all articles, specify the table from they're coming from an use the select method on it without any arguments.

```javascript
// Get all articles

app.get("/articles", async (_, response) => {
  try {
    const { data, error } = await supabase.from("posts").select();
    console.log(data);
    return response.send(data);
  } catch (error) {
    return response.send({ error });
  }
});

```

- To get a particular article, specify again the source table and use `select`. Then pass the id from the URL, which can be accessed via `requests.params.id`, as an argument to `eq` to get the article whose `id` equals that in the params object.

```javascript

// Get an article

app.get("/articles/:id", async (request, response) => {
  try {
    const { data, error } = await supabase
      .from("posts")
      .select()
      .eq("id", request.params.id)
    console.log(data);
    return response.send(data);
  } catch (error) {
    return response.send({ error });
  }
});

```

- To post an article, you'll need to specify the `title` and `body` in the payload, which can be accessed through the request object. Then pass that to the insert method, which then writes to the database. If there's an error, it's returned with a status code of 400.

```javascript
// Post an article

app.post("/articles", async (request, response) => {
  try {
    console.log(request.body);
    const { data, error } = await supabase.from("posts").insert(request.body);
    if (error) {
      return response.status(400).json(error);
    }
    response.status(200).json(request.body);
  } catch (error) {
    response.send({ error });
  }
});

```

- To update a particular article, whose `id` is passed in the URL, check with the ternary operator what needs to be updated. Then select the article to update with `eq`.

```javascript
// Update an article

app.put("/articles/:id", async (request, response) => {
  console.log(request.params);
  try {
    const { data: updatedData, error: updatedError } = await supabase
      .from("posts")
      .update({
        title: request.body.title ? request.body.title : data[0].title,
        body: request.body.body ? request.body.body : data[0].body,
      })
      .eq("id", request.params.id);
    const { data, err } = await supabase.from("posts").select();
    return response.status(200).send(data);
  } catch (error) {
    response.send({ error });
  }
});

```

- To delete an article, the `id` is needed too; do this with the Supabase delete function. Make the deletion first and then get all the articles, which no longer contains the deleted article.

```javascript
// Delete an article

app.delete("/articles/:id", async (request, response) => {
  try {
    const { data, error } = await supabase
      .from("posts")
      .delete()
      .eq("id", request.params.id);
    const { datar, errorr } = await supabase.from("posts").select();
    if (error) {
      return response.status(400).json(error);
    }
    return response.send(datar);
  } catch (error) {
    response.send({ error });
  }
});
```

### Conclusion

In this article, you learned how to make a CRUD app using Express and Supabase. It started out introducing the relevant technologies: Express and Supabase, also highlighting reasons why you should use Supabase in your projects.

The article also covered spinning up an Express server, setting up a Supabase project and creating the routes handling the necessary CRUD operations for the application.

This app was published on the [Supabase Launch Week](https://supabase.com/launch-week). I had already built a similar app about 4 months ago, so I didn't bother pushing this particular one to GitHub. So feel free to clone the [original project](https://github.com/Moyo75/ausbildung-blog-api) and see how it works.

-----

*I hope that this article is helpful and you learned something from it. Being a technical writer and content creator I am passionate about sharing my knowledge (including tips and advice) and helping other people achieve their goals, especially those just starting out in tech. You can connect with me on my social media profiles and blogs [here](https://linktr.ee/johnademoye).*