Skip to content

Repository files navigation

Microservices Assignment 🚀

A small microservices-based system built for the backend internship assignment: two backend services (User Service, Notification Service) that communicate only through NATS JetStream (no REST or WebSockets between them), fronted by an API Gateway.

See ARCHITECTURE.md for the full design rationale and a diagram, and API_DOCS.md for the API reference.

Project structure

microservices-assignment/
├── api-gateway/           # Public entry point — REST, JWT auth, forwarding
├── user-service/          # Registration/login, publishes events to NATS
├── notification-service/  # Subscribes to NATS events, sends notifications
├── docker-compose.yml     # Runs the entire stack (NATS + 3 services)
├── ARCHITECTURE.md
├── API_DOCS.md
└── README.md

Prerequisites

  • Node.js 18+ (only needed if running services without Docker)
  • Docker & Docker Compose (recommended — easiest way to run everything)

Option A: Run with Docker Compose (recommended)

  1. In each service folder, copy the example env file:
    cp user-service/.env.example user-service/.env
    cp notification-service/.env.example notification-service/.env
    cp api-gateway/.env.example api-gateway/.env
  2. Open each .env file and make sure JWT_SECRET and INTERNAL_API_KEY are identical across all three services (they must match — this is the shared secret gateway↔service auth described in ARCHITECTURE.md). Set a real random value instead of the placeholder, e.g.:
    node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
  3. From the project root:
    docker compose up --build
  4. The API Gateway is now available at http://localhost:4000.

Option B: Run locally without Docker

You'll need a local NATS server with JetStream enabled:

# macOS
brew install nats-server
nats-server -js

# or via Docker, just for NATS itself
docker run -p 4222:4222 nats:2.10-alpine -js

Then, in three separate terminals:

cd user-service && cp .env.example .env && npm install && npm start
cd notification-service && cp .env.example .env && npm install && npm start
cd api-gateway && cp .env.example .env && npm install && npm start

(Again — make sure JWT_SECRET and INTERNAL_API_KEY match across all three .env files.)

Trying it out

# Register a user
curl -X POST http://localhost:4000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","email":"[email protected]","password":"password123"}'

# Log in
curl -X POST http://localhost:4000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"password123"}'
# -> copy the "token" from the response

# Get your profile
curl http://localhost:4000/api/users/me \
  -H "Authorization: Bearer <token>"

# See notifications generated asynchronously via NATS
# (welcome email on register, login alert on login)
curl http://localhost:4000/api/notifications/me \
  -H "Authorization: Bearer <token>"

If everything is wired correctly, the notifications endpoint will show a WELCOME_EMAIL and a LOGIN_ALERT entry — proof that the Notification Service received events published by the User Service purely through NATS, with no direct HTTP/WebSocket call between them.

What satisfies each requirement

Requirement How it's met
Two backend services + API Gateway user-service/, notification-service/, api-gateway/
No REST/WebSockets between the two services They only communicate via NATS subjects (user.registered, user.login); neither knows the other's address
Message broker (NATS preferred) NATS with JetStream for persistence
Secure communication Internal shared-secret header (gateway↔service), JWT (client↔gateway), bcrypt password hashing, NATS user/pass auth support
Reliable, async delivery JetStream durable consumer, at-least-once delivery, retry + redelivery on failure
Clean, scalable architecture Stateless gateway, decoupled services, horizontal scalability (see ARCHITECTURE.md)
Error handling & validation Input validators, try/catch everywhere, meaningful HTTP status codes
Secrets in environment variables All secrets via .env / .env.example, nothing hardcoded

Notes

  • The "database" in this assignment is an in-memory Map for simplicity and to keep the focus on the messaging architecture the assignment asks about. In a production system this would be swapped for Postgres/Mongo with minimal changes (only user-service/server.js's storage calls would need to change).
  • "Sending" a notification currently just logs it and stores it in memory; swapping in a real provider (SES, SendGrid, Twilio, etc.) would only require changing the body of the two handler functions in notification-service/server.js.

About

This is basically a assignment for internship showcasing my microservices skill along with i have used NATS and Docker and build system which includes UserService and Notification Service also included API Gateway.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages