Backend for the APCinema cinema platform. Built as multiple NestJS services: synchronous calls over gRPC, async events over RabbitMQ. Clients only talk to the gateway over HTTP; everything else is internal.
| Service / package | Role |
|---|---|
| gateway-service | HTTP API, Swagger, proxies requests to microservices via gRPC |
| auth-service | Authentication: OTP via phone or email, accounts in PostgreSQL, codes in Redis, OTP events via RabbitMQ |
| user-service | User profiles: settings, avatars, admin operations; sync via RabbitMQ auth.account_created |
| notifiaction-service | Consumes async events from RabbitMQ (e.g. auth.otp_requested) for SMS/email delivery |
| contracts | Protobuf contracts and generated TypeScript types (@apcinema/contracts) |
| common | Shared utilities (@apcinema/shared) — gRPC → HTTP mapping, JWT helpers, etc. |
| core | Shared dev tooling (@apcinema/core) — currently mostly Prettier config |
| docker | Local infrastructure: PostgreSQL, Redis, and RabbitMQ |
Client (web / mobile)
│
▼ HTTP
┌───────────────────┐
│ gateway-service │ :3000 (default)
│ REST + Swagger │
└─────────┬─────────┘
│ gRPC
├──────────────────────┐
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ auth-service │ │ user-service │
│ :50051 │ │ :50052 │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
├───────────┐ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Redis │ │ PostgreSQL │
│ accounts │ │ OTP codes │ │ users │
└─────────────┘ └─────────────┘ └─────────────┘
│
│ RabbitMQ
├──────────────────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ notifiaction-service│ │ user-service │
│ auth.otp_requested │ │ auth.account_created│
└─────────────────────┘ └─────────────────────┘
When a user registers, auth-service creates an account and emits auth.account_created to the users_queue. user-service consumes the event and creates a profile linked by accountId.
When a user requests an OTP, auth-service stores the hashed code in Redis, logs it in dev mode, and emits an auth.otp_requested event to the notifications_queue. notifiaction-service consumes that event and will handle actual delivery.
Implemented domains:
Auth (/auth/*):
POST /auth/otp/send— send an OTP to a phone number or emailPOST /auth/otp/verify— verify the code and receive tokensPOST /auth/register,POST /auth/login,POST /auth/refresh,POST /auth/logoutGET /auth/me— auth data only (phone, email, role, verification flags)
Users (/users/*, /admin/users/*):
GET /users/me,PATCH /users/me,POST /users/me/avatarGET /users/:id,GET /users/by-username/:username— public profilesGET /admin/users,GET /admin/users/:id— admin onlyPATCH /admin/users/:id/block,PATCH /admin/users/:id/unblock,DELETE /admin/users/:id
API docs: http://<host>/docs (Swagger), OpenAPI YAML: /openapi.yaml.
- Node.js 20+ (the project uses
@types/nodev24) - npm
- Docker and Docker Compose — for PostgreSQL, Redis, and RabbitMQ
- Git with submodule support
git clone --recurse-submodules <repository-url>
cd backendIf the repo was already cloned without submodules:
git submodule update --init --recursiveCopy the example env file:
cp docker/.env.example docker/.envStart the containers:
cd docker
docker compose up -dDefault ports:
- PostgreSQL —
localhost:5433(container port5432) - Redis —
localhost:6379 - RabbitMQ —
localhost:5672(AMQP), management UI —localhost:15672
cd auth-service
npm installcp .env.example .envSet RMQ_URL and RMQ_QUEUE_NAME in .env to match your RabbitMQ credentials from docker/.env (defaults: amqp://admin:123456@localhost:5672, queue notifications_queue).
Apply the database schema:
npx prisma db pushRun in dev mode:
npm run start:devThe service listens for gRPC on localhost:50051.
cd gateway-service
npm installcp .env.example .envRun:
npm run start:devcd user-service
npm installcp .env.example .envCreate the users database (if it does not exist):
docker exec -it docker-postgres-1 psql -U apcinema -c "CREATE DATABASE apcinema_users;"Apply the database schema:
npx prisma db pushRun:
npm run start:devThe service listens for gRPC on localhost:50052 and HTTP (Swagger) on localhost:3002.
cd notifiaction-service
npm installCreate .env with the same RabbitMQ settings as auth-service:
RMQ_URL=amqp://admin:123456@localhost:5672
RMQ_QUEUE_NAME=notifications_queueRun:
npm run start:devThe service has no HTTP port — it only listens on the RabbitMQ queue.
# health check
curl http://localhost:3000/health
# send OTP (in dev the code is logged by auth-service; notifiaction-service logs the event)
curl -X POST http://localhost:3000/auth/otp/send \
-H "Content-Type: application/json" \
-d '{"identifier": "+421950353687", "type": "phone"}'Swagger: http://localhost:3000/docs
RabbitMQ management UI: http://localhost:15672 (credentials from docker/.env)
| Variable | Description |
|---|---|
HTTP_HOST |
Public service URL (used in logs) |
HTTP_PORT |
HTTP server port |
HTTP_CORS |
Allowed origins, comma-separated |
AUTH_GRPC_URL |
auth-service address (host:port) |
USER_GRPC_URL |
user-service address (host:port) |
SWAGGER_TITLE |
Swagger page title |
SWAGGER_DESCRIPTION |
API description in Swagger |
SWAGGER_VERSION |
API version in Swagger |
| Variable | Description |
|---|---|
DATABASE_URL |
Connection string for Prisma CLI |
POSTGRES_* |
PostgreSQL connection settings at runtime |
REDIS_* |
Redis connection settings |
RMQ_URL |
RabbitMQ connection URL (default: amqp://admin:123456@localhost:5672) |
RMQ_QUEUE_NAME |
Queue for notification events (default: notifications_queue) |
RMQ_USERS_QUEUE_NAME |
Queue for user profile events (default: users_queue) |
The auth-service gRPC port is hardcoded in auth-service/src/main.ts (localhost:50051). If you change it, update AUTH_GRPC_URL in the gateway as well.
| Variable | Description |
|---|---|
DATABASE_URL |
Connection string for Prisma CLI |
POSTGRES_* |
PostgreSQL connection settings at runtime (POSTGRES_DB=apcinema_users) |
USER_GRPC_URL |
user-service gRPC address (localhost:50052) |
HTTP_PORT |
HTTP/Swagger port (default: 3002) |
RMQ_URL |
RabbitMQ connection URL |
RMQ_USERS_QUEUE_NAME |
Queue for auth.account_created events (default: users_queue) |
AVATAR_STORAGE_PATH |
Local path for avatar files (default: ./uploads/avatars) |
AVATAR_PUBLIC_BASE_PATH |
Public URL prefix for avatars (default: /avatars) |
AVATAR_MAX_SIZE_MB |
Max avatar upload size in MB (default: 5) |
The user-service gRPC URL is read from USER_GRPC_URL. Update gateway USER_GRPC_URL if you change the port.
| Variable | Description |
|---|---|
RMQ_URL |
RabbitMQ connection URL — must match auth-service |
RMQ_QUEUE_NAME |
Queue name — must match auth-service |
| Variable | Description |
|---|---|
POSTGRES_USER / POSTGRES_PASSWORD |
PostgreSQL credentials |
REDIS_PASSWORD |
Redis password |
RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASS |
RabbitMQ credentials (used in RMQ_URL) |
The gateway maps gRPC errors to a unified HTTP format via GrpcExceptionFilter. Auth service error codes:
| Code | When |
|---|---|
OTP_EXPIRED |
Code expired or was never requested |
OTP_INVALID |
Wrong code |
ACCOUNT_NOT_FOUND |
Account not found after verification |
backend/
├── auth-service/ # gRPC authentication microservice
├── user-service/ # gRPC user profile microservice
├── gateway-service/ # HTTP gateway
├── notifiaction-service/ # RabbitMQ consumer for notifications
├── contracts/ # proto + generated types
├── common/ # @apcinema/shared
├── core/ # @apcinema/core
├── docker/ # docker-compose for local development
├── .gitmodules # submodule repository links
└── README.md
gateway-service, auth-service, notifiaction-service, contracts, common, and core are git submodules with their own repositories. Changes in contracts or common may require running npm install in dependent services.
In each service (auth-service, user-service, gateway-service, notifiaction-service):
npm run start:dev # hot-reload
npm run build # build
npm run lint # ESLint
npm run test # unit tests
npm run test:e2e # e2e testsSources live in contracts/proto/. After changing a .proto file:
cd contracts
npm install
npm run generateThe @apcinema/contracts package is published to npm; locally, services pull it from node_modules or the submodule.
@apcinema/shared→file:../common@apcinema/core→file:../core(gateway) or npm version (auth-service)
After changing common or core, reinstall dependencies in the services:
npm install- JWT tokens are placeholders (
access_token/refresh_tokenin the verify response) — real token issuance is in progress. - OTP delivery pipeline is wired via RabbitMQ (
auth.otp_requested), but SMS/email providers are not integrated yet — in dev the code is logged by auth-service and the event is logged by notifiaction-service. - Prisma migrations: currently using
prisma db push; onceprisma/migrationsexists, switch toprisma migrate dev.
Services are private (UNLICENSED). The @apcinema/contracts and @apcinema/core packages are published separately.