| name | express-api-scaffold |
| description | Generate production-ready Express.js REST API with TypeScript and auth
|
| shortcut | eas |
| category | backend |
| difficulty | intermediate |
| estimated_time | 5-10 minutes |
Express API Scaffold
Generates a complete Express.js REST API boilerplate with TypeScript, authentication, database integration, and testing setup.
What This Command Does
Generated Project:
- Express.js with TypeScript
- JWT authentication
- Database integration (Prisma or TypeORM)
- Input validation (Zod)
- Error handling middleware
- Rate limiting & security (Helmet, CORS)
- Testing setup (Jest + Supertest)
- Docker configuration
- Example CRUD endpoints
Output: Complete API project ready for development
Time: 5-10 minutes
Usage
/express-api-scaffold "Task Management API"
/eas "E-commerce API"
/eas "Blog API" --database postgresql
/eas "Social API" --auth jwt --database mongodb
Example Output
Input:
/eas "Task Management API" --database postgresql
Generated Project Structure:
task-api/
├── src/
│ ├── controllers/ # Request handlers
│ │ ├── auth.controller.ts
│ │ └── task.controller.ts
│ ├── middleware/ # Express middleware
│ │ ├── auth.middleware.ts
│ │ ├── error.middleware.ts
│ │ └── validation.middleware.ts
│ ├── models/ # Database models
│ │ └── task.model.ts
│ ├── routes/ # API routes
│ │ ├── auth.routes.ts
│ │ └── task.routes.ts
│ ├── services/ # Business logic
│ │ ├── auth.service.ts
│ │ └── task.service.ts
│ ├── utils/ # Utilities
│ │ ├── jwt.util.ts
│ │ └── password.util.ts
│ ├── config/ # Configuration
│ │ └── database.ts
│ ├── types/ # TypeScript types
│ │ └── express.d.ts
│ ├── app.ts # Express app setup
│ └── server.ts # Server entry point
├── tests/
│ ├── auth.test.ts
│ └── task.test.ts
├── prisma/
│ └── schema.prisma # Database schema
├── .env.example
├── .gitignore
├── package.json
├── tsconfig.json
├── jest.config.js
├── Dockerfile
├── docker-compose.yml
└── README.md
Generated Files
1. src/server.ts (Entry Point)
import app from './app'
import { config } from './config'
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
console.log(`Environment: ${process.env.NODE_ENV}`)
})
2. src/app.ts (Express Setup)
import express, { Application } from 'express'
import cors from 'cors'
import helmet from 'helmet'
import morgan from 'morgan'
import rateLimit from 'express-rate-limit'
import authRoutes from './routes/auth.routes'
import taskRoutes from './routes/task.routes'
import { errorHandler } from './middleware/error.middleware'
import { notFoundHandler } from './middleware/notFound.middleware'
const app: Application = express()
app.use(helmet())
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
credentials: true
}))
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
: ,
:
})
app.(, limiter)
app.(express.())
app.(express.({ : }))
(process.. !== ) {
app.(())
}
app.(, {
res.({ : , : ().() })
})
app.(, authRoutes)
app.(, taskRoutes)
app.(notFoundHandler)
app.(errorHandler)
app
3. src/controllers/auth.controller.ts
import { Request, Response, NextFunction } from 'express'
import { AuthService } from '../services/auth.service'
import { ApiError } from '../utils/ApiError'
const authService = new AuthService()
export class AuthController {
async register(req: Request, res: Response, next: NextFunction) {
try {
const { email, password, name } = req.body
const result = await authService.register({ email, password, name })
res.status(201).json({
data: {
user: result.user,
token: result.token
}
})
} catch (error) {
next(error)
}
}
async login(req: Request, res: Response, : ) {
{
{ email, password } = req.
result = authService.(email, password)
res.({
: {
: result.,
: result.
}
})
} (error) {
(error)
}
}
() {
{
userId = req.!.
user = authService.(userId)
res.({ : user })
} (error) {
(error)
}
}
}
4. src/middleware/auth.middleware.ts
import { Request, Response, NextFunction } from 'express'
import jwt from 'jsonwebtoken'
import { ApiError } from '../utils/ApiError'
interface JwtPayload {
userId: string
email: string
}
declare global {
namespace Express {
interface Request {
user?: {
id: string
email: string
}
}
}
}
export function authenticate(req: Request, res: Response, next: NextFunction) {
try {
const authHeader = req.headers.authorization
if (!authHeader?.startsWith('Bearer ')) {
throw new ApiError(401, 'No token provided')
}
const token = authHeader.()[]
decoded = jwt.(
token,
process..!
)
req. = {
: decoded.,
: decoded.
}
()
} (error) {
(error jwt.) {
( (, ))
} {
(error)
}
}
}
5. src/middleware/error.middleware.ts
import { Request, Response, NextFunction } from 'express'
import { ApiError } from '../utils/ApiError'
import { ZodError } from 'zod'
export function errorHandler(
err: Error,
req: Request,
res: Response,
next: NextFunction
) {
console.error('Error:', err)
if (err instanceof ApiError) {
return res.status(err.statusCode).json({
error: {
code: err.name,
message: err.message,
...(err.details && { details: err.details })
}
})
}
if (err instanceof ZodError) {
return res.status(400).({
: {
: ,
: ,
: err..( ({
: e..(),
: e.
}))
}
})
}
res.().({
: {
: ,
: process.. ===
?
: err.
}
})
}
6. src/routes/task.routes.ts
import { Router } from 'express'
import { TaskController } from '../controllers/task.controller'
import { authenticate } from '../middleware/auth.middleware'
import { validate } from '../middleware/validation.middleware'
import { createTaskSchema, updateTaskSchema } from '../schemas/task.schema'
const router = Router()
const taskController = new TaskController()
router.use(authenticate)
router.get('/', taskController.list)
router.post('/', validate(createTaskSchema), taskController.create)
router.get('/:id', taskController.getById)
router.patch('/:id', validate(updateTaskSchema), taskController.update)
router.delete('/:id', taskController.delete)
export default router
7. src/services/task.service.ts
import { PrismaClient } from '@prisma/client'
import { ApiError } from '../utils/ApiError'
const prisma = new PrismaClient()
export class TaskService {
async create(userId: string, data: { title: string; description?: string }) {
return await prisma.task.create({
data: {
...data,
userId
}
})
}
async findAll(userId: string) {
return await prisma.task.findMany({
where: { userId },
orderBy: { createdAt: 'desc' }
})
}
async findById(id: string, userId: string) {
const task = await prisma.task.findUnique({
where: { id }
})
if (!task) {
(, )
}
(task. !== userId) {
(, )
}
task
}
() {
.(id, userId)
prisma..({
: { id },
data
})
}
() {
.(id, userId)
prisma..({
: { id }
})
}
}
8. prisma/schema.prisma (Database Schema)
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
password String
name String
tasks Task[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
model Task {
id String @id @default(uuid())
title String
description String?
completed Boolean @default(false)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@map("tasks")
}
9. tests/task.test.ts (Integration Tests)
import request from 'supertest'
import app from '../src/app'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
describe('Task API', () => {
let authToken: string
let userId: string
beforeAll(async () => {
const res = await request(app)
.post('/api/auth/register')
.send({
email: '[email protected]',
password: 'password123',
name: 'Test User'
})
authToken = res.body.data.token
userId = res.body.data.user.id
})
afterAll(async () => {
await prisma.task.deleteMany({ where: { userId } })
prisma..({ : { : userId } })
prisma.$disconnect()
})
(, {
(, () => {
res = (app)
.()
.(, )
.({
: ,
:
})
(res.).()
(res..).()
(res...).()
})
(, () => {
res = (app)
.()
.({ : })
(res.).()
})
})
(, {
(, () => {
res = (app)
.()
.(, )
(res.).()
(.(res..)).()
})
})
})
10. package.json
{
"name": "task-api",
"version": "1.0.0",
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
"build": "tsc",
"start": "node dist/server.js",
"test": "jest --coverage",
"lint": "eslint src/**/*.ts",
"format": "prettier --write \"src/**/*.ts\"",
"db:migrate": "prisma migrate dev",
"db:push": "prisma db push",
"db:generate": "prisma generate"
},
"dependencies": {
"express": "^4.18.2",
Features
Security:
- Helmet.js for HTTP headers
- CORS with configurable origins
- Rate limiting (100 req/15min)
- JWT authentication
- Password hashing (bcrypt)
- Input validation (Zod)
Database:
- Prisma ORM with TypeScript
- Automatic migrations
- Type-safe queries
- Supports PostgreSQL, MySQL, SQLite
Testing:
- Jest + Supertest
- Integration tests
- Coverage reporting
- Test database isolation
Development:
- Hot reload (ts-node-dev)
- TypeScript with strict mode
- ESLint + Prettier
- Environment variables
Production:
- Docker support
- Health check endpoint
- Error logging
- Graceful shutdown
Getting Started
1. Install dependencies:
npm install
2. Configure environment:
cp .env.example .env
3. Run database migrations:
npm run db:migrate
4. Start development server:
npm run dev
5. Run tests:
npm test
Related Commands
/fastapi-scaffold - Generate FastAPI boilerplate
- Backend Architect (agent) - Architecture review
- API Builder (agent) - API design guidance
Build production-ready APIs. Ship faster. Scale confidently.