Built a production-grade REST API with JWT authentication, refresh token rotation, rate limiting, and auto-generated OpenAPI documentation, using TypeScript, Express, Drizzle ORM, and PostgreSQL.
- JWT authentication with access/refresh token rotation
- Role-based CRUD for users, projects, and tasks
- Rate limiting and HTTP security headers
- Input sanitization and Zod validation
- Auto-generated Swagger/OpenAPI documentation
- GitHub Actions CI/CD pipeline
- One-click Railway deployment
| Technology | Purpose |
|---|---|
| TypeScript | Strongly typed JavaScript |
| Express | Web framework |
| Drizzle ORM | Type-safe SQL ORM |
| PostgreSQL | Relational database |
| Helmet | Security headers |
| express-rate-limit | Rate limiting |
| Zod | Request validation |
| JWT | Authentication |
| Jest | Testing |
| Swagger UI | API documentation |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
Login |
| POST | /api/auth/refresh |
Refresh access token |
| POST | /api/auth/logout |
Revoke refresh token |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/users/me |
Get current user profile | Yes |
| PUT | /api/users/me |
Update profile | Yes |
| PUT | /api/users/me/password |
Change password | Yes |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/projects |
List projects (paginated) | Yes |
| GET | /api/projects/:id |
Get project by ID | Yes |
| POST | /api/projects |
Create project | Yes |
| PUT | /api/projects/:id |
Update project | Yes |
| DELETE | /api/projects/:id |
Delete project | Yes |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/tasks |
List tasks (paginated) | Yes |
| GET | /api/tasks/:id |
Get task by ID | Yes |
| POST | /api/tasks |
Create task | Yes |
| PUT | /api/tasks/:id |
Update task | Yes |
| DELETE | /api/tasks/:id |
Delete task | Yes |
- Node.js 20+
- PostgreSQL 16+
git clone https://github.com/casper-justus/api-platform.git
cd api-platform
npm installCopy .env.example to .env and update the values:
cp .env.example .envnpm run db:migratenpm startAPI runs at http://localhost:3000
- Swagger docs:
http://localhost:3000/api-docs - Health check:
http://localhost:3000/health
npm testAll required variables — set these in the Railway Variables tab or in your local .env file.
| Variable | Required | Description | Example |
|---|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string. Auto-injected by the Railway PostgreSQL plugin. | postgresql://user:pass@host:5432/db |
JWT_SECRET |
✅ | Secret key for signing access tokens. Use a long random string. | openssl rand -hex 64 |
JWT_REFRESH_SECRET |
✅ | Separate secret for signing refresh tokens. Must differ from JWT_SECRET. |
openssl rand -hex 64 |
JWT_EXPIRES_IN |
✅ | Access token lifetime. Keep short for security. | 15m |
JWT_REFRESH_EXPIRES_IN |
✅ | Refresh token lifetime. | 7d |
PORT |
✅ | Port the server listens on. Railway injects this automatically. | 3000 |
NODE_ENV |
✅ | Runtime environment. | production |
CORS_ORIGIN |
Optional | Allowed CORS origin. Defaults to *. Set to your frontend URL in production. |
https://your-frontend.vercel.app |
Tip: Generate secure secrets with
openssl rand -hex 64and never reuse the same value forJWT_SECRETandJWT_REFRESH_SECRET.
api-platform/
├── .github/workflows/ci.yml # CI/CD pipeline
├── src/
│ ├── app.ts # Express app setup
│ ├── config/
│ │ └── swagger.ts # Swagger configuration
│ ├── db/
│ │ ├── connection.ts # Database connection
│ │ ├── middleware/ # Auth, validation, security
│ │ ├── migrations/ # Drizzle migrations
│ │ └── schema/ # Drizzle schema definitions
│ └── modules/
│ ├── auth/ # Authentication logic
│ ├── users/ # User endpoints
│ ├── projects/ # Project endpoints
│ └── tasks/ # Task endpoints
├── test/ # Test suites
├── .env.example # Environment template
├── drizzle.config.ts # Drizzle configuration
├── jest.config.json # Jest configuration
├── railway.json # Railway deployment config
└── tsconfig.json # TypeScript configuration
- Sign up at Railway
- Connect your GitHub repository
- Add a PostgreSQL database service — Railway will auto-inject
DATABASE_URL - Set all required environment variables from the table above in the Variables tab
- Deploy — Railway handles build and start automatically
MIT