Live Application: https://readitblogs.vercel.app
A full-stack blog platform with real-time notifications built with Vue 3, NestJS, GraphQL, PostgreSQL, and Redis. Sign up, log in, create blog posts, real-time notifications when new blogs are published.
- Live Demo: Visit readitblogs.vercel.app to explore the application
- Real-time Notifications: Get instant notifications via GraphQL subscriptions when new blogs are posted
- Secure Authentication: JWT-based authentication with bcrypt password hashing
- Modern Stack: Built with Vue 3, NestJS, GraphQL, PostgreSQL, and Redis
- Production Ready: Deployed on Vercel (frontend) and Railway (backend)
- Blog Creation: User creates blog β Backend receives mutation
- Queue Processing: Blog event enqueued to Redis queue
- Worker Processing: Background worker processes queue β Creates notification marker
- Redis Broadcast: Marker published to Redis PubSub channel
- GraphQL Subscription: WebSocket delivers notification to subscribed clients
- Real-time Update: Frontend receives notification instantly
- Vue 3 - Progressive JavaScript framework
- Pinia - State management
- Apollo Client - GraphQL client with subscriptions support
- TypeScript - Type-safe development
- Vite - Fast build tool and dev server
- Vue Router - Client-side routing
- NestJS - Progressive Node.js framework
- Apollo Server - GraphQL server implementation
- TypeORM - Object-Relational Mapping
- Passport.js - Authentication middleware
- JWT - JSON Web Tokens for authentication
- ioredis - Redis client for PubSub and queues
- bcrypt - Password hashing
- PostgreSQL (Render) - Primary database
- Redis (Upstash) - PubSub and queue management
- Vercel - Frontend hosting
- Railway - Backend hosting
- β User registration with email, username, and password
- β Secure login with JWT tokens
- β Password hashing with bcrypt
- β Protected routes and GraphQL operations
- β Token-based authentication for HTTP and WebSocket
- β Blog CRUD
- β Real-time blog list updates
- β Blog author information and timestamps
- β Queue-based processing for reliability
- β Redis PubSub for cross-instance support
- β GraphQL subscriptions for WebSocket delivery
- β Persistent notification storage
- β Unread notification count
- β Auto-recovery on WebSocket reconnection
- Node.js (v18 or higher)
- PostgreSQL (v12 or higher) - or use cloud provider
- Redis (v6 or higher) - or use cloud provider
- npm or yarn package manager
git clone <repository-url>
cd rpg-assignmentcd backend
npm installCreate .env file in backend/ directory:
# Database Configuration
DATABASE_URL=postgresql://user:password@localhost:5432/rpg_blog
# OR use individual variables:
# DB_HOST=localhost
# DB_PORT=5432
# DB_USERNAME=postgres
# DB_PASSWORD=postgres
# DB_NAME=rpg_blog
# Redis Configuration
REDIS_URL=redis://localhost:6379
# OR use individual variables:
# REDIS_HOST=localhost
# REDIS_PORT=6379
# REDIS_PASSWORD=
# JWT Configuration
JWT_SECRET=your-secret-key-change-in-production
# Server Configuration
PORT=3200
NODE_ENV=development
FRONTEND_URL=http://localhost:5173Start the backend:
npm run start:devBackend will run on http://localhost:3200
GraphQL Playground: http://localhost:3200/graphql
cd frontend
npm installCreate .env file in frontend/ directory (optional for local dev):
VITE_GRAPHQL_URL=http://localhost:3200/graphql
VITE_GRAPHQL_WS_URL=ws://localhost:3200/graphqlStart the frontend:
npm run devFrontend will run on http://localhost:5173
- Production:
https://rpg-blog-backend.up.railway.app/graphql - WebSocket:
wss://rpg-blog-backend.up.railway.app/graphql - Local Development:
http://localhost:3200/graphql
mutation Register($input: RegisterInput!) {
register(input: $input) {
token
user {
id
email
username
createdAt
}
}
}Variables:
{
"input": {
"email": "dev@gmail.com",
"username": "dev",
"password": "dev123"
}
}mutation Login($input: LoginInput!) {
login(input: $input) {
token
user {
id
email
username
}
}
}Variables:
{
"input": {
"email": "dev@gmail.com",
"password": "dev123"
}
}query Blogs {
blogs {
id
title
content
author {
id
username
email
}
createdAt
updatedAt
}
}query Blog($id: ID!) {
blog(id: $id) {
id
title
content
author {
id
username
email
}
createdAt
updatedAt
}
}mutation CreateBlog($input: CreateBlogInput!) {
createBlog(input: $input) {
id
title
content
author {
id
username
}
createdAt
}
}Variables:
{
"input": {
"title": "My First Blog Post",
"content": "This is the content of my blog post..."
}
}query AllMarkers {
allMarkers {
markerVersion
blog {
id
title
content
author {
id
username
}
createdAt
}
createdAt
}
}query UnreadNotificationCount {
unreadNotificationCount
}subscription NewNotificationMarker {
newNotificationMarker {
markerVersion
blog {
id
title
content
author {
id
username
email
}
createdAt
}
createdAt
}
}For protected operations, include the JWT token in the request:
HTTP Requests:
Authorization: Bearer <your-jwt-token>
WebSocket Subscriptions: The token is automatically included in connection parameters from the frontend.
| Variable | Description | Default | Required |
|---|---|---|---|
DATABASE_URL |
PostgreSQL connection URL | - | Yes (or use individual DB_* vars) |
DATABASE_SSL |
Enable SSL for database | false |
No |
REDIS_URL |
Redis connection URL | - | Yes (or use individual REDIS_* vars) |
REDIS_TLS |
Enable TLS for Redis | false |
No |
JWT_SECRET |
Secret key for JWT tokens | - | Yes |
PORT |
Server port | 3200 |
No |
NODE_ENV |
Environment mode | development |
No |
FRONTEND_URL |
Frontend URL for CORS | http://localhost:5173 |
No |
| Variable | Description | Default |
|---|---|---|
VITE_GRAPHQL_URL |
GraphQL HTTP endpoint | http://localhost:3200/graphql |
VITE_GRAPHQL_WS_URL |
GraphQL WebSocket endpoint | ws://localhost:3200/graphql |
cd backend
# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:covcd frontend
# Run tests (if configured)
npm run test- Ensure
FRONTEND_URLin backend matches your frontend domain exactly - Check that
NODE_ENV=productionis set in production - Verify CORS logs in backend console
- Verify
DATABASE_URLis correctly formatted - For cloud providers (Render), ensure
DATABASE_SSL=true - Check database credentials and network access
- Verify
REDIS_URLis correctly formatted - For Upstash, ensure
REDIS_TLS=true - Check Redis credentials and network access
- Verify
VITE_GRAPHQL_WS_URLuseswss://for HTTPS - Check that JWT token is included in WebSocket connection params
- Ensure backend CORS allows WebSocket connections
Live Application: https://readitblogs.vercel.app
Try it out: Sign up, log in, create a blog post, and watch real-time notifications appear when new blogs are published! π
