Skip to content

Prisma Project — Build a Complete Application with Prisma

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Prisma Project. We cover key concepts, practical examples, and best practices to help you master this topic.

This project builds a complete file management API with Prisma: a database schema with User, File, and ProcessingJob models, migrations, CRUD operations with Express, middleware for audit logging, and production deployment configuration.

What You'll Learn

By the end of this project you will combine all Prisma concepts into one application, design and migrate a relational schema, implement a layered API architecture, add middleware, and configure production deployment.

Why It Matters

A real application integrates everything you have learned. Understanding how schema, migrations, queries, middleware, and deployment work together prepares you to build production-ready database applications.

Real-World Use

This project mirrors DodaZIP's Prisma architecture: users upload files, the API creates file records with processing jobs, middleware logs all mutations, and the deployment pipeline runs migrations automatically.

Schema Design

Define the complete database schema.

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

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

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  password  String   // Hashed, never stored in plaintext
  role      Role     @default(USER)
  files     File[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model File {
  id          Int            @id @default(autoincrement())
  name        String
  sizeBytes   BigInt
  mimeType    String?
  status      FileStatus     @default(PENDING)
  storagePath String?
  user        User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId      Int
  processing  ProcessingJob?
  createdAt   DateTime       @default(now())
  updatedAt   DateTime       @updatedAt
  deletedAt   DateTime?
  
  @@index([userId, status])
  @@index([status])
}

model ProcessingJob {
  id          Int            @id @default(autoincrement())
  file        File           @relation(fields: [fileId], references: [id], onDelete: Cascade)
  fileId      Int            @unique
  status      JobStatus      @default(QUEUED)
  progress    Int            @default(0)
  result      Json?
  errorMessage String?
  startedAt   DateTime?
  completedAt DateTime?
  createdAt   DateTime       @default(now())
}

enum Role { USER ADMIN }
enum FileStatus { PENDING PROCESSING COMPLETED FAILED }
enum JobStatus { QUEUED PROCESSING COMPLETED FAILED }

Middleware Implementation

Add audit logging and soft delete middleware.

// middleware/prisma.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Soft delete middleware
prisma.$use(async (params, next) => {
  if (params.model === 'File') {
    if (params.action === 'delete') {
      params.action = 'update';
      params.args.data = { deletedAt: new Date() };
    }
    if (params.action === 'deleteMany') {
      params.action = 'updateMany';
      params.args.data = { deletedAt: new Date() };
    }
    if (params.action === 'findMany' || params.action === 'findFirst') {
      if (!params.args) params.args = {};
      if (!params.args.where) params.args.where = {};
      params.args.where.deletedAt = null;
    }
  }
  return next(params);
});

// Audit logging middleware
prisma.$use(async (params, next) => {
  const result = await next(params);
  
  if (['create', 'update', 'delete'].includes(params.action)) {
    await prisma.auditLog.create({
      data: {
        model: params.model,
        action: params.action,
        recordId: result?.id?.toString(),
        timestamp: new Date(),
      },
    });
  }
  
  return result;
});

API Layer

Build the Express API with Prisma.

// services/file.service.ts
import { prisma } from '../db';

export class FileService {
  async upload(userId: number, data: { name: string; sizeBytes: number; mimeType?: string }) {
    return prisma.$transaction(async (tx) => {
      const file = await tx.file.create({
        data: { ...data, userId, status: 'PENDING' },
      });
      
      await tx.processingJob.create({
        data: { fileId: file.id },
      });
      
      return file;
    });
  }
  
  async list(userId: number, filters: { status?: string; page?: number }) {
    const where: any = { userId, deletedAt: null };
    if (filters.status) where.status = filters.status;
    
    const page = filters.page || 1;
    const [data, total] = await Promise.all([
      prisma.file.findMany({
        where,
        skip: (page - 1) * 20,
        take: 20,
        orderBy: { createdAt: 'desc' },
        include: { processing: { select: { status: true, progress: true } } },
      }),
      prisma.file.count({ where }),
    ]);
    
    return { data, total, page };
  }
}

Mini Project

This entire lesson is the project. Verify all components work together end-to-end.

def end_to_end_verification():
    checks = [
        ("Prisma schema with all models", True),
        ("Database migrations created and applied", True),
        ("Prisma Client generated", True),
        ("Middleware: soft delete on File", True),
        ("Middleware: audit logging", True),
        ("CRUD operations: User, File, ProcessingJob", True),
        ("Relations: User -> File, File -> ProcessingJob", True),
        ("Express API routes with validation", True),
        ("Pagination and filtering on list endpoints", True),
        ("Transaction for file upload flow", True),
        ("Error handling with HTTP status mapping", True),
        ("Production deployment configuration", True),
    ]
    
    print("End-to-End Verification:")
    for check, passed in checks:
        status = "[PASS]" if passed else "[FAIL]"
        print(f"  {status} {check}")

end_to_end_verification()

What's Next

Next: Microservices Communication for Distributed Systems.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro