https://github.com/sushantrahate/express-typescript-prisma-postgresql
For building RESTful APIs using Express, TypeScript, Prisma, and PostgreSQL.
https://github.com/sushantrahate/express-typescript-prisma-postgresql
eslint express nodejs postgresql prisma typescript
Last synced: 10 months ago
JSON representation
For building RESTful APIs using Express, TypeScript, Prisma, and PostgreSQL.
- Host: GitHub
- URL: https://github.com/sushantrahate/express-typescript-prisma-postgresql
- Owner: sushantrahate
- License: unlicense
- Created: 2023-10-12T07:12:34.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-11-24T14:59:42.000Z (over 1 year ago)
- Last Synced: 2024-11-24T15:33:59.069Z (over 1 year ago)
- Topics: eslint, express, nodejs, postgresql, prisma, typescript
- Language: TypeScript
- Homepage:
- Size: 813 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# π Express + TypeScript + Prisma + PostgreSQL Boilerplate (2025 Edition)
This is a backend built with Node.js, Express, TypeScript, and Prisma ORM. It follows modern best practices for API development, including strict type safety, structured error handling, security measures, and environment validation.
Designed to be modular and maintainable, the project features a clean architecture, making it easy to extend with new functionalities.
## β¨ Features
π οΈ Core Features\
β
TypeScript β Fully typed backend for maintainability\
β
Express.js β Lightweight and fast web framework\
β
Prisma ORM β Type-safe database interactions
β
PostgreSQL β Relational database\
\
π― Development & Code Quality\
β
Feature-Based Structure β Each feature has its own folder, keeping everything related to a feature (routes, schemas, types, services, controllers, repositories) together for better maintainability and scalability\
β
ESLint + Prettier β Code linting, formatting and autoformat on save\
β
Zod Validation β Strict schema validation for request & environment variables\
β
VSCode debugger\
\
π Environment & Security\
β
Environment Validation β Ensures required .env variables exist\
β
Helmet & Security Headers β Protects against web vulnerabilities\
β
Rate Limiter, Host whitelisting middleware\
\
β‘ API & Middleware\
β
Request Validation β Uses Zod for body, params, and query validation\
β
Error Handling Middleware β Centralized error handling with PostgreSQL error handling [(Ref)](https://www.prisma.io/docs/orm/reference/error-reference)\
β
Unified Response Structure β Uses [uni-response](https://github.com/sushantrahate/uni-response) for consistent API responses\
\
π§ͺ Testing & CI/CD\
β
Vitest β Unit and integration testing\
β
Husky + Lint-Staged β Enforces pre-commit linting and testing\
\
π Server Management\
β
Graceful Shutdown β Ensures proper cleanup of database & open connections during shutdown [(Ref)](https://github.com/sushantrahate/secure-nodejs-backend/tree/main/graceful-shutdown)
## π οΈ Clean Architecture & Feature-Based Structure
### π Clean Architecture & Framework-Agnostic Design
This project follows a feature-based modular structure, where each feature (e.g., user) has its own isolated folder containing everything related to that feature.
π Project Structure:
```bash
src/
βββ config/ # Configuration (e.g., environment variables, Prisma, security)
βββ constants/ # Shared constants (messages, enums, etc.)
βββ features/ # Feature-based modular structure
β βββ user/ # User feature module
β β βββ __tests__/ # Unit tests (vitest)
β β βββ controllers/ # Handles HTTP requests (Express-dependent)
β β βββ repositories/ # Database interactions (Prisma-dependent)
β β βββ routes/ # Express API routes (Express-dependent)
β β βββ schemas/ # Zod validation schemas (Framework-agnostic)
β β βββ services/ # Business logic (Completely framework-independent)
β β βββ types/ # TypeScript interfaces & types
βββ middleware/ # Global Express middlewares
βββ utils/ # Helper functions
βββ app.ts # Express app setup
βββ server.ts # Entry point
```
### π Layer-by-Layer Breakdown
### 1οΈβ£ Feature Modules (e.g., user/)
Each feature is self-contained, meaning everything related to "users" is inside `features/user/`
π― Benefit:\
π‘ You can easily add or remove features without affecting other parts of the app.
πΉ **No Cluttering, Even as the Project Grows Large β** The feature-based structure ensures that related files stay together, preventing scattered code.\
πΉ **Everything in One Place β** Developers can find all logic related to a feature (controllers, services, repositories, schemas) in a single folder, reducing confusion.\
πΉ **No Ambiguity in Large Systems β** Since each feature is self-contained, developers always know which controller, service, or repository to use, making onboarding and scaling easier.\
πΉ **Scalability & Maintainability β** Adding a new feature means simply creating a new folder under features/, without modifying unrelated parts of the app.
### 2οΈβ£ Controllers (controllers/)
β
Handles HTTP requests and responses\
β
Calls the service layer for business logic\
β
Only responsible for Express-specific logic
π Example: user.controller.ts
```ts
import { Request, Response } from "express";
import { UserService } from "../services/user.service";
export class UserController {
private userService: UserService;
constructor() {
this.userService = new UserService();
}
async getUsers(req: Request, res: Response) {
const users = await this.userService.getAllUsers();
res.json({ success: true, data: users });
}
}
```
### π οΈ Why This Structure?\
Express-specific logic stays here (e.g., req, res)\
Business logic is in the service layer (so itβs framework-agnostic)
π― Benefit:\
π‘ Can switch from Express to Fastify/NestJS by just changing the controllers.
### 3οΈβ£ Services (services/)
β
Contains core business logic\
β
Does NOT depend on Express or Prisma\
β
Interacts with repositories for data retrieval\
π Example: user.service.ts
```ts
import { UserRepository } from "../repositories/user.repository";
export class UserService {
private userRepository: UserRepository;
constructor() {
this.userRepository = new UserRepository();
}
async getAllUsers() {
return await this.userRepository.getUsers();
}
}
```
### π οΈ Why This Structure?
- No dependency on Express or HTTP requests
- Calls repository for database access
π― Benefit:\
π‘ Can be reused in a CLI app, background worker, or GraphQL API without changes.
### 4οΈβ£ Repositories (repositories/)
β
Handles all database queries\
β
Uses Prisma (or any ORM, easily replaceable)\
β
Interacts only with services/, never controllers
π Example: user.repository.ts
```ts
import { prisma } from "@/config/prisma.config";
export class UserRepository {
async getUsers() {
return await prisma.user.findMany();
}
}
```
### π οΈ Why This Structure?
- Keeps database logic separate from business logic
- Easy to swap Prisma for another ORM (e.g., Drizzle, TypeORM)
π― Benefit:\
π‘ Can change the database or ORM without affecting services/controllers.
### 5οΈβ£ Routes (routes/)
β
Defines API endpoints\
β
Maps controllers to Express routes
π Example: user.routes.ts
```ts
import { Router } from "express";
import { UserController } from "../controllers/user.controller";
const router = Router();
const userController = new UserController();
router.get("/", (req, res) => userController.getUsers(req, res));
export default router;
```
### π οΈ Why This Structure?
- Controllers are injected into routes for better testability
- Only Express-dependent part is here
π― Benefit:\
π‘ Can switch to NestJS, Fastify, or Hono by only changing routes & controllers.
### 6οΈβ£ Validation Schemas (schemas/)
β
Uses Zod for request validation
β
Completely framework-independent
π Example: user.schema.ts
```ts
import { z } from "zod";
export const createUserSchema = z.object({
email: z.string().email(),
name: z.string().optional(),
});
```
### π οΈ Why This Structure?
- Schemas donβt depend on Express, so they can be used anywhere
- Validation logic is reusable (can be used in GraphQL, CLI, or workers)
-
π― Benefit:\
π‘ Easier to enforce validation rules across different application layers.
### π οΈ Final Benefits Summary
Layer
Purpose
Benefit
Controllers
Handle HTTP requests
Framework-dependent, easily replaceable
Services
Business logic
Framework-agnostic, reusable anywhere
Repositories
Database interactions
Can switch ORM (Prisma, TypeORM, Drizzle)
Routes
Maps controllers to APIs
Only responsible for Express routing
Schemas
Data validation
Reusable validation logic across app
## β¨ Setup from scratch
## β‘ TypeScript & Development Dependencies Setup
```bash
mkdir express-ts-prisma && cd express-ts-prisma
npm init -y
```
```bash
npm install --save-dev typescript tsx nodemon @types/node tsc-alias
```
> Create `tsconfig.json`
## β‘ Add Express, CORS, and .env Setup
```bash
npm install express cors dotenv
npm install --save-dev @types/express @types/cors
```
> Create `.env.dev` File from `.env.example`
> Create src/config/env-config.ts // Env Configuration file
> Create src/config/env-schema.ts // Schema for environment variables
## β‘ ESLint, Prettier & Linting Plugins
```bash
npm install --save-dev eslint prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-prettier eslint-plugin-node eslint-plugin-import eslint-plugin-simple-import-sort eslint-plugin-unicorn eslint-plugin-security eslint-config-prettier
```
> Create `eslint.config.js`
> Create `.prettierrc.json`
> Create `.prettierignore`
π Prettier will ignore these files & folders (same format as `.gitignore`).
> create `.vscode/settings.json` to Autoformat using Prettier on save
## β‘ Setup Prisma & PostgreSQL
> Create Database and Shadow Database
> Update `.env.dev` File
```ini
DATABASE_URL="postgresql://dev_user:dev_password@localhost:5432/dev_db"
SHADOW_DATABASE_URL="postgresql://dev_user:dev_password@localhost:5432/dev_db_shadow"
```
### Install Prisma
```bash
npm install @prisma/client
npm install --save-dev prisma
```
### Initialize Prisma
```bash
npx prisma init
```
### Modify prisma/schema.prisma
```js
model User {
id String @id @default(uuid())
email String @unique
name String?
createdAt DateTime @default(now())
}
```
### Run Migrations
```bash
npx prisma generate
npx prisma migrate dev --name init
```
## β‘ Create Express Server
### Create `src/server.ts`
## β‘ Setup Husky + Lint-Staged
```bash
npm install --save-dev husky lint-staged
```
### Enable Husky
```bash
npx husky install
npm set-script prepare "husky install"
```
### Add Pre-commit Hook
```bash
npx husky add .husky/pre-commit "npx lint-staged"
```
Modify `package.json`
```json
// Runs linters (ESLint, Prettier) only on changed files before committing.
"lint-staged": {
"**/*.{ts,json,md}": ["eslint --fix", "prettier --write"]
}
```
Add Pre-Push Hook
```sh
// Before git push trigger tests & build validation.
npx husky add .husky/pre-push "npm run lint && npm run format && npm run test && npm run build"
```
## β‘ Add Scripts in package.json
```json
"scripts": {
"prebuild": "npm run lint && npm run format && npm run test",
"build": "tsc",
"start": "node dist/server.js",
"dev": "nodemon --ext ts --exec tsx src/server.ts",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext ts --fix",
"format": "prettier --write .",
"test": "vitest",
"prepare": "npx husky install"
}
```
## β‘ Run the Project
```bash
# Start Dev Server
npm run dev
# Lint Code
npm run lint
npm run lint:fix
# Format Code
npm run format
```
## β‘ Vitest for Unit Testing
```bash
npm install --save-dev vitest @vitest/coverage-v8 @types/jest supertest @types/supertest
```
Create test files at `src\features\user\__tests__`
## β‘ Security
```sh
npm i helmet express-rate-limit
```
## β‘ Logger
```bash
npm install pino pino-pretty pino-http
npm install -D @types/pino @types/pino-pretty @types/pino-http
```
Create src\middleware\pino-logger.ts
## β‘ Constants
## β‘ Middleware
## β‘ Utils
If you liked it then please show your love by β the repo