Below is a step‑by‑step tutorial that not only shows you how to set up Prisma with Express and TypeScript for a simple todo app but also highlights the changes you made—replacing your custom TaskClient with Prisma for full type safety. Let’s jump in.
Initialize Prisma
Initialize Prisma by running:
npx prisma initThis creates a prisma/ folder with a schema.prisma file. Edit the file to define your Task model:
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model Task {
id Int @id @default(autoincrement())
title String
description String?
completed Boolean @default(false)
}Then run a migration and generate the Prisma client:
npx prisma migrate dev --name init
npx prisma generateIntegrating Prisma in Your Express Server
Previously, you used a custom TaskClient (and a Database type from SQLite) to handle your database operations. Now we’re switching to Prisma to get true type safety. Here’s a look at your diff with changes:
diff --git a/server/src/server.ts b/server/src/server.ts
index bbdd593..53ee07a 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -1,8 +1,10 @@
import cors from 'cors';
import express from 'express';
-import type { Database } from 'sqlite';
import swaggerUi from 'swagger-ui-express';
+import { PrismaClient } from '@prisma/client';
+const prisma = new PrismaClient();
+Replacing CRUD Methods
Instead of calling methods on your custom client, you now use Prisma’s methods:
GET /tasks
const tasks = await prisma.task.findMany();GET /tasks/:id
const task = await prisma.task.findUnique({ where: { id } });POST /tasks
await prisma.task.create({ data: task });PUT /tasks/:id
const data = req.body;
await prisma.task.update({ where: { id }, data });DELETE /tasks/:id
await prisma.task.delete({ where: { id } });Full Updated server.ts Example
Here’s what your complete server.ts might look like after integrating Prisma:
import cors from 'cors';
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
import {
NewTaskSchema,
TaskParamsSchema,
TaskQuerySchema,
UpdateTaskSchema,
} from 'busy-bee-schema';
import { handleError } from './handle-error.js';
import { openApiDocument } from './openapi.js';
import { validate } from './validate.js';
export async function createServer() {
const app = express();
app.use(cors());
app.use(express.json());
// Serve OpenAPI docs
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiDocument));
// Expose OpenAPI spec as JSON
app.get('/openapi.json', (req, res) => {
res.json(openApiDocument);
});
app.get('/tasks', validate({ query: TaskQuerySchema }), async (req, res) => {
try {
const tasks = await prisma.task.findMany();
return res.json(tasks);
} catch (error) {
return handleError(req, res, error);
}
});
// Get a specific task
app.get('/tasks/:id', validate({ params: TaskParamsSchema }), async (req, res) => {
try {
const { id } = req.params;
const task = await prisma.task.findUnique({ where: { id } });
if (!task) return res.status(404).json({ message: 'Task not found' });
return res.json(task);
} catch (error) {
return handleError(req, res, error);
}
});
app.post('/tasks', validate({ body: NewTaskSchema }), async (req, res) => {
try {
const task = req.body;
await prisma.task.create({ data: task });
return res.status(201).json({ message: 'Task created successfully' });
} catch (error) {
return handleError(req, res, error);
}
});
// Update a task
app.put(
'/tasks/:id',
validate({ params: TaskParamsSchema, body: UpdateTaskSchema }),
async (req, res) => {
try {
const { id } = req.params;
const data = req.body;
await prisma.task.update({ where: { id }, data });
return res.status(200).json({ message: 'Task updated successfully' });
} catch (error) {
return handleError(req, res, error);
}
},
);
// Delete a task
app.delete('/tasks/:id', validate({ params: TaskParamsSchema }), async (req, res) => {
try {
const { id } = req.params;
await prisma.task.delete({ where: { id } });
return res.status(200).json({ message: 'Task deleted successfully' });
} catch (error) {
return handleError(req, res, error);
}
});
return app;
}Note: Make sure you handle type conversions if your route parameters (like id) need to be numbers. Depending on your Prisma schema, you might need to convert id from string to number (e.g., using Number(id)).