Skip to content

Repository files navigation

Node Inventory API

A warehouse inventory API built with Node.js, TypeScript, GraphQL, Prisma, and PostgreSQL.

The codebase separates domain rules, application workflows, delivery, and persistence. Business logic depends on repository interfaces rather than GraphQL, Express, Prisma, or PostgreSQL, allowing each boundary to be tested and replaced independently.

See the domain vocabulary and business rules.

Architecture

HTTP / GraphQL
      |
Application use cases
      |
Repository ports
      |
      +-- Prisma repositories ---- PostgreSQL
      |
      `-- In-memory repositories

The dependency direction points inward:

  • Domain objects enforce business invariants.
  • Use cases coordinate domain objects through repository ports.
  • GraphQL and Express translate transport concerns.
  • Prisma repositories translate persisted records into domain objects.
  • main.ts selects and assembles the production adapters.

This view describes the application boundaries. See the AWS architecture for the deployed network, runtime, IAM, Terraform ownership, and lifecycle relationships.

HTTP delivery

Express listens on http://localhost:3000 by default. PORT changes the listening port without changing the route structure.

Method URL Preview key Purpose
GET /health No Process liveness. Returns 200 with {"status":"ok"} without querying PostgreSQL.
GET /ready No Database readiness. Runs SELECT 1 through Prisma and returns 200 with {"status":"ready"} or 503 with {"status":"not_ready"}.
GET or POST /graphql Yes GraphQL Yoga interface. POST executes operations; HTML GET requests open Yoga's browser interface.
GET / Yes Returns the current Hello World! application response.
GET /lifetime Yes Demonstrates application, request, and transient object lifetimes.

Example local requests:

curl http://localhost:3000/health
curl http://localhost:3000/ready

curl \
  --header "X-Preview-Key: $APP_PREVIEW_KEY" \
  --header 'content-type: application/json' \
  --data '{"query":"{ health }"}' \
  http://localhost:3000/graphql

GraphQL delivery

GraphQL Yoga exposes the application use cases through thin resolvers. Zod validates external input before it enters the application layer.

The delivery boundary also provides:

  • intentional BAD_USER_INPUT, NOT_FOUND, and CONFLICT errors;
  • a stable extensions.issues[] error shape;
  • masking for unexpected internal failures;
  • integer-cent conversion into the domain Money value object;
  • a GraphQL product-unit enum aligned with the domain values.

The assembled HTTP boundary uses an application preview key to keep local and disposable environments private. Organization registration, login, JWT verification, and authenticated GraphQL context creation are implemented. Tenant-scoped authorization remains under development. See the preview-key and authentication designs for those boundaries.

Example product mutation:

mutation CreateProduct($input: CreateProductInput!) {
  createProduct(input: $input) {
    id
    organizationId
    supplierId
    sku
    label
    unit
    purchasePriceCent
    isActive
  }
}
{
  "input": {
    "organizationId": "01JZQ4QAZ4JZX2N1EHMEKYP2YJ",
    "supplierId": "01JZQ4R2D96B5YD3YJ9HXXE89P",
    "sku": "SKU993",
    "label": "T-shirt",
    "unit": "PIECE",
    "purchasePriceCent": 2500,
    "isActive": true
  }
}

PostgreSQL persistence

One Prisma client is shared by all repositories. Repository constructors accept that client explicitly, which also allows database tests to supply an isolated Testcontainers connection.

See the setup for PostgreSQL, Prisma, migration, and database test commands.

Testing

The test suite is separated by boundary:

  • Domain tests verify entities, value objects, invariants, and calculations.
  • Use-case tests verify workflows through individual in-memory repositories.
  • GraphQL tests execute Yoga operations with fresh in-memory repository sets.
  • HTTP integration tests verify the assembled Express application.
  • Database tests apply committed migrations to disposable PostgreSQL containers and verify Prisma repositories. The stock-movement adapter tests cover aggregate reconstruction and prove rollback when one nested line fails.

The default suite stays independent from Docker:

npm test

Database tests with Docker Desktop or a Docker-compatible CI runner:

npm run test:database

Database tests with local Colima:

npm run test:database:colima

Testcontainers starts postgres:17-alpine, applies migrations with prisma migrate deploy, provides the generated connection URL to Vitest, and removes the container after the suite.

Local development

Install dependencies:

npm install

Create the local environment file:

cp .env-sample .env

Set DATABASE_URL, APP_PREVIEW_KEY, and JWT_SECRET in .env, start PostgreSQL, and apply the committed migrations. Use at least 32 random bytes for secrets in shared environments; for local development you can generate a value with openssl rand -base64 32.

Then prepare the database:

docker compose up --wait database
npx prisma migrate deploy
npx prisma generate

Start the API:

npm run dev

The server listens on http://localhost:3000; GraphQL Yoga is mounted at /graphql.

Local container verification

Compose runs PostgreSQL on an internal network and also publishes it on the host loopback interface for development tools such as TablePlus. The database uses a named volume so it survives ordinary container restarts. Migrations remain an explicit operation and are not part of the API entry point.

The Compose API service reads APP_PREVIEW_KEY and JWT_SECRET from .env and refuses to start if either is missing.

Build the image, migrate the database, and start the API:

docker compose build
docker compose up --detach --wait database

docker compose run --rm api \
  npx --no-install prisma migrate deploy

docker compose up --detach --wait api

Verify the operational and GraphQL endpoints with the preview key from .env. Export the same value in the current shell so curl can read it:

curl http://localhost:3000/health
curl http://localhost:3000/ready

export APP_PREVIEW_KEY="replace-with-local-preview-key"

curl \
  --header "X-Preview-Key: $APP_PREVIEW_KEY" \
  --header 'content-type: application/json' \
  --data '{"query":"{ health }"}' \
  http://localhost:3000/graphql

Set HTTP_PORT when port 3000 is already in use:

HTTP_PORT=32000 docker compose up --detach --wait api

Stop the environment while retaining its database volume:

docker compose down

Remove the environment and its local database data:

docker compose down --volumes

Commands

npm test                       # fast domain/application/delivery tests
npm run test:database          # PostgreSQL tests through Testcontainers
npm run test:database:colima   # PostgreSQL tests through local Colima
npm run typecheck              # TypeScript validation
npm run lint                   # ESLint
npm run build                  # compile to dist
npx prisma format              # format Prisma schema file
npx prisma validate            # validate Prisma configuration and schema
npx prisma generate            # regenerate Prisma Client
npx prisma migrate dev         # create/apply development migrations
npx prisma migrate deploy      # apply committed migrations
npx prisma studio              # inspect the development database

Project structure

src/
  domain/                 domain entities and value objects
  application/
    auth/                 authenticated actor types under development
    ports/                repository contracts
    use-cases/            application workflows
  delivery/
    graphql/              schema, resolvers, validation, error mapping
    http/                 Express server and routes
  infra/
    inmemory/             map-backed repository adapters
    database/             Prisma client factory and repository adapters
    auth/                  password-hashing and token components

prisma/
  schema.prisma           relational model
  migrations/             committed PostgreSQL migrations

tests/
  domain/                 domain behavior
  application/            use-case behavior
  delivery/               GraphQL behavior
  integration/            HTTP application behavior
  database/               Prisma/PostgreSQL behavior

About

Domain-first Node.js inventory API practice project with TypeScript, tests, GraphQL, Prisma, and PostgreSQL

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages