Prisma

2025-04-19

From Zero to API: Building a REST Backend with Express, Prisma, and TypeScript

Introduction

In this guide, we will show you how to make a RESTful API using Express, TypeScript, and Prisma. We will start by setting up the project and end with a working backend that uses a PostgreSQL database , and also we will build full CRUD (Create, Read, Update, Delete) features to manage cars. This means you will learn how to:

  • Add a new car to the database

  • Get a list of all cars

  • Change car details

  • Delete a car from the system

All of this will be done using REST API routes.

NOTE : We will also test the API using Postman

          

Project Setup

Here’s the structure we’ll use:

image.png

          

Step 1: Project Initialization

mkdir final_test_prisma && cd final_test_prisma
npm init -y

          

Step 2: Install Dependencies

Main dependencies

npm install express cors dotenv @prisma/client

Dev dependencies

npm install -D typescript ts-node prisma nodemon @types/node @types/express @types/cors

          

Step 3: TypeScript Config

npx tsc --init

Update tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "CommonJS",
    "rootDir": "src",
    "outDir": "dist",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

          

Step 4: Initialize Prisma

1. Set up Prisma:

npx prisma init

2. Configure .env:

DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
PORT=8080

3. Define Schema in prisma/schema.prisma:

generator client {
  provider = "prisma-client-js"
  output   = "./client"
}

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

model Car {
  id        Int      @id @default(autoincrement())
  make      String
  model     String
  year      Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

4. Run Migrations:

npx prisma migrate dev --name init

          

Step 5: Setup Express Server

src/index.ts:

import express from "express";

const router = express.Router();

import { getCars, createCar, getCarById, updateCar, deleteCar } from "../controller/car.controller";

// Define the routes for car operations
router.get("/car", getCars); // Get all cars
router.get("/car/:id", getCarById); // Get a car by ID
router.post("/car", createCar); // Create a new car
router.put("/car/:id", updateCar); // Update a car by ID
router.delete("/car/:id", deleteCar); // Delete a car by ID

export default router;

          

Step 6: Routes, Controller, Service

routes/car.routes.ts:

import express from "express";

const router = express.Router();

import { getCars, createCar } from "../controller/car.controller";

// Define the routes for car operations
router.get("/car", getCars); // Get all Cars
router.post("/car", createCar); // Create a new Car

export default router;

controllers/car.controller.ts:

import { Request, Response } from "express";
import { PrismaClient } from "../../prisma/client";

const prisma = new PrismaClient();

export const getCars = async (req: Request, res: Response) => {
  try {
    const cars = await prisma.car.findMany();
    res.status(200).json(cars);
  } catch (error) {
    console.error("Error fetching cars:", error);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    await prisma.$disconnect();
  }
};

export const createCar = async (req: Request, res: Response) => {
  const { make, model, year } = req.body;
  try {
    const newCar = await prisma.car.create({
      data: {
        make,
        model,
        year,
      },
    });
    res.status(201).json(newCar);
  } catch (error) {
    console.error("Error creating car:", error);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    await prisma.$disconnect();
  }
};

export const getCarById = async (req: Request, res: Response) => {
  const { id } = req.params;
  try {
    const car = await prisma.car.findUnique({
      where: { id: Number(id) },
    });
    if (!car) {
      res.status(404).json({ error: "Car not found" });
    }
    res.status(200).json(car);
  } catch (error) {
    console.error("Error fetching car:", error);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    await prisma.$disconnect();
  }
};

export const updateCar = async (req: Request, res: Response) => {
  const { id } = req.params;
  const { make, model, year } = req.body;
  try {
    const updatedCar = await prisma.car.update({
      where: { id: Number(id) },
      data: {
        make,
        model,
        year,
      },
    });
    res.status(200).json(updatedCar);
  } catch (error) {
    console.error("Error updating car:", error);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    await prisma.$disconnect();
  }
};

export const deleteCar = async (req: Request, res: Response) => {
  const { id } = req.params;
  try {
    await prisma.car.delete({
      where: { id: Number(id) },
    });
    res.status(204).send();
  } catch (error) {
    console.error("Error deleting car:", error);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    await prisma.$disconnect();
  }
};

          

Step 7: Testing with Postman

1. Create Car

  • Method: POST
  • URL: http://localhost:8080/api/car

image.png

2. Get All cars

  • Method: GET
  • URL: http://localhost:8080/api/car

image.png

3. Get Car by ID

  • Method: GET

  • URL: http://localhost:8080/api/car/3

    image.png

4. Update Car

  • Method: PUT
  • URL: http://localhost:8080/api/car/3

image.png

5. Delete Car

  • Method: DELETE
  • URL: http://localhost:8080/api/car/3 image.png
sohaibb.dev