Skip to content

Medinz01/backsmith-prisma

Repository files navigation

backsmith-prisma

A production-structured Project Management REST API built with Prisma + PostgreSQL + Express + TypeScript.

Part of the BackSmith reference implementation series — a collection of 4 backend repos each demonstrating a different ORM/database stack with identical architecture patterns.


Stack

Layer Technology
Runtime Node.js + TypeScript
Framework Express
ORM Prisma v5
Database PostgreSQL 16
Validation Zod
Auth JWT (access + refresh token rotation)
Dev DB Docker

Domain

Project Management Platform — a Linear-inspired backend covering workspaces, projects, tasks, labels, comments, attachments, activity logs, and notifications.


Features

  • JWT authentication with HttpOnly cookie refresh token rotation
  • Zod-validated request bodies, query params, and route params
  • Module-based folder structure — every feature is fully self-contained
  • Thin controllers, fat services — all business logic and DB calls in the service layer
  • Centralized error handling with typed error classes (AppError, NotFoundError, ForbiddenError, etc.)
  • Prisma error code mapping (P2002 → 409, P2025 → 404)
  • Soft delete on User and Task with restore support
  • Offset pagination and cursor-based pagination via reusable utility
  • Full-text search via $queryRaw with to_tsvector / plainto_tsquery
  • Activity log (append-only) automatically written on task mutations
  • Postman collection with happy path + 400/401/403/404 cases + full lifecycle integration test

Prisma Coverage

Every directive and query pattern from the Prisma docs is demonstrated at least once:

Schema directives @id @default(cuid()), @default(uuid()), @unique, @@unique([compound]), @index, @@index([compound]), @default(now()), @updatedAt, onDelete: Cascade, onDelete: Restrict, onDelete: SetNull, onDelete: NoAction, enums, Json field, Float field, self-referential relations, explicit M:N junction tables

Query patterns findUnique, findUniqueOrThrow, findMany with where/orderBy/take/skip, cursor pagination, include (nested 2 levels), select, create with nested connect, create with nested create, update, updateMany, upsert, delete, deleteMany, soft delete pattern, $transaction (array form), $transaction (interactive), count, aggregate, groupBy, $queryRaw, Prisma error code mapping


Project Structure

backsmith-prisma/
├── prisma/
│   ├── schema.prisma
│   └── seed.ts
├── postman/
│   ├── backsmith-prisma.collection.json
│   └── environments/local.json
└── src/
    ├── app.ts
    ├── config/
    │   ├── env.ts
    │   └── constants.ts
    ├── lib/
    │   └── prisma.ts
    ├── middleware/
    │   ├── auth.middleware.ts
    │   ├── error.middleware.ts
    │   └── validate.middleware.ts
    ├── types/
    │   └── express.d.ts
    ├── utils/
    │   ├── errors.ts
    │   ├── pagination.ts
    │   └── slug.ts
    └── modules/
        ├── auth/
        ├── workspace/
        ├── project/
        ├── task/
        ├── label/
        ├── comment/
        ├── attachment/
        ├── activity/
        └── notification/

Each module contains: [feature].routes.ts, [feature].controller.ts, [feature].service.ts, [feature].schema.ts


Data Model

User
 ├── owns → Workspace (Restrict)
 ├── member of → WorkspaceMember (Cascade)
 ├── owns → Project (Restrict)
 ├── assigned → Task (SetNull)
 ├── reported → Task (Restrict)
 ├── authored → Comment (Restrict)
 └── uploaded → Attachment (Restrict)

Workspace
 ├── has many → WorkspaceMember (Cascade)
 └── has many → Project (Cascade)

Project
 ├── has many → Task (Cascade)
 └── has many → Label (Cascade)

Task (self-referential subtasks)
 ├── has many → TaskLabel (Cascade)
 ├── has many → Comment (Cascade, self-referential threads)
 ├── has many → Attachment (Cascade)
 ├── has many → Activity (Cascade)
 └── has many → Notification (SetNull)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker (for PostgreSQL)

1. Start the database

docker compose up -d

2. Install dependencies

npm install

3. Configure environment

cp .env.example .env

Edit .env — the only values you must change:

JWT_SECRET=any-random-string-minimum-32-characters-long
JWT_REFRESH_SECRET=another-random-string-minimum-32-characters

Everything else works out of the box with the Docker defaults.

4. Run migrations and seed

npx prisma migrate dev --name init
npx prisma db seed

Seed creates:

  • alice@example.com / password123 — ADMIN
  • bob@example.com / password123 — MEMBER
  • A workspace, project, tasks, labels, comments, and notifications

5. Start the server

npm run dev

Server runs on http://localhost:3001


