A robust, scalable RESTful API built with a microservices architecture for a social networking platform. This project demonstrates advanced skills in distributed systems, Serverless deployment, and CI/CD pipelines.
This system implements the core backend logic for a social network. Instead of a traditional monolith, the application is divided into 3 containerized services that communicate via HTTP. It features centralized JWT authentication and a fully automated deployment pipeline to Docker Hub.
- Distributed Architecture: Services for Users, Posts, Follows, and Likes.
- Multi-Process Container: Multiple Node.js processes running in a single container with shell script.
- Automated CI/CD: GitHub Actions workflow automatically builds Docker images and pushes to Docker Hub on every push to the
mainbranch. - Cloud Database: Powered by Render's managed PostgreSQL (free tier).
- Centralized Auth: Secure endpoints protected by JWT validation.
- API Gateway Pattern: API Service acts as the single entry point for all client requests.
The application uses an API Gateway pattern where the API Service is the single entry point, with a shell script managing multiple processes within a container.
graph TD
Client([Client / App / Postman])
subgraph "Render Cloud"
API[API Gateway\n:3000]
CONTENT[Content Service\n:3001,3003,3004]
DB_SVC[DB Service\n:3002]
end
subgraph "External"
PG[(Render PostgreSQL)]
end
Client -- HTTP / Bearer JWT --> API
API -- HTTP --> CONTENT
CONTENT -- HTTP --> DB_SVC
DB_SVC -- SSL Connection --> PG
The API Service (port 3000) functions as the API Gateway:
- Receives all client requests
- Handles JWT authentication
- Routes requests to internal services
| Service | Container | Ports | Description |
|---|---|---|---|
| api-service | Docker | 3000 | Auth + Users + Routing |
| content-service | Docker (shell script) | 3001, 3003, 3004 | Posts + Follows + Likes |
| db-service | Docker | 3002 | Database access layer |
| Category | Technologies Used |
|---|---|
| Backend Core | Node.js, Express.js, TypeScript |
| Architecture | Microservices, API Gateway Pattern |
| Database | PostgreSQL (Render Managed - Free Tier) |
| Security | JWT (JSON Web Tokens), bcrypt |
| API Docs | OpenAPI 3.0 rendered with Scalar (@scalar/express-api-reference) |
| DevOps & CI/CD | Docker, GitHub Actions |
| Cloud Provider | Render (Web Services + Managed PostgreSQL) |
| Container Registry | Docker Hub |
Before running the project, ensure you have:
- Node.js 22+ installed
- Docker Desktop installed and running
- pnpm package manager
- Docker Hub account (for container registry)
- Render account (for web services hosting and managed PostgreSQL)
git clone https://github.com/YorberR/Microservices.git
cd Microservicespnpm installCreate a .env file in the root directory:
API_PORT=3000
JWT_SECRET=your-secret-key
DB_HOST=localhost
DB_PORT=5432
DB_NAME=microservices
DB_USER=postgres
DB_PASSWORD=postgres
POST_SERVICE_PORT=3001
FOLLOW_SERVICE_PORT=3003
LIKE_SERVICE_PORT=3004
PG_SERVICE_PORT=3002- Create a database named
microservices - Run the SQL commands in the Database Schema section below
- Create a PostgreSQL instance in the Render Dashboard
- Copy the connection details (Host, Port, Database, User, Password) from the Render database dashboard
- Run the SQL commands in the Database Schema section below
- Update
.envwith your Render PostgreSQL connection details - Add
DB_SSL=trueif your connection requires SSL
docker-compose up --build# Terminal 1 - PostgreSQL Service
pnpm run dev:postgres
# Terminal 2 - Main API
pnpm run dev
# Terminal 3 - Post Service
pnpm run dev:post
# Terminal 4 - Follow Service
pnpm run dev:follow
# Terminal 5 - Like Service
pnpm run dev:likeBase URL: http://localhost:3000/api
All endpoints are RESTful and require JWT authentication unless otherwise noted.
| Endpoint | Method | Description |
|---|---|---|
/api/auth/login |
POST | Login with username/password, returns JWT token |
/api/auth/verify |
POST | Verify JWT token and permissions |
curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user1","password":"pass123"}'{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400
}| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/user |
GET | List all users | Optional |
/api/user |
POST | Create new user | Optional |
/api/user/:id |
GET | Get user by ID | Optional |
/api/user/:id |
PUT | Update user (own record only) | Optional |
/api/user/:id |
DELETE | Delete user (own record only) | Optional |
/api/user/query |
POST | Query users | Required |
/api/user/aggregate |
POST | Aggregate user data | Required |
curl -X POST http://localhost:3000/api/user \
-H "Content-Type: application/json" \
-d '{"name":"new_user","password":"secure_password123","email":"newuser@example.com"}'curl -X GET http://localhost:3000/api/user \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/posts |
GET | List all posts | Required |
/api/posts |
POST | Create new post | Required |
/api/posts/:uuid |
GET | Get post by UUID | Required |
/api/posts/:uuid |
PUT | Update post (owner only) | Required |
/api/posts/:uuid |
DELETE | Delete post (owner only) | Required |
/api/posts/user/:userId |
GET | Get posts by user | Required |
curl -X POST http://localhost:3000/api/posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{"user_id":1,"content":"Hello world! This is my first post."}'curl -X GET http://localhost:3000/api/posts \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/follows |
POST | Follow a user | Required |
/api/follows/:followerId/:followingId |
DELETE | Unfollow a user | Required |
/api/follows/followers/:userId |
GET | Get followers | Required |
/api/follows/following/:userId |
GET | Get following | Required |
/api/follows/check/:followerId/:followingId |
GET | Check follow status | Required |
curl -X POST http://localhost:3000/api/follows \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{"follower_id":1,"following_id":2}'curl -X DELETE http://localhost:3000/api/follows/1/2 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/likes |
POST | Like a post | Required |
/api/likes/:userId/:postId |
DELETE | Unlike a post | Required |
/api/likes/post/:postId |
GET | Get likes on a post | Required |
/api/likes/user/:userId |
GET | Get user likes | Required |
/api/likes/check/:userId/:postId |
GET | Check like status | Required |
/api/likes/count/:postId |
GET | Get like count | Required |
curl -X POST http://localhost:3000/api/likes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{"user_id":1,"post_id":1}'curl -X GET http://localhost:3000/api/likes/count/1 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."- Username:
user1 - Password:
pass123
-
Login: Send username and password to
/api/auth/loginPOST /api/auth/login { "username": "user1", "password": "pass123" }
Receive JWT token in response
-
Get Token: The JWT is returned in the response body
{ "error": false, "status": 200, "body": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } -
Use Token: Include JWT in the
Authorizationheader for all protected endpointsAuthorization: Bearer <your-jwt-token> -
Verify Token: Optional - Validate token via
/api/auth/verifyPOST /api/auth/verify { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "action": "create", "resource": "post", "ownerId": 1 }
- Scalar UI: Visit
http://localhost:3000/api-docsto explore the API interactively - All endpoints are documented with request/response examples, authentication requirements, and error codes
# Build all services
docker-compose builddocker-compose up -ddocker-compose down- Create a Render account (for web services)
- Create a Render PostgreSQL instance (for PostgreSQL)
- Create a Docker Hub account
- Create an Access Token in Docker Hub (if logging in via GitHub)
In your GitHub repository, add these secrets:
DOCKER_USERNAME: Your Docker Hub usernameDOCKER_PASSWORD: Your Docker Hub password or access token
Simply push to the main branch:
git add .
git commit -m "Deploy to production"
git push origin mainGitHub Actions will automatically build and push Docker images to Docker Hub.
After images are pushed to Docker Hub:
-
Create Web Services in Render:
- db-service:
docker.io/your-username/db-service:latest, port 3002 - api-service:
docker.io/your-username/api-service:latest, port 3000 - content-service:
docker.io/your-username/content-service:latest, port 3001
- db-service:
-
Create Render PostgreSQL:
- Go to the Render Dashboard
- Create a new PostgreSQL instance
- Note the connection details (host, port, user, password, database)
-
Configure Environment Variables for each service:
DB_HOST: your-render-postgres-host DB_PORT: 5432 DB_NAME: your_database_name DB_USER: your_username DB_PASSWORD: your_password DB_SSL: true
The deployment is designed to be free:
- Render: 750 hours/month across all web services (free tier)
- Render PostgreSQL: Managed PostgreSQL free tier available
- Docker Hub: Free container registry
- Services sleep after 15 minutes of inactivity
Estimated Cost: $0/month for typical portfolio traffic
microservices/
βββ .github/
β βββ workflows/
β βββ deploy.yml # CI/CD for Docker Hub
βββ src/
β βββ api/ # Main API service
β β βββ components/
β β β βββ auth/ # Authentication
β β β βββ user/ # User management
β β βββ index.ts
β β βββ Dockerfile # β api-service
β β
β βββ content-service/ # Multi-service container (Post + Follow + Like)
β β βββ Dockerfile # β content-service
β β βββ start.sh # Shell script to start all services
β β βββ (references post/follow/like services)
β β
β βββ post-service/ # Posts microservice
β β βββ components/post/
β β βββ index.ts
β β βββ scalar.json
β β
β βββ follow-service/ # Follows microservice
β β βββ components/follow/
β β βββ index.ts
β β βββ scalar.json
β β
β βββ like-service/ # Likes microservice
β β βββ components/like/
β β βββ index.ts
β β βββ scalar.json
β β
β βββ postgres-service/ # Database access layer
β β βββ index.ts
β β βββ network.ts
β β βββ scalar.json
β β βββ Dockerfile # β db-service
β β
β βββ store/ # Database clients
β β βββ postgres.ts # PostgreSQL client
β β βββ remote.ts # HTTP client
β β
β βββ auth/ # JWT utilities
β βββ network/ # Response helpers
β βββ utils/ # Error handling
β βββ config.ts # Configuration
β
βββ .dockerignore
βββ .env # Environment variables
βββ docker-compose.yml # Docker Compose (local)
βββ package.json
βββ tsconfig.json
Create these tables in your PostgreSQL database:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
uuid VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
uuid VARCHAR(50) UNIQUE NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE follows (
id SERIAL PRIMARY KEY,
follower_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
following_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(follower_id, following_id)
);
CREATE TABLE likes (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, post_id)
);| Command | Description |
|---|---|
pnpm run dev |
Start API service |
pnpm run dev:postgres |
Start PostgreSQL service |
pnpm run dev:post |
Start post service |
pnpm run dev:follow |
Start follow service |
pnpm run dev:like |
Start like service |
pnpm run build |
Compile TypeScript |
| Service | URL |
|---|---|
| API | http://localhost:3000 |
| API Docs (Scalar) | http://localhost:3000/api-docs |
| Post Service | http://localhost:3001 |
| Follow Service | http://localhost:3003 |
| Like Service | http://localhost:3004 |
| Postgres Service | http://localhost:3002 |
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project was originally inspired by the architectural concepts taught in a microservices course at [Platzi]. However, to challenge myself and align the system with modern enterprise standards, I completely re-architected the original baseline.
Key improvements and deviations from the original course include:
- Language Upgrade: Migrated the entire codebase from plain JavaScript to strict TypeScript to ensure type safety, better developer experience, and maintainability.
- Database Modernization: Replaced the traditional local MySQL setup with Render PostgreSQL for a managed cloud database.
- Multi-Process Container: Implemented a shell script approach to run multiple Node.js processes within a single container, optimizing for free tier deployment.
- Container Registry: Integrated Docker Hub for hosting container images with automated CI/CD via GitHub Actions.
- Cloud Deployment: Migrated from local deployments to Render for production-ready web services.
- API Gateway Pattern: Implemented the API Service as the single entry point for all client requests, handling authentication and routing.
This project is licensed under the ISC License - see the LICENSE file for details.
Yorber Rojas
- GitHub: @YorberR
- Built with Express.js and TypeScript
- Deployed on Render
- Database hosted on Render PostgreSQL
- Containerized with Docker
- Images hosted on Docker Hub