Skip to content

ahmedk20/Chirpy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🐦 Chirpy

A Modern Micro-Blogging API

Full-featured Twitter-like API built with TypeScript, Express, and PostgreSQL

TypeScript Express PostgreSQL License: ISC PRs Welcome

Features β€’ Quick Start β€’ API Docs β€’ Architecture β€’ Contributing


πŸ“– Overview

Chirpy is a production-ready micro-blogging API that demonstrates modern backend development practices. Built with a clean, layered architecture, it provides all the essential features for a Twitter-like social platform including user authentication, post management, and premium subscriptions.

Why Chirpy?

  • πŸ—οΈ Clean Architecture: Layered design with clear separation of concerns
  • πŸ” Secure by Default: JWT auth, Argon2 password hashing, refresh tokens
  • πŸ“ Type-Safe: Written in TypeScript with strict type checking
  • πŸš€ Production-Ready: Comprehensive error handling, logging, and validation
  • πŸ“š Well-Documented: Extensive inline docs and API reference
  • πŸ§ͺ Tested: Unit tests with Vitest
  • 🎯 Best Practices: Follows SOLID principles and Express.js conventions

✨ Features

Core Functionality

  • πŸ‘€ User Management

    • User registration with email/password
    • Secure authentication with JWT tokens
    • Refresh token rotation
    • Profile updates
  • πŸ“ Chirp Management

    • Create chirps (max 140 characters)
    • Read all chirps or filter by author
    • Sort chronologically (ascending/descending)
    • Delete own chirps
    • Automatic profanity filtering
  • πŸ”’ Security

    • JWT-based authentication
    • Argon2 password hashing
    • Refresh token revocation
    • API key authentication for webhooks
    • Resource ownership authorization
    • SQL injection protection (Drizzle ORM)
  • πŸ’Ž Premium Features

    • ChirpyRed premium subscriptions
    • Webhook integration for payment processing
    • Premium status tracking
  • πŸ“Š Admin Tools

    • Metrics dashboard
    • Hit counter
    • Reset functionality (dev mode only)

πŸš€ Quick Start

Prerequisites

  • Node.js 21.7.0 or higher
  • PostgreSQL 14+
  • npm or yarn

Installation

  1. Clone the repository

    git clone https://github.com/ahmedk20/chirpy.git
    cd chirpy
  2. Install dependencies

    npm install
  3. Set up environment variables

    cp .env.example .env

    Edit .env with your configuration:

    DB_URL=postgres://username:password@localhost:5432/chirpy?sslmode=disable
    JWT_SECRET=your-super-secret-jwt-key-min-32-chars
    PLATFORM=dev
    POLKA_KEY=your-webhook-api-key
  4. Set up the database

    # Create the database
    createdb chirpy
    
    # Generate and run migrations
    npm run db:generate
    npm run db:migrate
  5. Build and start

    npm run build
    npm start

    πŸŽ‰ Server running at http://localhost:3000

Development Mode

# Run with auto-compilation
npm run dev

# Run TypeScript in watch mode
npm run watch

Run Tests

npm test

πŸ“ Project Structure

chirpy/
β”œβ”€β”€ public/                    # Static files
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ config/               # Configuration & constants
β”‚   β”œβ”€β”€ controllers/          # HTTP request handlers
β”‚   β”œβ”€β”€ middleware/           # Express middleware
β”‚   β”œβ”€β”€ services/             # Business logic layer
β”‚   β”œβ”€β”€ db/                   # Database layer (Drizzle ORM)
β”‚   β”œβ”€β”€ routes/               # API route definitions
β”‚   β”œβ”€β”€ types/                # TypeScript type definitions
β”‚   β”œβ”€β”€ utils/                # Utility functions
β”‚   └── tests/                # Test files
β”œβ”€β”€ .env.example              # Environment variables template
β”œβ”€β”€ drizzle.config.ts         # Drizzle ORM configuration
β”œβ”€β”€ tsconfig.json             # TypeScript configuration
└── package.json              # Project dependencies

πŸ“š API Documentation

Authentication Endpoints

Register User

POST /api/users
Content-Type: application/json

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

Response: 201 Created

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "isChirpyRed": false,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z"
}

Login

POST /api/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "securePassword123",
  "expiresInSeconds": 3600
}

Response: 200 OK

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "a1b2c3d4e5f6...",
  "isChirpyRed": false,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z"
}

Refresh Token

POST /api/refresh
Authorization: Bearer <refresh-token>

Response: 200 OK

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Revoke Token

POST /api/revoke
Authorization: Bearer <access-token>

Response: 204 No Content


Chirp Endpoints

Create Chirp

POST /api/chirps
Authorization: Bearer <access-token>
Content-Type: application/json

{
  "body": "Hello, world! This is my first chirp! 🐦"
}

Response: 201 Created

{
  "id": "660e8400-e29b-41d4-a716-446655440000",
  "body": "Hello, world! This is my first chirp! 🐦",
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "createdAt": "2025-01-15T10:35:00.000Z",
  "updatedAt": "2025-01-15T10:35:00.000Z"
}

Get All Chirps

GET /api/chirps?sort=desc&authorId=<user-id>

Query Parameters:

  • sort (optional): asc or desc (default: asc)
  • authorId (optional): Filter by user ID

Response: 200 OK

[
  {
    "id": "660e8400-e29b-41d4-a716-446655440000",
    "body": "Hello, world! This is my first chirp! 🐦",
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "createdAt": "2025-01-15T10:35:00.000Z",
    "updatedAt": "2025-01-15T10:35:00.000Z"
  }
]

Get Single Chirp

GET /api/chirps/:chirpID

Response: 200 OK

Delete Chirp

DELETE /api/chirps/:chirpID
Authorization: Bearer <access-token>

Response: 204 No Content

⚠️ Note: Users can only delete their own chirps

Validate Chirp

POST /api/validate_chirp
Content-Type: application/json

{
  "body": "This chirp contains kerfuffle!"
}

Response: 200 OK

{
  "cleanedBody": "This chirp contains ****!"
}

User Endpoints

Update User

PUT /api/users
Authorization: Bearer <access-token>
Content-Type: application/json

{
  "email": "newemail@example.com",
  "password": "newSecurePassword123"
}

Response: 200 OK


Admin Endpoints

Get Metrics

GET /api/metrics

Response: 200 OK (plain text)

Hits: 42

Admin Dashboard

GET /admin/metrics

Response: 200 OK (HTML page)

Reset Metrics

POST /admin/reset

Response: 200 OK (plain text)

⚠️ Note: Only available when PLATFORM=dev


Webhook Endpoints

Polka Webhook (Premium Upgrades)

POST /api/polka/webhooks
Authorization: ApiKey <polka-api-key>
Content-Type: application/json

{
  "event": "user.upgraded",
  "data": {
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Response: 204 No Content


Health Check

GET /api/healthz

Response: 200 OK (plain text)

OK

πŸ›οΈ Architecture

Chirpy follows a layered architecture pattern for maximum maintainability and testability.

Architecture Layers

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            HTTP Request                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Routes (API Endpoints)           β”‚
β”‚  - Define endpoints                      β”‚
β”‚  - Apply middleware                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       Middleware (Cross-cutting)         β”‚
β”‚  - Authentication (JWT)                  β”‚
β”‚  - Authorization (API Key)               β”‚
β”‚  - Logging                               β”‚
β”‚  - Error handling                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      Controllers (HTTP Handlers)         β”‚
β”‚  - Extract request data                  β”‚
β”‚  - Call services                         β”‚
β”‚  - Format responses                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       Services (Business Logic)          β”‚
β”‚  - Validation                            β”‚
β”‚  - Business rules                        β”‚
β”‚  - Coordinate operations                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      Database (Data Access)              β”‚
β”‚  - Queries (Drizzle ORM)                 β”‚
β”‚  - Schema definitions                    β”‚
β”‚  - Migrations                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Patterns

  • Dependency Injection: Services injected into controllers
  • Repository Pattern: Database queries abstracted in db/queries.ts
  • Service Layer Pattern: Business logic separated from HTTP logic
  • Factory Pattern: Singleton services (metrics, chirp validation)
  • Middleware Pattern: Cross-cutting concerns (auth, logging, errors)

πŸ”’ Security

Authentication Flow

  1. User registers or logs in with email/password
  2. Password hashed with Argon2 (memory-hard algorithm)
  3. Server generates:
    • Access Token (JWT, 1 hour expiry)
    • Refresh Token (random hex, 60 days expiry, stored in DB)
  4. Client sends access token in Authorization: Bearer <token> header
  5. Middleware validates token and extracts userId
  6. When access token expires, client uses refresh token to get new access token
  7. Refresh tokens can be revoked

Security Features

βœ… Password Security

  • Argon2 hashing (winner of Password Hashing Competition)
  • Minimum 8 character requirement
  • Passwords never stored in plain text

βœ… Token Security

  • Short-lived access tokens (1 hour)
  • Refresh token rotation
  • Token revocation support
  • Secure random token generation

βœ… API Security

  • JWT validation on protected routes
  • API key authentication for webhooks
  • Resource ownership verification
  • SQL injection protection (parameterized queries)
  • Input validation and sanitization

βœ… Data Security

  • Environment variable configuration
  • Secrets not committed to git
  • Centralized error handling (no data leaks)

πŸ—„οΈ Database Schema

Entity Relationship Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       Users         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (PK)             β”‚
β”‚ email (unique)      β”‚
β”‚ hashedPassword      β”‚
β”‚ isChirpyRed         β”‚
β”‚ createdAt           β”‚
β”‚ updatedAt           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β”‚ 1:N
           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      Chirps         β”‚         β”‚   Refresh Tokens    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (PK)             β”‚         β”‚ token (PK)          β”‚
β”‚ body                β”‚         β”‚ userId (FK)         │◄────┐
β”‚ userId (FK)         β”‚         β”‚ expiresAt           β”‚     β”‚
β”‚ createdAt           β”‚         β”‚ revokedAt           β”‚     β”‚
β”‚ updatedAt           β”‚         β”‚ createdAt           β”‚     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚ updatedAt           β”‚     β”‚
                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
                                           N:1 β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Tables

Users

  • Stores user accounts
  • Email must be unique
  • Password hashed with Argon2
  • Premium status tracked via isChirpyRed

Chirps

  • User-generated posts (max 140 chars)
  • Foreign key to user (cascade delete)
  • Timestamps for sorting

Refresh Tokens

  • Secure refresh tokens for auth
  • Can be revoked
  • Automatically deleted when user is deleted

πŸ› οΈ Configuration

Environment Variables

Variable Required Default Description
DB_URL βœ… Yes - PostgreSQL connection string
JWT_SECRET βœ… Yes - Secret key for JWT signing (min 32 chars)
PLATFORM No prod dev or prod (affects admin endpoints)
POLKA_KEY βœ… Yes - API key for webhook authentication

Constants

Edit src/config/constants.ts to modify:

PORT = 3000                      // Server port
MAX_CHIRP_LENGTH = 140          // Character limit
PROFANE_WORDS = [...]           // Words to censor
REFRESH_TOKEN_EXPIRY_DAYS = 60  // Token validity
DEFAULT_JWT_EXPIRY_SECONDS = 3600 // Access token TTL

πŸ§ͺ Testing

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

Test Structure

src/tests/
β”œβ”€β”€ auth.test.ts       # JWT creation and validation
β”œβ”€β”€ chirp.test.ts      # Chirp validation and filtering
└── user.test.ts       # User creation and updates

πŸ“¦ Scripts

Command Description
npm run build Compile TypeScript to JavaScript
npm start Start production server
npm run dev Run in development mode
npm run watch Watch mode (auto-compile)
npm test Run tests
npm run db:generate Generate database migrations
npm run db:migrate Run migrations
npm run db:studio Open Drizzle Studio (DB GUI)

🚧 Troubleshooting

Common Issues

Build fails with TypeScript errors

# Clean and rebuild
rm -rf dist/
npm run build

Database connection fails

  • Verify PostgreSQL is running: pg_isready
  • Check DB_URL format in .env
  • Ensure database exists: createdb chirpy

JWT errors in production

  • Ensure JWT_SECRET is set and at least 32 characters
  • Verify tokens haven't expired
  • Check system clock synchronization

Migration errors

# Reset migrations
npm run db:generate
npm run db:migrate

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch
    git checkout -b feature/amazing-feature
  3. Make your changes
    • Follow existing code style
    • Add tests for new features
    • Update documentation
  4. Commit your changes
    git commit -m 'Add amazing feature'
  5. Push to your fork
    git push origin feature/amazing-feature
  6. Open a Pull Request

Code Style

  • Use TypeScript strict mode
  • Follow existing naming conventions
  • Add JSDoc comments for public APIs
  • Keep functions small and focused
  • Write tests for new features

Commit Guidelines

Follow Conventional Commits:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • refactor: Code refactoring
  • test: Test updates
  • chore: Build process or tooling changes

πŸ“„ License

This project is licensed under the ISC License - see the LICENSE file for details.


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

Yomna Khaled


πŸ™ Acknowledgments


πŸ“Š Project Stats

  • Language: TypeScript
  • Framework: Express.js 5.x
  • Database: PostgreSQL + Drizzle ORM
  • Auth: JWT + Refresh Tokens
  • Testing: Vitest
  • Code Style: ESLint + Prettier
  • License: ISC

πŸ—ΊοΈ Roadmap

  • Add email verification
  • Implement rate limiting
  • Add image upload support
  • Create REST API documentation (OpenAPI/Swagger)
  • Add real-time notifications (WebSockets)
  • Implement follower/following system
  • Add direct messaging
  • Create admin panel UI
  • Add comprehensive integration tests
  • Set up CI/CD pipeline
  • Docker containerization
  • Add API versioning

⭐ Star this repo if you find it helpful!

Made with ❀️ and TypeScript

Report Bug Β· Request Feature

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors