Welcome to the Blog API — a modern, production-ready backend for a full-featured blogging platform. This document gives you the complete picture of what this project does, how it works, and how to get it running.
Think of this as a complete backend system for a blogging platform like Medium or Dev.to. Users can create and share blog posts, interact with content through comments and likes, receive personalized feeds, and subscribe to author newsletters. Behind the scenes, there's a robust system handling email delivery, real-time notifications, and AI-powered content summarization.
It's built with TypeScript, Node.js/Express, PostgreSQL, Redis, and Socket.IO — a solid, scalable tech stack for a production application.
- Registration & Authentication: Secure registration with password hashing (bcrypt), JWT-based login with token refresh.
- Email Verification: One-time passwords (OTP) sent via email for account verification.
- Password Reset: Users can reset passwords securely with email-based confirmation.
- Token Management: Blacklist mechanism to invalidate tokens on logout.
- User Profiles: Full profile support with custom profile pictures (uploaded to Cloudinary).
- Create & Edit Posts: Write rich blog posts with title, description, and images.
- Publish/Draft Mode: Control when posts go live.
- Image Management: Upload multiple images per post (hosted on Cloudinary).
- Categorization: Organize posts with categories and tags.
- Author Control: Only the author can edit or delete their posts.
- Global Feed: Browse all published posts.
- User-Specific Feed: See posts from authors you follow.
- Search: Find posts by keyword.
- Categories & Tags: Browse content by topic.
- Comments & Replies: Leave comments on posts with nested reply support.
- Likes: Users can like posts and see engagement metrics.
- Bookmarks: Save posts for later reading.
- Interaction Tracking: Record all engagement for analytics.
- Blog Summarization: Automatically generate summaries of blog posts using AI (OpenAI).
- Smart Processing: Summaries are cached and retrieved on demand.
- Subscribe/Unsubscribe: Users can follow authors' newsletters.
- Bulk Email Delivery: When an author publishes, subscribers receive emails with a link to the new post.
- HTML Email Templates: Professional-looking emails with unsubscribe links.
- Queue-Based Processing: Emails are queued and processed asynchronously to avoid blocking requests.
- Socket.IO Integration: Instant notifications for likes, comments, bookmarks, and newsletter updates.
- Live Sync: Users see updates in real-time without refreshing.
- Event Types: Sync requests, likes, comments, bookmarks, newsletter events.
- Swagger UI: Auto-generated API docs served at
/api-docsfor easy exploration. - Endpoint Discovery: Test endpoints directly from the browser.
┌─────────────────────────────────────────────────────────────────┐
│ Client Applications │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ REST API Calls │ │ WebSocket Events │ │
│ │ (HTTP) │ │ (Socket.IO) │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │ │ │
└───────────┼──────────────────────────────┼───────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Express.js HTTP Server │
│ (src/index.ts) │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ Route Handlers │ │ Middlewares │ │ Socket.IO │ │
│ │ (Controllers) │ │ (Auth, etc) │ │ (Consumers) │ │
│ └──────────────────┘ └──────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ │
├──────────────────────────────────────┤
│ │
▼ ▼
┌──────────────────────────┐ ┌────────────────────────────┐
│ Prisma ORM │ │ BullMQ Queue Workers │
│ ┌────────────────────┐ │ │ │
│ │ PostgreSQL DB │ │ │ ┌──────────────────────┐ │
│ │ (Users, Blogs, │ │ │ │ Email Job Processor │ │
│ │ Comments, Likes, │ │ │ │ Newsletter Handler │ │
│ │ etc) │ │ │ └──────────────────────┘ │
│ └────────────────────┘ │ │ │
└──────────────────────────┘ └────────────────────────────┘
│ │
│ ┌─────────────────┼─────────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────┐
│ Redis │ │ Nodemailer │ │ Cloudinary │ │ OpenAI │
│ (Cache & │ │ (SMTP Email)│ │ (Image │ │ or AI │
│ Queue) │ │ │ │ Storage) │ │ Service │
└──────────────┘ └──────────────┘ └────────────────┘ └──────────┘
- Express Server: Handles HTTP requests and serves the REST API.
- Socket.IO Server: Manages WebSocket connections for real-time updates.
- Prisma Client: Object-relational mapper for database operations.
- Redis: In-memory store for caching and BullMQ job queue backend.
- BullMQ Workers: Background job processing for email and newsletter tasks.
- Nodemailer: SMTP client for sending emails.
- Cloudinary: CDN for storing and serving user-uploaded images.
- AI Service: Integrates with OpenAI or a Token-Mix proxy for blog summarization.
Here's what the data model looks like:
- id (UUID, primary key)
- email (unique)
- password (hashed)
- fullName, profilePicture
- role (user/admin)
- isActive, emailVerified
- timestamps (createdAt, updatedAt)
- Relations: blogs, comments, likes, bookmarks, interactions, tokens, notifications
- id (UUID)
- title, description, images (JSON)
- published (boolean)
- authorId (FK to User)
- categoryId (FK to Category)
- timestamps
- Relations: author, category, tags, comments, likes, bookmarks, interactions, summary
- id (UUID)
- content (text)
- authorId (FK to User)
- blogId (FK to Blog)
- parentId (FK to self - for nested replies)
- timestamps
- Relations: author, blog, parent, replies
- id (UUID)
- userId (FK to User)
- blogId (FK to Blog)
- unique constraint on (userId, blogId) - one like per user per blog
- timestamps
- id (UUID)
- userId (FK to User)
- blogId (FK to Blog)
- unique constraint on (userId, blogId)
- timestamps
- id (UUID)
- name, description
- slug (unique, for URLs)
- timestamps
- id (UUID)
- ownerId (FK to User - the author)
- subscriberId (FK to User - the subscriber)
- unique constraint on (ownerId, subscriberId)
- timestamps
- id (UUID)
- ownerId (FK to User - who receives it)
- senderId (FK to User - who triggered it, nullable)
- event (string: "like", "comment", "bookmark", "newsletter", etc.)
- message (optional)
- timestamps
- Indexed on (ownerId, event, senderId, createdAt) for fast queries
- OutstandingToken: Stores issued JWT tokens for validation.
- BlacklistedToken: Tracks revoked tokens on logout.
- OneTimePassword: Temporary OTP codes for email verification.
- BlogSummary: Cached AI-generated summaries.
- BlogPostInteraction: Tracks user engagement metrics.
The system uses BullMQ (with Redis) to handle background jobs asynchronously. Here's how it works:
JobName: "email"
Data: {
to: "user@example.com",
subject: "Email Subject",
body: "HTML email body"
}
Flow:
- Controller queues the job → Queue stores in Redis → Worker picks it up
- Worker uses Nodemailer to send via SMTP → Job completes or retries
JobName: "newsletter"
Data: {
authorId: "author-uuid",
blogPostId: "blog-uuid",
emails: ["subscriber1@example.com", "subscriber2@example.com"]
}
Flow:
- When a blog is published, controller queues a newsletter job
- Worker fetches author and blog details from DB
- Generates HTML email from template with blog link and unsubscribe URL
- Splits work: creates individual email jobs for each subscriber
- Those jobs are processed by the email worker
- Queue Name: "task-processing"
- Connection: Redis
- Default Retries: 5 attempts per job
- Backoff: Exponential (5 second initial delay)
- Cleanup: Remove completed jobs after 100 entries, failed after 500 entries
Email sending can be slow (1-5 seconds per email). Without a queue, your API would timeout if you tried to send 100 emails synchronously. The queue lets the controller respond instantly while jobs run in the background. Even if the process crashes, Redis keeps the jobs so they're processed on restart.
Real-time notifications happen via Socket.IO. Here's the journey:
-
User Connects
Client → WebSocket Connect → Server registers socket connection → Client sends "userConnection" event with userId → Server validates user exists in DB → Server joins socket to a room named after the userId -
Action Triggers Event
Example: User A likes Blog by User B User A → REST API /blogs/{blogId}/like → Controller records like in DB → Controller calls createNotifications(userB_id, userA_id, "like") → Notification stored in DB -
Event Broadcast
Socket.IO listeners are configured to catch certain triggers → When notification is created, emit event to the recipient's room → Example: io.to(userB_id).emit("receive_message", {...}) -
Client Receives Update
Client listens on socket.on("receive_message") → Receives notification payload in real-time → UI updates instantly without polling
- "sync_request": User connected/synced
- "like": Someone liked a post
- "comment": Someone commented on a post
- "bookmark": Someone bookmarked a post
- "newsletter": New post from followed author
{
"event": "like",
"status": "completed",
"timeStamp": "2026-06-14T10:30:00Z",
"socketId": "socket-123",
"userId": "user-456"
}This approach gives you instant feedback without polling the server every second. It's perfect for modern, responsive applications.
The API is self-documenting using Swagger/OpenAPI.
- URL:
http://localhost:3000/api-docs(or your deployed domain) - Format: Interactive Swagger UI
- Features:
- Browse all endpoints
- See request/response schemas
- Test endpoints directly from the browser
- Copy curl examples
- Docs are auto-generated by
swagger-autogenduring the build step:npm run build-docs - Outputs to
swagger-output.json(included in repo) - Server serves this file via
swagger-ui-expressmiddleware
POST /api/register- Create accountPOST /api/login- Get JWT tokensPOST /api/logout- Revoke tokensPOST /api/rotate-token- Refresh access tokenPOST /api/send-verification-email- Send OTPPOST /api/verify-email- Confirm emailPOST /api/password-reset- Request password resetPOST /api/password-reset-confirm- Complete reset
GET /api/blogs- List all blogsGET /api/blogs?search=keyword- Search blogsPOST /api/blogs- Create blogGET /api/blogs/{id}- Get single blogPUT /api/blogs/{id}- Update blogDELETE /api/blogs/{id}- Delete blogGET /api/blogs/{id}/summarize- Get AI summary
POST /api/blogs/{id}/like- Like a blogDELETE /api/blogs/{id}/unlike- Unlike a blogPOST /api/blogs/{id}/comments- Add commentGET /api/blogs/{id}/comments- List commentsPUT /api/blogs/{id}/comments/{commentId}- Edit commentDELETE /api/blogs/{id}/comments/{commentId}- Delete commentPOST /api/blogs/{id}/comments/{commentId}/replies- Reply to commentPOST /api/blogs/{id}/bookmark- Bookmark blogDELETE /api/blogs/{id}/bookmark/{bookmarkId}- Remove bookmark
GET /api/profile- Get user profilePOST /api/picture- Upload profile pictureGET /api/notifications- Get notifications
POST /api/{userId}/subscribe- Subscribe to authorPOST /api/{userId}/unsubscribe- UnsubscribeGET /api/{userId}/subscriptions- List subscriptions
POST /api/categories- Create category (admin)GET /api/categories- List categoriesPOST /api/tags- Create tag (admin)GET /api/tags- List tags
Getting this to production requires a few key steps. Let's walk through them.
You'll need:
- PostgreSQL 12+ database with a connection string
- Redis server (for queues and caching)
- SMTP credentials (for email sending)
- Cloudinary account (for image hosting)
- OpenAI API key or similar (for blog summarization, optional)
- Node.js 18+ and npm
Create a .env.production file with these variables:
# Server
PORT=3000
NODE_ENV=production
BASE_URL=https://yourdomain.com
# Database
DATABASE_URL=postgresql://user:password@host:port/blogapi_prod
# Redis
REDIS_URL=redis://user:password@host:port
# JWT & Auth
JWT_SECRET_KEY=your-super-secret-key-min-32-chars
JWT_EXPIRY_TIME=7d
JWT_ALGORITHM=HS512
JWT_AUDIENCE=your-app-users
JWT_ISSUER=your-app-name
EXPIRES_AT=7d
# Encryption
SALT=10
# SMTP (Email)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_EMAIL_FROM=noreply@yourdomain.com
# Cloudinary
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_SECRET=your-api-secret
# AI Service (optional)
TOKEN_MIX_API_KEY=your-api-key
TOKEN_MIX_BASE_URL=https://api.tokenmix.com-
Install Dependencies
npm install
-
Generate Prisma Client
npx prisma generate
-
Run Database Migrations
npx prisma migrate deploy
-
Build the Project
npm run build npm run build-docs
-
Start the Server
npm start
The app will start listening on PORT (default 3000) on 0.0.0.0 (accessible from outside).