Skip to content

Repository files navigation

Social Platform API

TypeScript NestJS Drizzle ORM JWT PostgreSQL Docker Swagger

A robust NestJS REST API for a social blogging platform with JWT authentication, user management, posts, comments, and groups. Built with TypeScript, Drizzle ORM, and PostgreSQL.

πŸš€ Features

Core Functionality

  • Authentication & Authorization - JWT-based auth with secure password hashing (bcrypt)
  • User Management - Complete CRUD operations with profile support
  • Posts & Comments - Create, read, update, and delete blog posts and comments
  • Groups - Create and manage groups with member roles (owner/member)
  • Profile System - Rich user profiles with metadata (bio, avatar, social links, preferences)

Technical Features

  • Type-Safe ORM - Drizzle ORM with full TypeScript support
  • API Documentation - Auto-generated Swagger/OpenAPI documentation
  • Validation - Class-validator with comprehensive DTO validation
  • Error Handling - Global exception filters with detailed error responses
  • Rate Limiting - Built-in throttling (100 requests/minute)
  • Health Checks - Database health monitoring endpoint
  • Security - Password hashing, JWT tokens, ownership guards

πŸ“‹ Database Schema

Database Schema

πŸ› οΈ Tech Stack

  • Framework: NestJS 11.0.1
  • Language: TypeScript 5.7.3
  • Database: PostgreSQL
  • ORM: Drizzle ORM
  • Authentication: JWT (jsonwebtoken)
  • Validation: class-validator, class-transformer
  • Documentation: Swagger/OpenAPI (@nestjs/swagger)
  • Security: bcrypt, @nestjs/throttler
  • Health Checks: @nestjs/terminus

πŸ“¦ Installation

Prerequisites

  • Node.js 22
  • PostgreSQL 17
  • pnpm

Setup

  1. Clone the repository
git clone https://github.com/juniorenv/nestjs-social-api.git
cd nestjs-social-api
  1. Install dependencies
pnpm install
  1. Environment configuration

Create a .env file in the root directory based on .env.example:

cp .env.example .env

For Docker deployment (recommended):

# Database Configuration
POSTGRES_USER=pguser
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_DB=nestjsdrizzle
POSTGRES_PORT=5432

# API Configuration
API_PORT=3000
NODE_ENV=development

# JWT Configuration
JWT_SECRET=your_jwt_secret_here
JWT_EXPIRATION=3600

The DATABASE_URL will be automatically constructed by the application using the individual PostgreSQL variables above.

For local PostgreSQL (without Docker):

Add the DATABASE_URL to your .env file:

# Database Configuration
DATABASE_URL=postgresql://pguser:your_secure_password_here@localhost:5432/nestjsdrizzle

# Or use individual variables (the app will construct the URL)
POSTGRES_USER=pguser
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_DB=nestjsdrizzle
POSTGRES_PORT=5432

# API Configuration
API_PORT=3000
NODE_ENV=development

# JWT Configuration
JWT_SECRET=your_jwt_secret_here
JWT_EXPIRATION=3600
  1. Database setup

Generate migration:

pnpm db:generate

Run migrations:

pnpm db:migrate

Seed database (optional):

pnpm db:seed
  1. Start the application

Development mode:

pnpm dev

Production mode:

pnpm build
pnpm start:prod

Docker Deployment (Recommended)

The project includes Docker Compose configuration for easy deployment:

  1. Ensure Docker and Docker Compose are installed

  2. Configure environment

cp .env.example .env
# Edit .env with your configuration
  1. Start all services
docker compose up -d

This will start:

  • PostgreSQL database container
  • NestJS API container
  1. View logs
docker compose logs -f
  1. Stop services
docker compose down
  1. Stop and remove volumes (database data)
docker compose down -v

πŸ“š API Documentation

Once the application is running, access the interactive API documentation at:

http://localhost:3000/docs

The Swagger UI provides:

  • Complete endpoint documentation
  • Request/response schemas
  • Try-it-out functionality
  • Authentication support

Screenshots

Swagger docs 0 Swagger docs 1 Swagger docs 2

πŸ” Authentication

Sign Up

POST /auth/signup
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john.doe@example.com",
  "password": "SecurePass123!"
}

Response:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "uuid",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "createdAt": "2025-02-10T12:00:00.000Z",
    "updatedAt": "2025-02-10T12:00:00.000Z"
  }
}

Sign In

POST /auth/signin
Content-Type: application/json

{
  "email": "john.doe@example.com",
  "password": "SecurePass123!"
}

Response:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Using the Token

Include the JWT token in the Authorization header for protected endpoints:

GET /users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

πŸ“ API Endpoints

Authentication

Method Endpoint Auth Description
POST /auth/signup No Register new user
POST /auth/signin No Login user

Users

Method Endpoint Auth Description
POST /users No Create user (alternative to signup)
GET /users/:userId No Get user by ID
GET /users/me Yes Get current user profile
PATCH /users/me Yes Update current user
DELETE /users/me Yes Delete current user
PUT /users/me/password Yes Change password
POST /users/me/profile Yes Create profile
PATCH /users/me/profile Yes Update profile

Posts

Method Endpoint Auth Description
POST /posts Yes Create post
GET /posts/:postId No Get post by ID
PATCH /posts/:postId Yes* Update post
DELETE /posts/:postId Yes* Delete post
POST /posts/:postId/comments Yes Add comment to post

*Only the post author can update/delete

Comments

Method Endpoint Auth Description
GET /comments/:commentId No Get comment by ID
PATCH /comments/:commentId Yes* Update comment
DELETE /comments/:commentId Yes* Delete comment

*Only the comment author can update/delete

Groups

Method Endpoint Auth Description
POST /groups Yes Create group
GET /groups/:groupId No Get group by ID
PATCH /groups/:groupId Yes** Update group
DELETE /groups/:groupId Yes** Delete group
POST /groups/:groupId/join Yes Join group
DELETE /groups/:groupId/leave Yes Leave group
DELETE /groups/:groupId/members/:userId Yes** Remove member

**Only group owner can perform these actions

Health

Method Endpoint Auth Description
GET /health No Check system health

πŸ’‘ Usage Examples

Create a Post

curl -X POST http://localhost:3000/posts \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Post",
    "content": "This is the content of my post about TypeScript and NestJS."
  }'

Get Post with Comments

curl http://localhost:3000/posts/post-uuid-here

Response:

{
  "id": "uuid",
  "title": "My First Post",
  "content": "This is the content...",
  "createdAt": "2025-02-10T12:00:00.000Z",
  "updatedAt": "2025-02-10T12:00:00.000Z",
  "author": {
    "id": "uuid",
    "name": "John Doe"
  },
  "comments": [
    {
      "id": "uuid",
      "text": "Great post!",
      "createdAt": "2025-02-10T12:30:00.000Z",
      "updatedAt": "2025-02-10T12:30:00.000Z",
      "author": {
        "id": "uuid",
        "name": "Jane Smith"
      }
    }
  ]
}

Create User Profile

curl -X POST http://localhost:3000/users/me/profile \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "metadata": {
      "bio": "Full-stack developer passionate about TypeScript",
      "avatar": "https://i.pravatar.cc/300",
      "phone": "+5577996483728",
      "location": "Brazil",
      "website": "https://johndoe.dev",
      "socialLinks": {
        "twitter": "https://twitter.com/johndoe",
        "linkedin": "https://linkedin.com/in/johndoe",
        "github": "https://github.com/johndoe"
      },
      "preferences": {
        "theme": "dark",
        "notifications": true,
        "language": "en",
        "emailNotifications": true,
        "timezone": "America/New_York"
      },
      "occupation": "Software Engineer",
      "company": "Tech Corp",
      "skills": ["TypeScript", "React", "Node.js"]
    }
  }'

Create and Join a Group

# Create group
curl -X POST http://localhost:3000/groups \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "TypeScript",
    "description": "A community for TypeScript enthusiasts"
  }'

# Join group
curl -X POST http://localhost:3000/groups/group-uuid-here/join \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

πŸ—οΈ Project Structure

