AuthForge is a production-inspired authentication API built with NestJS, showcasing modern backend architecture, secure JWT authentication, Drizzle ORM, PostgreSQL, Redis, BullMQ, and interchangeable infrastructure providers.
Originally started from a YouTube authentication course, the project has since evolved into a significantly enhanced codebase featuring modular architecture, asynchronous email processing, infrastructure abstraction, Docker-based local development, and numerous quality-of-life improvements that better reflect real-world backend development practices.
🔗 Repository: https://github.com/nmrisrl11/nestjs-authentication-api
- Framework: NestJS (Express under the hood)
- Database: PostgreSQL (via Neon or Local Docker)
- ORM: Drizzle ORM
- Caching / Queues: Redis (via Upstash or Local Docker) & BullMQ
- Email Providers: Nodemailer / Resend
- Validation & Type Safety: Zod & T3 Env
- API Documentation: Swagger UI
- Code Quality: Prettier, ESLint, Greptile
- Custom Authentication: Full JWT-based auth flow (Access & Refresh tokens).
- Token Management: Hashing/saving refresh tokens to the DB and setting them in HTTP-only client cookies.
- Account Verification: Email verification route and user creation upon registration.
- Password Recovery: Forgot password flow with expiring reset tokens.
- Role-Based Access Control (RBAC): Dedicated Admin module for listing/deleting users.
- Rate Limiting: Integrated NestJS Throttler.
- Task Module: Basic CRUD operations for tasks.
- Modular Architecture: Strictly decoupled codebase with a dedicated
modules/layer. - Email Service Abstraction: Separated email logic into its own module. Created an abstract provider token to seamlessly swap between
NodemailerandResendusing NestJS'suseClass. - Background Jobs (BullMQ + Redis): Offloaded email sending from the main execution thread to a background queue. Includes Bull Board integration for a visual queue dashboard (protected by middleware).
- Type-Safe Environments: Opted into
@t3-oss/env-coreto guarantee environment variables are present and correctly typed at build time. - Drizzle ORM Injectable: Converted Drizzle database initialization into an injectable NestJS Module.
- Custom CLI Tooling: Built a custom script (
npm run create:resource) to quickly scaffold a Module, Service, and Controller inside the modules layer. - Security & Data Privacy:
- Admin users are prevented from deleting their own accounts.
- Sensitive user data is filtered out before being returned in admin endpoints.
- Proper UUID validation across all routes expecting UUIDs.
- Resilience: Added a fallback method for resending verification emails if the initial registration email fails.
- Code Quality: Enforced a project-wide Prettier configuration (tabs over spaces, double quotes, semicolons), unified DTOs, and integrated Greptile for PR vulnerability/quality checks.
This project is built to be environment-agnostic, allowing you to easily swap out infrastructure providers just by changing variables in your .env file.
- Neon: Set
DATABASE_URLto your Neon serverless Postgres connection string. - Docker: Set
DATABASE_URLto point tolocalhost:5432and usenpm run services:upto spin up the local Postgres container.
- Upstash: Provide your Upstash Redis connection string to
REDIS_URL. - Docker: Point
REDIS_URLto your local Docker Redis instance (redis://localhost:6379).
Change the EMAIL_PROVIDER env variable to either nodemailer or resend. The application will automatically inject the correct provider class at runtime. Handlebars templates are fully supported on both ends.
Create a .env file in the root directory. You can use the .env.example as a template:
NODE_ENV= # "development", "test", or "production"
# --- DATABASE ---
DATABASE_URL= # Neon URL or Docker local Postgres URL
POSTGRES_USER= # Local Docker DB User
POSTGRES_PASSWORD= # Local Docker DB Password
POSTGRES_DB= # Local Docker DB Name
# --- AUTHENTICATION ---
# Generate via: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_ACCESS_SECRET=
JWT_ACCESS_EXPIRES_IN=
JWT_REFRESH_SECRET=
JWT_REFRESH_EXPIRES_IN=
# --- APP CONFIG ---
APP_URL=http://localhost:3000
PORT=3000
# --- EMAIL PROVIDERS ---
EMAIL_PROVIDER= # "nodemailer" or "resend"
RESEND_API_KEY=
FROM_EMAIL= # Default to onboarding@resend.dev if using Resend
# Nodemailer SMTP Config
SMTP_HOST= # e.g., smtp.gmail.com
SMTP_PORT= # 587 or 465
SMTP_SECURE= # true for 465, false for 587
SMTP_USER= # Your email address
SMTP_PASS= # App Password
# --- QUEUES (REDIS) ---
REDIS_URL= # Upstash URL or Local Docker Redis URL
BULL_BOARD_USER= # Optional: Dashboard username
BULL_BOARD_PASSWORD= # Optional: Dashboard password
- Installation
npm install
- Database Migrations Generate and push the Drizzle schema to your database:
npm run db:generate
npm run db:push
- Running the App Option A: Fully Local (With Docker) Runs the NestJS server alongside PostgreSQL and Redis containers.
npm run dev:full
Note: If you stop the app using Ctrl+C, the Docker containers will remain running in the background. To stop them, run npm run services:down.
Option B: Standard Development (Cloud DB/Redis)
Runs just the NestJS server. Ensure your DATABASE_URL and REDIS_URL are pointing to your cloud providers (Neon/Upstash). Note: If no Redis is connected, background queues will be mocked/fail.
npm run start:dev
- Code Generation Tool To quickly generate a new resource module inside the src/modules folder:
npm run create:resource
Throughout the development process, specific features were isolated into their own branches for easier tracking. You can explore the repository branches to see the integration.
(Check the GitHub branches tab for the full list)
Building AuthForge went far beyond a standard tutorial. Key takeaways from scaling this architecture include:
-
Dependency Injection & Abstraction: Learning to use NestJS's useClass to create abstract contracts (like the Email Provider) made the codebase incredibly flexible, allowing me to swap between Resend and Nodemailer without touching the core authentication logic.
-
Background Processing: Integrating BullMQ and Redis taught me the importance of offloading heavy tasks (like sending emails) to prevent blocking the main event loop, significantly improving API response times.
-
Type-Safe Environments: Catching misconfigured .env variables at build time with T3 Env (Zod) prevented hard-to-debug runtime errors related to missing database URLs or JWT secrets.
-
Modular Monolith Design: Moving away from a flat structure and grouping features into a cohesive modules/ layer improved maintainability and enforced the DRY principle across services.
-
Infrastructure Automation: Integrating Docker Compose for local development made the transition between local and cloud providers (Neon/Upstash) seamless, deeply improving my local developer experience (DX).
Initial inspiration and core authentication flow derived from the NestJS Authentication Full Course by code with lari.