A Simple Crud App With Prisma, Express, and PostgreSQL
Tags: webdev, prisma, postgres, beginners
Published: 2024-02-21 02:09:50

## Building a Simple Blog App with Express, Prisma, and PostgreSQL

In this tutorial, I will take you step by step to building a simple(CRUD) blog application. The app will support creating, retrieving, updating, and deleting blog posts.

## Prerequisites

Make sure you have Node.js and npm installed on your machine, and you have PostgreSQL set up with a database.
>I hope you know how to setup a Postgres database😉😉😉. If you don't You have to😎, 

## Let's Dive Right in

![Diving](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o3y7g2wu1epk77h3xir.gif)

## Step 1: Project Setup

Create a new directory for your project and initialize it with npm:

```bash
mkdir prisma-express-blog-app
cd prisma-express-blog-app
npm init -y
```

## Step 2: We'll Install Our Project Dependencies
```bash
npm install express @prisma/client dotenv
```

## Step 3: We'll Set Up Prisma
```bash
npx prisma init
```
> This would install the necessary files and folders for prisma

## Step 4: We'll configure our prisma schema to look like this
```
// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// Post model represents individual blog posts
model Post {
  id        Int       @id @default(autoincrement())
  title     String
  content   String
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
}
```
## Step 5: We'll Configure our .env file to contain our postgres database URL. Mine looks like this
```
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
```
I'll edit that and add my postgresDb URL
```
DATABASE_URL="postgresql://postgres:qwer@localhost:5432/mydb?schema=public"
```
> You'll change your password and database name

## Step 6: We'll Create A Database Migration
```bash
npx prisma migrate dev
```
This would create a migration for you and add the folder, when you open the folder you see your migration.sql file which looks exactly like your schema
```sql
-- CreateTable
CREATE TABLE "Post" (
    "id" SERIAL NOT NULL,
    "title" TEXT NOT NULL,
    "content" TEXT NOT NULL,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" TIMESTAMP(3) NOT NULL,

    CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);
```
## Step 7: Now we'll create our main entry file and call it `app.js`. 
> We'll initialize express, dotenv our middleware and also listen to our port
```javascript
const express = require("express");
require("dotenv").config();

const app = express();
app.use(express.json());
const port = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.send("Welcome Tou Prisma, Express And PSQL Tutorial");
});

app.listen(port, () => {
  console.log(`Server listening on ${port}`);
});
```
## Step 8: Now lets import prismaClient and configure it, add this lines of code to your `app.js` file
```javascript
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
```

## Step 9: Now we add our first route, which would be to add a blog
> This route would look like this:
```javascript
app.post("/post", async (req, res) => {
  try {
    const { title, content } = req.body;
    if (!title || !content) {
      return res
        .status(400)
        .json({ message: "Please input Title Anc Content" });
    }

    const blog = await prisma.post.create({
      data: { title, content },
    });

    return res
      .status(201)
      .json({ message: "Blog created successfully", data: blog });
  } catch (error) {
    return res.status(500).json({ message: "Error creating blog" });
  }
});
```
After adding this to your `app.js` file we should test that route before moving on, my route looks like this `http://localhost:4500/post` 
> I got my response after testing

![Create Post](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vju5s5b93fi961a5ypzb.png)
> Add as much Blogs as Possible

## Step 10: Stretch Your Fingers 😁😁

![Stretch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftkqox4dpbtnaihd0o4g.gif)

## Step 11: Now we'll add a Route to get all Blogs and tell us the number of blogs available
```javascript
app.get("/posts", async (req, res) => {
  try {
    const blogs = await prisma.post.findMany();
    return res.status(201).json({ data: blogs.length, blogs });
  } catch (error) {
    return res.status(500).json({ message: "Error fetching blogs" });
  }
});
```
> after testing: 

![Get All](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q02exv7cxzdupkq53aqx.png)

## Step 12: We'll add a route to get a single blog by title 
```javascript
app.get("/get", async (req, res) => {
    const { title } = req.query;
    try {
      const blog = await prisma.post.findFirst({
        where: { title }
      });
      if (blog) {
        return res.status(200).json(blog);
      } else {
        return res.status(404).json({ message: "Blog not found" });
      }
    } catch (error) {
      console.log(error);
      return res.status(500).json({ message: "Error fetching blog" });
    }
  });
```
## Step 13: I'll add some more routes, you can add and test for yourself
> Now I finally have this:
```javascript
const express = require("express");
const { PrismaClient } = require("@prisma/client");
require("dotenv").config();

const prisma = new PrismaClient();

const app = express();
app.use(express.json());
const port = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.send("Welcome to Prisma, Express, and PSQL Tutorial");
});

// Create a blog
app.post("/post", async (req, res) => {
  try {
    const { title, content } = req.body;
    if (!title || !content) {
      return res
        .status(400)
        .json({ message: "Please input Title and Content" });
    }

    const blog = await prisma.post.create({
      data: { title, content },
    });

    return res
      .status(201)
      .json({ message: "Blog created successfully", data: blog });
  } catch (error) {
    return res.status(500).json({ message: "Error creating blog" });
  }
});

// Get all blogs
app.get("/posts", async (req, res) => {
  try {
    const blogs = await prisma.post.findMany();
    return res.status(201).json({ data: blogs.length, blogs });
  } catch (error) {
    return res.status(500).json({ message: "Error fetching blogs" });
  }
});

// Get a single blog by title
app.get("/get", async (req, res) => {
    const { title } = req.query;
    try {
      const blog = await prisma.post.findFirst({
        where: { title }
      });
      if (blog) {
        return res.status(200).json(blog);
      } else {
        return res.status(404).json({ message: "Blog not found" });
      }
    } catch (error) {
      console.log(error);
      return res.status(500).json({ message: "Error fetching blog" });
    }
  });
  

// Update a blog by id
app.put("/post/:id", async (req, res) => {
  const { id } = req.params;
  const { title, content } = req.body;
  try {
    const updatedBlog = await prisma.post.update({
      where: { id: parseInt(id) },
      data: { title, content },
    });
    return res.json({
      message: "Blog updated successfully",
      data: updatedBlog,
    });
  } catch (error) {
    console.log(error);
    return res.status(500).json({ message: "Error updating blog" });
  }
});

// Delete a blog by id
app.delete("/post/:id", async (req, res) => {
  const { id } = req.params;
  try {
    await prisma.post.delete({
      where: { id: parseInt(id) },
    });
    return res.json({ message: "Blog deleted successfully" });
  } catch (error) {
    return res.status(500).json({ message: "Error deleting blog" });
  }
});

app.listen(port, () => {
  console.log(`Server listening on ${port}`);
});
```
> And that is a comprehensive CRUD app using Prisma, PSQL, and express
[Link to Github Repo Containing The Code](https://github.com/FredAbod/Prisma-Tutorial)
## Thank you for taking the time to read 🥰🥰, I hope this was helpful, Don't Forget to drop a like and follow😋😋. See You on the Next One🤗

![Bow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2go8l3113h2kw6enp2d5.gif)