API Endpoints

Auth

Method Endpoint Description
POST /api/v1/auth/register Register
POST /api/v1/auth/login Login
POST /api/v1/auth/refresh Refresh access token
POST /api/v1/auth/logout Logout
POST /api/v1/auth/verify-email Verify email

Workspaces

Method Endpoint Description
POST /api/v1/workspaces Create workspace
GET /api/v1/workspaces List my workspaces
GET /api/v1/workspaces/:workspaceId Get workspace
PATCH /api/v1/workspaces/:workspaceId Update workspace
DELETE /api/v1/workspaces/:workspaceId Delete workspace
GET /api/v1/workspaces/:workspaceId/stats Workspace stats
POST /api/v1/workspaces/:workspaceId/members Invite member
GET /api/v1/workspaces/:workspaceId/members List members
PATCH /api/v1/workspaces/:workspaceId/members/:userId Change member role
DELETE /api/v1/workspaces/:workspaceId/members/:userId Remove member

Projects

Method Endpoint Description
POST /api/v1/workspaces/:workspaceId/projects Create project
GET /api/v1/workspaces/:workspaceId/projects List projects
GET /api/v1/projects/:projectId Get project
PATCH /api/v1/projects/:projectId Update project
DELETE /api/v1/projects/:projectId Delete project
POST /api/v1/projects/:projectId/archive Archive project
PATCH /api/v1/projects/:projectId/visibility Change visibility
GET /api/v1/projects/:projectId/task-breakdown Task counts by status

Tasks

Method Endpoint Description
POST /api/v1/projects/:projectId/tasks Create task
GET /api/v1/projects/:projectId/tasks List tasks (offset or cursor)
GET /api/v1/projects/:projectId/tasks/search?q= Full-text search
GET /api/v1/tasks/:taskId Get task
PATCH /api/v1/tasks/:taskId Update task
DELETE /api/v1/tasks/:taskId Soft delete task
POST /api/v1/tasks/:taskId/restore Restore task
PATCH /api/v1/tasks/:taskId/status Change status
PATCH /api/v1/tasks/:taskId/priority Change priority
PATCH /api/v1/tasks/:taskId/assign Assign/unassign
PATCH /api/v1/tasks/:taskId/reorder Reorder (drag-and-drop)

Labels

Method Endpoint Description
POST /api/v1/projects/:projectId/labels Create label
GET /api/v1/projects/:projectId/labels List labels
PATCH /api/v1/projects/:projectId/labels/:labelId Update label
DELETE /api/v1/projects/:projectId/labels/:labelId Delete label
POST /api/v1/tasks/:taskId/labels Attach label to task
DELETE /api/v1/tasks/:taskId/labels/:labelId Detach label from task

Comments

Method Endpoint Description
POST /api/v1/tasks/:taskId/comments Create comment (supports threading via parentId)
GET /api/v1/tasks/:taskId/comments List comments
PATCH /api/v1/tasks/:taskId/comments/:commentId Edit comment
DELETE /api/v1/tasks/:taskId/comments/:commentId Soft delete comment

Attachments

Method Endpoint Description
POST /api/v1/tasks/:taskId/attachments Register attachment
GET /api/v1/tasks/:taskId/attachments List attachments
DELETE /api/v1/tasks/:taskId/attachments/:attachmentId Delete attachment

Activity & Notifications

Method Endpoint Description
GET /api/v1/tasks/:taskId/activities Activity log for task
GET /api/v1/notifications List notifications
GET /api/v1/notifications/unread-count Unread count
POST /api/v1/notifications/mark-all-read Mark all read
PATCH /api/v1/notifications/:notificationId/read Mark one read

Testing with Postman

  1. Import postman/backsmith-prisma.collection.json
  2. Import postman/environments/local.json and set it as the active environment
  3. Open the Collection Runner, select all folders, enable Run in sequence
  4. Hit Run

The collection runs top-to-bottom as a complete lifecycle integration test. Each request saves variables ({{token}}, {{workspaceId}}, {{projectId}}, {{taskId}}) for downstream requests. Every entity includes 400/401/403/404 error case coverage.

To reset between runs:

npx prisma migrate reset --force

Available Scripts

Script Description
npm run dev Start with hot reload
npm run build Compile TypeScript
npm start Run compiled output
npx prisma migrate dev Run migrations
npx prisma db seed Seed database
npx prisma studio Visual DB browser
npx prisma migrate reset --force Reset DB (wipes all data)

Architecture Decisions

See DECISIONS.md for a full log of non-obvious architectural choices made in this repo.

About

Production-structured Project Management REST API — Prisma + PostgreSQL + Express + TypeScript

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors