Skip to content

sw3do/example-user-backend-nestjs

Repository files navigation

πŸ” NestJS Authentication API with MariaDB

Modern, secure, and production-ready authentication system built with NestJS, TypeORM, MariaDB, and JWT.

NestJS TypeScript MariaDB JWT

✨ Features

πŸ”’ Security First

  • Password Hashing: bcrypt with 12 rounds (industry standard)
  • JWT Authentication: Secure token-based authentication
  • Account Locking: Automatic lock after 5 failed login attempts (15 minutes)
  • Password Requirements: Strong password validation
  • Email Normalization: Automatic lowercase and trim
  • SQL Injection Protection: TypeORM parameterized queries
  • XSS Protection: Input validation and sanitization
  • CORS Configuration: Configurable cross-origin resource sharing

🎯 Core Features

  • User Registration with validation
  • User Login with secure password verification
  • Protected routes with JWT Guards
  • User profile endpoint
  • Login attempt tracking
  • Account lock/unlock mechanism
  • Email uniqueness validation

πŸ›  Technical Stack

  • Framework: NestJS 11
  • Language: TypeScript 5
  • Database: MariaDB/MySQL
  • ORM: TypeORM
  • Authentication: Passport JWT
  • Validation: class-validator & class-transformer
  • Password Hashing: bcrypt

πŸ“‹ Prerequisites

  • Node.js (v18 or higher)
  • npm or yarn
  • MariaDB or MySQL (v10.5+)

πŸš€ Quick Start

1. Clone & Install

git clone https://github.com/sw3do/example-user-backend-nestjs.git
cd example-user-backend-nestjs
npm install

2. Database Setup

Option A: Using Docker (Recommended)

docker run -d \
  --name mariadb \
  -e MYSQL_ROOT_PASSWORD=123123 \
  -e MYSQL_DATABASE=nestjs_auth \
  -p 3306:3306 \
  mariadb:latest

Option B: Local Installation

# macOS
brew install mariadb
brew services start mariadb

# Ubuntu/Debian
sudo apt install mariadb-server
sudo systemctl start mariadb

# Create database
mysql -u root -p -e "CREATE DATABASE nestjs_auth CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

3. Environment Configuration

Copy the example env file and configure:

cp .env.example .env

Edit .env:

NODE_ENV=development

DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=your_password
DB_NAME=nestjs_auth

JWT_SECRET=change-this-to-a-very-secure-random-string-min-32-chars
JWT_EXPIRES_IN=7d

CORS_ORIGIN=http://localhost:3000
PORT=3000

⚠️ Important: Change JWT_SECRET to a strong, random string in production!

4. Run Application

# Development mode with hot reload
npm run start:dev

# Production mode
npm run build
npm run start:prod

The API will be available at http://localhost:3000

πŸ“‘ API Endpoints

Base URL

http://localhost:3000/api

1. Register New User

POST /api/auth/register

Request:

{
  "email": "user@example.com",
  "password": "SecureP@ss123",
  "firstName": "John",
  "lastName": "Doe"
}

Response (201):

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe"
  }
}

2. Login

POST /api/auth/login

Request:

{
  "email": "user@example.com",
  "password": "SecureP@ss123"
}

Response (200):

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe"
  }
}

3. Get Profile (Protected)

GET /api/auth/profile

Headers:

Authorization: Bearer <your_access_token>

Response (200):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "firstName": "John",
  "lastName": "Doe"
}

πŸ§ͺ Testing the API

Using cURL

Register:

curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "Test@123456",
    "firstName": "John",
    "lastName": "Doe"
  }'

Login:

curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "Test@123456"
  }'

Get Profile:

curl -X GET http://localhost:3000/api/auth/profile \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

Using the Test Script

Run the automated test script:

chmod +x test-api.sh
./test-api.sh

πŸ” Security Features in Detail

Password Requirements

  • βœ… Minimum 8 characters
  • βœ… Maximum 128 characters
  • βœ… At least one uppercase letter
  • βœ… At least one lowercase letter
  • βœ… At least one number OR special character
  • βœ… Bcrypt hashing with 12 salt rounds

Account Protection

  • πŸ”’ Login attempts are tracked
  • πŸ”’ Account locks after 5 failed attempts
  • πŸ”’ Lock duration: 15 minutes
  • πŸ”’ Automatic unlock after duration
  • πŸ”’ Failed attempts reset on successful login

JWT Security

  • 🎫 Token expiration: 7 days (configurable)
  • 🎫 Secret key from environment variables
  • 🎫 Bearer token authentication
  • 🎫 Token validation on protected routes

Database Security

  • πŸ’Ύ UUID primary keys
  • πŸ’Ύ Unique email constraint
  • πŸ’Ύ Password field excluded from queries by default
  • πŸ’Ύ UTF-8MB4 charset (emoji support)
  • πŸ’Ύ Automatic timestamps

πŸ“ Project Structure

src/
β”œβ”€β”€ auth/                           # Authentication module
β”‚   β”œβ”€β”€ dto/                        # Data Transfer Objects
β”‚   β”‚   β”œβ”€β”€ register.dto.ts        # Registration validation
β”‚   β”‚   └── login.dto.ts           # Login validation
β”‚   β”œβ”€β”€ guards/                     # Route guards
β”‚   β”‚   └── jwt-auth.guard.ts      # JWT authentication guard
β”‚   β”œβ”€β”€ strategies/                 # Passport strategies
β”‚   β”‚   └── jwt.strategy.ts        # JWT strategy implementation
β”‚   β”œβ”€β”€ auth.controller.ts         # Auth endpoints
β”‚   β”œβ”€β”€ auth.service.ts            # Auth business logic
β”‚   └── auth.module.ts             # Auth module definition
β”œβ”€β”€ users/                          # Users module
β”‚   β”œβ”€β”€ entities/                   # Database entities
β”‚   β”‚   └── user.entity.ts         # User entity with hooks
β”‚   β”œβ”€β”€ users.service.ts           # User business logic
β”‚   └── users.module.ts            # Users module definition
β”œβ”€β”€ app.module.ts                   # Root module
└── main.ts                         # Application entry point

πŸ”§ Configuration

Environment Variables

Variable Description Default Required
NODE_ENV Environment mode development No
PORT Server port 3000 No
DB_HOST Database host localhost No
DB_PORT Database port 3306 No
DB_USERNAME Database username root Yes
DB_PASSWORD Database password - Yes
DB_NAME Database name nestjs_auth Yes
JWT_SECRET JWT secret key - Yes
JWT_EXPIRES_IN Token expiration 7d No
CORS_ORIGIN Allowed CORS origin http://localhost:3000 No

🐳 Docker Support

Docker Compose Setup

Create docker-compose.yml:

version: '3.8'

services:
  mariadb:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: 123123
      MYSQL_DATABASE: nestjs_auth
    ports:
      - "3306:3306"
    volumes:
      - mariadb_data:/var/lib/mysql

volumes:
  mariadb_data:

Run:

docker-compose up -d

πŸ“Š Error Handling

Common Error Responses

400 Bad Request - Validation Error

{
  "statusCode": 400,
  "message": [
    "Password must be at least 8 characters long",
    "Email must be a valid email"
  ],
  "error": "Bad Request"
}

401 Unauthorized - Invalid Credentials

{
  "statusCode": 401,
  "message": "Invalid credentials",
  "error": "Unauthorized"
}

409 Conflict - Email Already Exists

{
  "statusCode": 409,
  "message": "Email already exists",
  "error": "Conflict"
}

🧩 Validation Rules

Registration

  • Email: Valid email format, unique
  • Password: 8-128 chars, uppercase, lowercase, number/special char
  • First Name: 2-100 chars
  • Last Name: 2-100 chars

Login

  • Email: Valid email format
  • Password: Required string

🚦 Development

Available Scripts

# Development
npm run start:dev       # Start with hot reload

# Production
npm run build          # Build application
npm run start:prod     # Run production build

# Code Quality
npm run format         # Format code with Prettier
npm run lint          # Lint code with ESLint
npm run lint:fix      # Fix linting issues

πŸ“ API Testing Examples

Postman Collection

Import these endpoints to Postman:

  1. Register: POST http://localhost:3000/api/auth/register
  2. Login: POST http://localhost:3000/api/auth/login
  3. Profile: GET http://localhost:3000/api/auth/profile

Thunder Client / REST Client

Create api.http:

### Register
POST http://localhost:3000/api/auth/register
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "Test@123456",
  "firstName": "John",
  "lastName": "Doe"
}

### Login
POST http://localhost:3000/api/auth/login
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "Test@123456"
}

### Profile (Replace TOKEN)
GET http://localhost:3000/api/auth/profile
Authorization: Bearer YOUR_TOKEN_HERE

πŸ› Troubleshooting

Database Connection Issues

  1. Check MariaDB is running:
# macOS
brew services list

# Linux
sudo systemctl status mariadb

# Docker
docker ps
  1. Verify credentials in .env
  2. Check database exists:
mysql -u root -p -e "SHOW DATABASES;"

Port Already in Use

Change port in .env:

PORT=3001

JWT Token Invalid

  1. Check JWT_SECRET is set in .env
  2. Verify token hasn't expired (default 7 days)
  3. Check Authorization header format: Bearer <token>

πŸ“š Learn More

🀝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License.

πŸ‘¨β€πŸ’» Author

Created by @sw3do with ❀️ using NestJS


⭐ If you find this project helpful, please give it a star!

About

Modern, secure, and production-ready authentication system built with NestJS, TypeORM, MariaDB, and JWT.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published