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:
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
2. Get All cars
- Method:
GET
- URL:
http://localhost:8080/api/car
3. Get Car by ID
-
Method:
GET
-
URL:
http://localhost:8080/api/car/3
4. Update Car
- Method:
PUT
- URL:
http://localhost:8080/api/car/3
5. Delete Car
- Method:
DELETE
- URL:
http://localhost:8080/api/car/3