Module F-8·15 min read

Building a complete CRUD REST API end-to-end — project structure, error handling, dotenv, nodemon, and testing with curl and Postman.

Written by Jatin Jain Saraf · Architect of SupraScan

Module F-8 — Your First Real Application

What this module covers: Modules F-1 through F-7 gave you all the individual pieces. This module assembles them into a real, structured project. We build a Blog API — full CRUD for posts and users, proper project structure, environment configuration, error handling, and a working Prisma database layer. By the end you will have a template that reflects how real Node.js applications are organised, and you will understand why each layer exists.


What We Are Building

A Blog API with two resources:

  • Users — register, read profile
  • Posts — create, read, update, delete blog posts

Endpoints:

POST   /users              — register a user
GET    /users/:id          — get user profile
 
GET    /posts              — list all published posts (with author)
GET    /posts/:id          — get a single post
POST   /posts              — create a post (requires userId in body)
PATCH  /posts/:id          — update a post
DELETE /posts/:id          — delete a post

This is deliberately simple — no authentication yet (that's P-2). The goal is clean project structure and solid fundamentals.


Project Structure

blog-api/
├── prisma/
│   └── schema.prisma          # database schema
├── src/
│   ├── db/
│   │   └── prisma.js          # Prisma client singleton
│   ├── routes/
│   │   ├── users.js           # user route handlers
│   │   └── posts.js           # post route handlers
│   ├── middleware/
│   │   └── errorHandler.js    # global error handler
│   └── index.js               # app entry point
├── .env                       # environment variables (not in git)
├── .env.example               # example env file (committed to git)
├── .gitignore
├── nodemon.json
└── package.json

This is the layered architecture we will formalise in P-1. For now the pattern is: routes handle HTTP, the db/ layer handles data access, and middleware/ holds cross-cutting concerns.


Step 1: Project Initialisation

mkdir blog-api && cd blog-api
npm init -y
npm install express @prisma/client dotenv
npm install -D prisma nodemon
npx prisma init

Update package.json:

{
  "name": "blog-api",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "db:migrate": "prisma migrate dev",
    "db:studio": "prisma studio"
  }
}

Create .gitignore:

node_modules/
.env
dist/
*.log

Create .env:

DATABASE_URL=postgres://postgres:password@localhost:5432/blog_dev
PORT=3000
NODE_ENV=development

Create .env.example (committed — shows what vars are needed without exposing values):

DATABASE_URL=postgres://user:password@host:5432/dbname
PORT=3000
NODE_ENV=development

Step 2: 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())
  name      String
  email     String   @unique
  createdAt DateTime @default(now())
  posts     Post[]
}
 
model Post {
  id          Int      @id @default(autoincrement())
  title       String
  content     String
  published   Boolean  @default(false)
  author      User     @relation(fields: [authorId], references: [id])
  authorId    Int
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

Run the migration:

npx prisma migrate dev --name init

This creates the tables in your PostgreSQL database and generates the Prisma Client.


Step 3: Prisma Client Singleton

// src/db/prisma.js
import { PrismaClient } from '@prisma/client';
 
const prisma = new PrismaClient({
  log: process.env.NODE_ENV === 'development' ? ['query', 'warn', 'error'] : ['error'],
});
 
export default prisma;

One PrismaClient instance for the entire application. Creating one per request wastes connections and causes pool exhaustion.


Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.