src/
β”œβ”€β”€ auth/                   # Authentication module
β”‚   β”œβ”€β”€ dto/               # Auth DTOs (SignIn, SignUp)
β”‚   β”œβ”€β”€ interfaces/        # JWT payload interface
β”‚   β”œβ”€β”€ auth.controller.ts
β”‚   β”œβ”€β”€ auth.service.ts
β”‚   β”œβ”€β”€ auth.guard.ts      # JWT authentication guard
β”‚   └── auth.module.ts
β”‚
β”œβ”€β”€ user/                  # User management module
β”‚   β”œβ”€β”€ dto/              # User DTOs
β”‚   β”œβ”€β”€ user.controller.ts
β”‚   β”œβ”€β”€ user.service.ts
β”‚   └── user.module.ts
β”‚
β”œβ”€β”€ post/                 # Post management module
β”‚   β”œβ”€β”€ dto/
β”‚   β”œβ”€β”€ post.controller.ts
β”‚   β”œβ”€β”€ post.service.ts
β”‚   └── post.module.ts
β”‚
β”œβ”€β”€ comment/              # Comment module
β”‚   β”œβ”€β”€ dto/
β”‚   β”œβ”€β”€ comment.controller.ts
β”‚   β”œβ”€β”€ comment.service.ts
β”‚   └── comment.module.ts
β”‚
β”œβ”€β”€ group/                # Group management module
β”‚   β”œβ”€β”€ dto/
β”‚   β”œβ”€β”€ group.controller.ts
β”‚   β”œβ”€β”€ group.service.ts
β”‚   └── group.module.ts
β”‚
β”œβ”€β”€ drizzle/              # Database layer
β”‚   β”œβ”€β”€ schema/           # Drizzle schemas
β”‚   β”‚   β”œβ”€β”€ users.schema.ts
β”‚   β”‚   β”œβ”€β”€ posts.schema.ts
β”‚   β”‚   β”œβ”€β”€ comments.schema.ts
β”‚   β”‚   β”œβ”€β”€ groups.schema.ts
β”‚   β”‚   β”œβ”€β”€ profileInfo.schema.ts
β”‚   β”‚   └── schema.ts
β”‚   β”œβ”€β”€ types/            # Type definitions
β”‚   β”œβ”€β”€ drizzle.module.ts
β”‚   β”œβ”€β”€ migrate.ts        # Migration runner
β”‚   └── seed.ts           # Database seeder
β”‚
β”œβ”€β”€ common/               # Shared utilities
β”‚   β”œβ”€β”€ constants/        # Swagger examples, etc.
β”‚   β”œβ”€β”€ decorators/       # Custom decorators
β”‚   β”œβ”€β”€ filters/          # Exception filters
β”‚   β”œβ”€β”€ guards/           # Authorization guards
β”‚   └── types/            # Express type extensions
β”‚
β”œβ”€β”€ health/               # Health check module
β”‚   β”œβ”€β”€ health.controller.ts
β”‚   β”œβ”€β”€ health.module.ts
β”‚   └── drizzle-health.indicator.ts
β”‚
β”œβ”€β”€ app.module.ts         # Root module
└── main.ts               # Application entry point

πŸ”’ Security Features

Password Security

  • Passwords are hashed using bcrypt (10 salt rounds)
  • Password requirements: 8-50 characters
  • Passwords are never returned in API responses (using @Exclude() decorator)

JWT Authentication

  • Tokens expire after 1 hour (configurable)
  • Secure token validation with error handling
  • Tokens required for protected endpoints

Authorization

  • Resource Ownership Guard: Users can only modify their own posts/comments
  • Group Ownership Guard: Only group owners can update/delete groups or remove members
  • Role-based access control in groups (owner vs member)

Rate Limiting

  • 100 requests per minute per IP
  • Prevents brute-force attacks

Input Validation

  • All request bodies validated using class-validator
  • UUID validation for path parameters
  • Field length restrictions
  • Email format validation
  • URL validation for profile links

πŸ§ͺ Database Seeding

The project includes a comprehensive seeder for development:

pnpm run db:seed

This creates:

  • 50 users with hashed passwords
  • 50 profile records with complete metadata
  • 50 posts
  • 50 comments
  • 3 groups (TypeScript, Rust, GO)
  • User-group memberships

πŸ”§ Available Scripts

{
  "start": "nest start",
  "start:dev": "nest start --watch",
  "start:prod": "node dist/main",
  "build": "nest build",
  "db:generate": "drizzle-kit generate",
  "db:migrate": "tsx src/drizzle/migrate.ts",
  "db:seed": "tsx src/drizzle/seed.ts",
  "db:studio": "drizzle-kit studio"
}

🎯 Key Design Patterns

Guards

  • AuthGuard: Validates JWT tokens, attaches user to request
  • ResourceOwnershipGuard: Ensures users can only modify their own resources
  • GroupOwnershipGuard: Restricts group management to owners

Custom Decorators

  • @ResourceType(): Specifies resource type for ownership validation
  • @AtLeastOneField(): Validates that partial updates have at least one field

DTO Transformation

  • plainToInstance(): Ensures response DTOs exclude sensitive fields
  • @Exclude(): Prevents password from being serialized

Error Handling

  • DatabaseExceptionFilter: Catches and formats database errors
  • Detailed error responses with timestamps and request paths
  • Type guards for Drizzle/PostgreSQL error detection

🚨 Error Responses

The API provides consistent error responses:

{
  "statusCode": 400,
  "timestamp": "2026-02-10T12:00:00.000Z",
  "path": "/users/me",
  "message": {
    "message": ["Invalid email format", "..."],
    "error": "Bad Request"
  }
}

Common error codes:

  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (invalid/missing token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 409 - Conflict (duplicate email/group name)
  • 500 - Internal Server Error
  • 503 - Service Unavailable (database connection)

About

A robust NestJS REST API for a social blogging platform. Features JWT authentication, Drizzle ORM, PostgreSQL, rate limiting, and comprehensive CRUD operations. Fully containerized with Docker Compose for deployment.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages