Skip to content

SenhorRG/eShopOnContainers-NestJS

eShop NestJS monorepo

About this repository

This is a personal study project modeled on concepts from Microsoft's sample e‑commerce stacks:

  • dotnet/eShop — reference domain and bounded contexts
  • .NET Aspire — ideas around local orchestration, observability hooks, and service-oriented composition

The implementation here is NestJS, React, Prisma, and Docker Compose, not .NET.

Independence. This codebase is not maintained by Microsoft or the dotnet/eShop authors. There is no guarantee that features, endpoints, databases, or UI match the upstream sample, nor that updates keep pace with Aspire or dotnet/eShop. Treat it as an educational fork in spirit only.

Storefront visuals only. To keep comparisons easy during study, the storefront layout echoes the reference app in a coarse way where practical, and reuses similar stock images/icons where files are bundled for that purpose. That is stylistic resemblance for learning—not an official port, remix, or endorsed UI.

Learning modes (study only)

This repository is for learning and local experimentation, not for advertising a hardened production deployment.

  1. Hybrid development (fast loop)pnpm infra:up starts Postgres (pgvector), Redis, RabbitMQ, and the observability bundle (see deploy/compose/observability/README.md). Use pnpm setup:local for infra + DB migrations/seeds. Nest APIs, workers, and the mobile BFF run on the host with pnpm dev; Vite shells with pnpm dev:ui.
  2. Packaged stack (stack Compose profile) — Docker builds images for the same Nest services as pnpm dev plus nginx for the storefront. See deploy/compose/README-stack.md.

.NET Aspire is not included. Docker Compose plus pnpm scripts provide repeatable topology on a laptop only — not feature parity with Aspire dashboards, resource graphs, or cloud deployment pipelines.

Getting started

Prerequisites

  • Node.js 20.19+ or 22.12+ (see package.json engines; required by Vite 8 for web apps)
  • pnpm 9.x (see packageManager in package.json)
  • Docker Desktop with Compose v2 (WSL2 filesystem recommended on Windows for bind mounts)

Quick start

From the repository root:

cp .env.example .env
pnpm install
pnpm build
pnpm setup:local    # infra + migrations + seeds (or: pnpm infra:up && pnpm db:setup)
pnpm dev            # Nest APIs, workers, mobile-bff

In a second terminal for the web UIs (storefront, webhook tester, operations hub):

pnpm dev:ui

Compose-only path (no host-run Nest): pnpm stack:up / pnpm stack:down — see [deploy/compose/README-stack.md](deploy/compose/README-stack.md).

Open Grafana (http://localhost:3200), Jaeger (http://localhost:16686), and Prometheus (http://localhost:9099) from the operations hub or directly. Stop infra with pnpm infra:down. Port list: deploy/compose/observability/README.md.

Configuration

  • Root .env — copy from .env.example. Used by dotenv-cli for db:* and dev scripts. Never commit real secrets.
  • Composedeploy/compose/.env.compose.example for pnpm infra:up / pnpm infra:down (merges observability/docker-compose.observability.yml). For the stack profile: deploy/compose/.env.stack.example and deploy/compose/README-stack.md. See deploy/compose/README.md for other profiles.
  • Defaults in templates are for local dev only (for example Postgres postgres/postgres, Redis and Rabbit on non-default host ports). Replace before any shared or public deployment.

Study defaults (JWT and auth shortcuts)

Committed examples use weak secrets and optional auth bypass flags so exercises work without a formal threat model. Do not expose these stacks to the public internet unchanged.

Variable Role (study) If leaked or left on a public host
ESHOP_ORDERING_AUTH_BYPASS When 1 / true, Ordering HTTP may skip Bearer validation Unauthenticated access to ordering APIs
ESHOP_ORDERING_JWT_SECRET Symmetric material Ordering trusts when bypass is off; stack identity-service receives the same value via ESHOP_JWT_SYMMETRIC_SECRET interpolation so issued tokens can validate downstream when you turn bypass off Token forgery
ESHOP_JWT_SYMMETRIC_SECRET (identity code path) Signs JWTs in identity-service (compose sets it from the ordering secret for alignment) Forged identity tokens
ESHOP_BASKET_JWT_SECRET Basket HTTP/gRPC demo validation Session / basket abuse
ESHOP_WEBHOOKS_AUTH_BYPASS When 1, Webhooks API skips strict JWT for local demos Misuse of webhook registration endpoints

See .env.example, deploy/compose/.env.stack.example, and SECURITY.md. Treat every secret in templates as disposable lab material.

Root package.json scripts vs manual steps

Goal Use pnpm script Manual alternative
Install workspace pnpm install
Start infra + observability pnpm infra:up docker compose -f deploy/compose/docker-compose.yml -f deploy/compose/observability/docker-compose.observability.yml --env-file deploy/compose/.env.compose.example up -d --wait
Migrations + seeds pnpm db:setup (after cp .env.example .env) Per-service pnpm --filter @eshop/<service> prisma:migrate
Nest APIs on host pnpm dev Start each app with pnpm --filter @eshop/<service> start:dev
Vite UIs pnpm dev:ui Per-app pnpm --filter @eshop/storefront-web dev, etc.
Full stack in Docker pnpm stack:up / pnpm stack:down Same compose files as documented in deploy/compose/README-stack.md
Kubernetes (study) make -C deploy/k3d k3d-up then helm-install-dev helm lint deploy/helm/eshop-nestjs; see [deploy/k3d/README.md](deploy/k3d/README.md)
GitOps (study) kubectl apply per [deploy/argocd/README.md](deploy/argocd/README.md)
OIDC E2E (optional) pnpm test:e2e:oidc Keycloak Compose + JWKS on APIs — [deploy/keycloak/README.md](deploy/keycloak/README.md)
OpenAPI snapshots (maintainers / CI) pnpm contracts:export-openapi (APIs must be running) GET http://127.0.0.1:<port>/api/docs-json per service

Fresh clone minimum: cp .env.example .envpnpm installpnpm buildpnpm dev (plus pnpm dev:ui in a second terminal). Run pnpm test:unit when you want a quick sanity check. Everything else (infra, contracts:*, E2E, k3d, Keycloak) is optional.

OpenAPI / Swagger UI

With pnpm dev running, open Swagger UI per service (JSON at /api/docs-json):

Service Port Swagger UI
identity-service 5051 http://127.0.0.1:5051/api/docs
catalog-service 5052 http://127.0.0.1:5052/api/docs
ordering-service 5053 http://127.0.0.1:5053/api/docs
basket-service 5054 http://127.0.0.1:5054/api/docs
webhooks-service 5055 http://127.0.0.1:5055/api/docs
mobile-bff 5070 http://127.0.0.1:5070/api/docs (static gateway contract)

Workers (order-grace-worker, payment-worker) expose health HTTP only — no public REST catalogue. When you change HTTP contracts on purpose, regenerate snapshots with pnpm contracts:export-openapi (with pnpm dev running) and commit the JSON under contracts/openapi/nest/. CI runs pnpm contracts:check; you do not need it to run the stack locally. See contracts/openapi/README.md.

Validation

Step Command
Lint + format (Biome) pnpm lint or pnpm format:check
Lint + compile (quick gate) pnpm check
PR CI parity (build + contracts + tests) pnpm ci (Docker for integration/E2E)
Compile all packages and apps pnpm build
Unit and selected contract tests pnpm test:unit
OpenAPI snapshot gate (CI; optional locally) pnpm contracts:check
Playwright smoke (no stack) pnpm test:smoke
Integration tests (Docker / Testcontainers) pnpm test:integration
Playwright E2E (probes; skips if stack down) pnpm test:e2e
Helm chart lint (requires Helm CLI) helm lint deploy/helm/eshop-nestjs

GitHub Actions (.github/workflows/eshop-nest-ci.yml) runs install, pnpm build, contract checks, and tests. Locally, after pnpm install: pnpm ci (Docker required for integration/E2E) or pnpm check for lint + compile only

Stack

Component Location / notes
Nest microservices identity, catalog, ordering, basket, webhooks
Workers order-grace-worker, payment-worker
mobile-bff HTTP proxy on 5070
Frontends storefront-web, webhook-client, operations-dashboard (Vite)
PostgreSQL Compose initdb → logical DBs per context
Redis Basket projections
RabbitMQ Integration events + DLQ
Keycloak Optional Compose IdP — deploy/keycloak/
Observability Grafana, Jaeger, Prometheus, Loki, OTel collector — deploy/compose/observability/
Helm deploy/helm/eshop-nestjs (8 workloads + migrate Jobs + PDB/HPA/ServiceMonitor)
Argo CD GitOps manifests — deploy/argocd/, deploy/gitops/environments/
k3d (optional) Local K3s rehearsal — deploy/k3d/

Not in this repository: Temporal, Kong, Istio, Vault, Apicurio, .NET Aspire AppHost, MAUI/HybridApp.

Runtime libraries

Area Choices
Language / runtime TypeScript on Node 20+
API framework NestJS (HTTP; basket-service gRPC)
Web apps React, Vite, Tailwind; @eshop/ui
Data Prisma → PostgreSQL; Redis (basket)
Messaging RabbitMQ; outbox (@eshop/outbox); inbox (@eshop/inbox); @eshop/event-bus-amqp
Auth Symmetric JWT (identity-service) + optional Keycloak OIDC (@eshop/auth JWKS)
Observability @eshop/observability, Pino, Compose observability merge
Testing Vitest/Jest unit; Playwright tests/e2e; Testcontainers tests/integration

What the reference eShop-main (.NET solution) organizes

The upstream solution under eShop-main/src/ is shaped roughly as follows:

  • One ASP.NET Core project per service host: Basket.API, Catalog.API, Ordering.API, Identity.API, Webhooks.API, plus background hosts OrderProcessor and PaymentProcessor.
  • Ordering vertical slice: Ordering.Domain, Ordering.Infrastructure, Ordering.API — domain model and EF persistence separated from HTTP.
  • Ordering.API/Application: CQRS-style commands/queries, validators, pipeline behaviors, and integration-event emission.
  • IntegrationEventLogEF and EventBus / EventBusRabbitMQ: transactional outbox and messaging abstractions.
  • eShop.ServiceDefaults: shared OpenTelemetry and health conventions for Aspire.
  • eShop.AppHost: Aspire orchestrator (dashboard, resource bindings, debugging).
  • Frontends: WebApp (Blazor), WebAppComponents, WebhookClient; mobile-oriented ClientApp / HybridApp.
  • **tests/**: unit and functional suites per bounded context.

This is documented in the upstream eShop-main/README.md architecture diagram (img/eshop_architecture.png), not reproduced here.

What this NestJS repo organizes instead

Repository shape follows pnpm workspaces (pnpm-workspace.yaml): runnable code in apps/, reusable libraries in packages/, and cross-service artefacts in contracts/.

Inside each Nest microservice (for example apps/ordering-service/src/), layering is intentionally consistent:

Folder (typical) Role
api/ HTTP controllers (and module wiring), DTOs, guards, Swagger/OpenAPI
application/ Use cases: command/query handlers, orchestration workflows, domain-facing facades
integration/ AMQP subscribers, integration-event payloads, outbox publish queries
infrastructure/ Prisma modules, adapters to PostgreSQL

Workers (order-grace-worker, payment-worker) expose small HTTP health endpoints and concentrate on Rabbit-driven logic.

packages/domains/ordering holds framework-free aggregates and rules (same idea as Ordering.Domain, without EF).

packages/infrastructure/* replaces cross-cutting .NET DLLs (EventBus*, telemetry helpers, filters) as small typed packages.

Design patterns and UI system

Patterns reused in spirit

  • Domain-driven boundaries — separate Postgres databases per context; catalog vs ordering vs webhooks vs identity.
  • Outbox + message bus — write business data and outbox rows in one transaction; background publisher delivers to RabbitMQ (same conceptual role as IntegrationEventLogEF + bus).
  • CQRS-lite — explicit command/query style handlers in Nest application/ layers rather than fat controllers everywhere.
  • Backend-for-frontendmobile-bff proxies catalog, ordering, basket, and identity HTTP. In the Compose stack profile, identity proxy is on by default (port 5070). The storefront uses 5051 for login and VITE_ESHOP_BFF_ORIGIN for the server-backed cart when authenticated.
  • API versioning and OpenAPI — Nest services expose Swagger UI at /api/docs; snapshots live under contracts/openapi/.

Design system (UI)

  • Reference: Blazor WebApp + WebAppComponents from dotnet eShop.
  • Here: Tailwind CSS tokens via @eshop/ui (shadcn-style React primitives) reused by storefront-web and webhook-client.

Operational UX

  • Reference: Aspire dashboard bundles logs, traces, URLs.
  • Here: operations-dashboard SPA polls health URLs and deep-links Grafana / Prometheus / Jaeger when observability is up (pnpm infra:up).

Parity with dotnet/eShop and Aspire (high level)

This section explains how this repo echoes the reference stacks and where it diverges by design or necessity.

Topic Reference world (dotnet/eShop, Aspire) This repo
Orchestration Aspire AppHost wires projects, connection strings, and dashboards Docker Compose profiles plus root pnpm scripts. No Aspire host: you start infra and apps explicitly.
Service discovery Aspire resource references and generated wiring Explicit URLs in ESHOP_* / VITE_* variables. Easier for Node but more manual than AppHost codegen.
Observability story Aspire promotes OTEL exporters and telemetry integration Compose merge (observability/) started with pnpm infra:up, plus @eshop/observability in processes.
API shape ASP.NET controllers, YARP gateway in the sample Nest controllers, mobile-bff as a thin HTTP proxy for mobile-style clients; storefront calls catalog/ordering directly from the browser in dev (Vite env).
UI Blazor-based sample WebApp React + Vite SPA. Similar page flow and assets only where useful for comparison—not a faithful Blazor replica.
Data stack EF Core, SQL Server variants in tutorials Prisma + PostgreSQL (pgvector for catalog AI-related features when enabled). Different ORM and engine.
Integration events MassTransit-oriented patterns in the .NET ecosystem RabbitMQ + outbox tables; routing keys and flows documented under contracts/. Event names often follow the reference vocabulary for easier mental mapping; wire formats are documented in-repo.

What stayed intentionally similar

  • Bounded contexts (catalog, ordering, basket, identity, webhooks, background payment/grace concepts).
  • Integration-event vocabulary for order lifecycle and catalog signals (see contracts/integration-events.md).
  • gRPC contract for basket (contracts/grpc/basket.proto).
  • A BFF-shaped service (mobile-bff) for consolidation patterns; upstream dotnet gateway tech differs.

What did not stay similar (and why)

  • Aspire UI and project graph — not applicable to a Node monorepo without building a parallel tool; Compose + scripts are the pragmatic substitute.
  • Byte-for-byte UI parity — React and Tailwind differ from Blazor; only partial layout and assets align.
  • Compose stack profile — same Nest footprint as pnpm dev plus storefront nginx. webhook-client and operations-dashboard still expect pnpm dev:ui on the host unless you add more images.
  • Full production hardening — samples use fake credentials; this repo matches that learning posture (see SECURITY.md).

Repository layout

Directory skeleton

Abbreviated tree (omit node_modules, dist, Prisma generated trees, lockfiles). Each Nest service usually mirrors src/api, src/application, src/integration, src/infrastructure, plus prisma/schema.prisma.

eShopOnContainers-NestJS/
├── apps/
│   ├── basket-service/          # Redis + RabbitMQ; HTTP health + gRPC basket API
│   ├── catalog-service/       # Postgres (catalogdb) + RabbitMQ AI hooks optional
│   ├── identity-service/      # Postgres (identitydb)
│   ├── mobile-bff/              # Reverse proxy aggregation for mobile-style callers
│   ├── operations-dashboard/    # SPA: health probes + Grafana/Prometheus/Jaeger links
│   ├── order-grace-worker/
│   ├── ordering-service/      # Postgres (orderingdb) + RabbitMQ
│   ├── payment-worker/
│   ├── storefront-web/        # Main React storefront (Vite)
│   ├── webhook-client/        # SPA to exercise Webhooks API
│   └── webhooks-service/       # Postgres (webhooksdb) + RabbitMQ
├── contracts/
│   ├── golden/               # Integration-event payload samples (JSON)
│   ├── grpc/                 # basket.proto
│   ├── openapi/nest/         # Published OpenAPI snapshots
│   └── *.md                   # Integration-event catalogue docs
├── deploy/
│   ├── compose/              # docker-compose*.yml, env templates, initdb/, observability/
│   ├── docker/               # Dockerfiles + nginx storefront config
│   ├── helm/eshop-nestjs/    # Reference Kubernetes chart
│   ├── k3d/                  # Optional local K3s lifecycle (Makefile)
│   ├── argocd/               # Argo CD Application manifests (GitOps study)
│   ├── gitops/environments/  # Per-env Helm value overlays
│   └── keycloak/             # Optional Keycloak Compose + realm export
├── packages/
│   ├── domains/              # basket, catalog, identity, ordering
│   └── infrastructure/       # auth, event-bus-amqp, health, http-resilience,
│                               # idempotency, inbox, integration-event-types,
│                               # observability, openapi-common, outbox,
│                               # shared-exception-filters, ui
├── tests/
│   ├── e2e/                  # `@eshop/e2e-tests` Playwright
│   └── integration/          # `@eshop/integration-tests` Vitest + containers
├── .env.example
├── package.json
└── pnpm-workspace.yaml

Table (quick lookup)

Path Responsibility
apps/* Runnable Nest servers, React/Vite shells, Dockerfile build targets
packages/domains/* Domain aggregates and validation without framework imports
packages/infrastructure/* Messaging, telemetry, guards, filters, HTTP helpers, @eshop/ui
contracts/ gRPC/proto, golden JSON fixtures, integration-event catalogue, exported OpenAPI
deploy/compose/ Local infra stacks, Observability merges, Postgres init scripts
deploy/docker/ Multi-stage Nest + storefront images
deploy/helm/ Kubernetes chart (study)
deploy/k3d/ Optional k3d cluster + Helm (Makefile targets)
deploy/argocd/ GitOps bootstrap manifests
tests/e2e Browser automation
tests/integration Backing-service integration coverage

Request-centric flow (synchronous HTTP view)

Modern browser clients call backends over HTTP. mobile-bff targets callers that want one host port (5070) proxying catalog, ordering, basket, and (in the Compose stack profile) identity. The storefront calls identity/catalog/ordering via VITE_* origins (5051–5053) and syncs the cart through the BFF (VITE_ESHOP_BFF_ORIGIN/api/basket) when logged in; guests keep sessionStorage unless VITE_ESHOP_CART_GUEST_MODE=true.

Operations dashboard traffic is telemetry and health probes (/api/health, /alive, /health depending on app) plus links to Grafana; it never participates in catalogue or order workflows.

flowchart TB
  subgraph browser["Browser clients"]
    SF[storefront-web]
    WHCli[webhook-client]
    OPS[operations-dashboard]
  end
  subgraph apis["Application tier"]
    ID[identity-service]
    CAT[catalog-service]
    ORD[ordering-service]
    BSK[basket-service]
    WH[webhooks-service]
    BFF[mobile-bff]
    GRC[order-grace-worker]
    PAY[payment-worker]
  end
  subgraph data_plane["Stateful dependencies"]
    PG[(PostgreSQL\nidentitydb catalogdb orderingdb webhooksdb)]
    RD[(Redis\nbasket slots)]
    RMQ[(RabbitMQ\nnot used inline in UX requests)]
  end
  SF -->|"REST JWT login/register"| ID
  SF -->|"catalog REST"| CAT
  SF -->|"orders REST"| ORD
  WHCli -->|"webhook API"| WH
  OPS -.->|"GET health probes only"| ID
  OPS -.-> CAT
  OPS -.-> ORD
  OPS -.-> BSK
  OPS -.-> WH
  OPS -.-> BFF
  OPS -.-> GRC
  OPS -.-> PAY
  BFF -->|"HTTP proxy"| CAT
  BFF -->|"HTTP proxy"| ORD
  BFF -->|"/identity proxy (stack default)"| ID
  ID --> PG
  CAT --> PG
  ORD --> PG
  WH --> PG
  BSK --> RD
  CAT -.->|"async publishes"| RMQ
  ORD -.->|"async publishes"| RMQ
  BSK -.->|"async publishes"| RMQ
  WH -.->|"async publishes"| RMQ
Loading

Async integration flow (RabbitMQ + outbox)

Services treat business commands as transactional work in PostgreSQL and enqueue integration-event rows (outbox). A dispatcher reads those rows and publishes to RabbitMQ with routing keys from contracts/integration-events.md. Separate consumers (possibly the same codebase, different subscriptions) mutate local state.

The diagram emphasizes principal message paths rather than listing every correlation id or retry policy.

flowchart LR
  subgraph producers["Transactional publishers outbox to AMQP"]
    ORD_pub[ordering-service]
    CAT_pub[catalog-service]
    PAY_pub[payment-worker]
    GRACE_pub[order-grace-worker]
  end
  BUS(("RabbitMQ"))
  subgraph subscribers["Consumers"]
    ORD_sub[ordering-service]
    CAT_sub[catalog-service]
    BSK_sub[basket-service]
    WH_sub[webhooks-service]
    PAY_sub[payment-worker]
  end
  ORD_pub -->|order lifecycle events| BUS
  CAT_pub -->|stock and price events| BUS
  PAY_pub -->|payment outcomes| BUS
  GRACE_pub -->|GracePeriodConfirmed| BUS
  BUS -->|OrderStarted| BSK_sub
  BUS -->|AwaitingValidation| CAT_sub
  BUS -->|stock confirmed or rejected| ORD_sub
  BUS -->|stock confirmed saga| PAY_sub
  BUS -->|payment succeeded or failed| ORD_sub
  BUS -->|paid or shipped hooks| CAT_sub
  BUS -->|paid shipped price| WH_sub
  BUS -->|GracePeriodConfirmed| ORD_sub
Loading

How to read it

  1. Left column emits events after Postgres commits—the outbox guarantees AMQP publishes track database state (@eshop/outbox).
  2. Center remains the shared broker topology (queues/bindings derive from @eshop/event-bus-amqp setup).
  3. Right column reacts: for example basket-service clears state on OrderStartedIntegrationEvent; catalog-service participates in awaiting-validation and paid flows; payment-worker and ordering-service continue the saga after stock confirmation.

Consult contracts/integration-events.md for the publisher/consumer table and golden/ for JSON samples.

Optional k3d path (Kubernetes rehearsal)

flowchart LR
  dev[Developer laptop]
  k3d[k3d cluster eshop-dev]
  helm[Helm chart eshop-nestjs]
  pods[Nest Deployments + Services]
  dev -->|make -C deploy/k3d k3d-up| k3d
  dev -->|helm-install-dev| helm
  helm --> pods
  k3d --> pods
Loading

Compose + pnpm dev remain the default loop; k3d exercises probes, Services, and Helm values without a cloud cluster. Details: [deploy/k3d/README.md](deploy/k3d/README.md).

Topology and ports (typical local dev)

Postgres (host 55432), Redis (56379), RabbitMQ AMQP (55672, management 55673). Application HTTP defaults include catalog 5052, ordering 5053, basket 5054, webhooks 5055, identity 5051, workers 5065 / 5066, mobile BFF 5070. Vite UIs: storefront 5173, webhook client 5174, operations hub 5188 (if a port is busy, Vite picks the next free one). Loki 3100, Grafana 3200, Prometheus 9099, Jaeger 16686 when pnpm infra:up includes observability. Static stack storefront on 8080 with the stack profile.

Further reading

Topic Location
Compose profiles and env deploy/compose/README.md
Full stack in Docker deploy/compose/README-stack.md
Helm chart deploy/helm/eshop-nestjs/README.md
k3d local cluster deploy/k3d/README.md
Argo CD GitOps deploy/argocd/README.md
Keycloak OIDC deploy/keycloak/README.md
Observability merge deploy/compose/observability/README.md
E2E tests tests/e2e/README.md
Contract artefacts contracts/README.md
Security expectations SECURITY.md

License

MIT License. Third-party notices: NOTICE.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors