Saltar al contenido principal

Kysely Module

Overview

The kysely module is a developer and admin utility that exposes the database migration and seed history via a REST API. It allows engineers to inspect which Kysely migrations and seeds have been applied to a given environment without direct database access.


Architecture

The module follows the hexagonal (ports & adapters) architecture used across the backend:

kysely/
├── kysely.module.ts # NestJS module — wires DI
├── application/
│ ├── kysely.service.ts # Use cases: getMigrations, getSeeds
│ └── kysely.service.spec.ts
├── domain/
│ └── kysely-repository.domain.ts # IKyselyRepository port + KYSELY_REPOSITORY token
├── infrastructure/
│ └── kysely.repository.ts # Kysely DB adapter (reads views)
└── interfaces/
├── kysely.controller.ts # HTTP endpoints
└── kysely.controller.spec.ts

Layer responsibilities

LayerFileResponsibility
Domainkysely-repository.domain.tsDefines the IKyselyRepository port and the KYSELY_REPOSITORY injection token
Applicationkysely.service.tsOrchestrates use cases; depends only on IKyselyRepository
Infrastructurekysely.repository.tsImplements IKyselyRepository using Kysely queries against two DB views
Interfacekysely.controller.tsThin HTTP controller; maps routes to service methods, handles errors

Dependency wiring

KyselyController → KyselyService → IKyselyRepository ← KyselyRepository

KYSELY_REPOSITORY token (module provider)

Domain Concepts

  • Migration — A schema change file managed by Kysely's migrator. Stored in kysely_migration table; exposed as kyselyMigrationView.
  • Seed — A data seeding script managed by Kysely's seeder. Stored in kysely_seeds table; exposed as kyselySeedsView.

Both records contain only { name: string; timestamp: string }.


API Endpoints

Base path: /kysely

All endpoints require:

  • A valid Firebase ID token (Authorization: Bearer <token> or flowpos-id-token cookie)
  • The Kysely permission resource role on the authenticated user

GET /kysely/migrations

Returns all applied migrations ordered by timestamp descending.

Response 200:

[
{ "name": "20240201000000_add_locations", "timestamp": "2024-02-01T00:00:00.000Z" },
{ "name": "20240101000000_initial_schema", "timestamp": "2024-01-01T00:00:00.000Z" }
]

GET /kysely/seeds

Returns all applied seeds ordered by timestamp descending.

Response 200:

[
{ "name": "seed_products", "timestamp": "2024-03-15T00:00:00.000Z" },
{ "name": "seed_businesses", "timestamp": "2024-03-01T00:00:00.000Z" }
]

Error responses (both endpoints)

StatusMeaning
401Missing or invalid Firebase token
403User lacks the Kysely permission
500Database error

Database Views

The module reads from two PostgreSQL views rather than the underlying tables directly:

ViewUnderlying tablePurpose
kyselyMigrationViewkysely_migrationApplied schema migrations
kyselySeedsViewkysely_seedsApplied seed runs

Both views select name and timestamp columns.


Design Decisions

Why expose migrations and seeds via HTTP? Allows platform engineers and admins to verify environment state (e.g., "did staging apply migration X?") without needing database credentials or SSH access.

Why two separate views instead of querying tables directly? Views abstract the underlying Kysely-internal table structure. If Kysely changes its internal schema, the view contract is the stable adapter boundary — the module code does not need to change.

Why KYSELY_REPOSITORY injection token? Following strict hexagonal architecture, the application layer (KyselyService) depends on the IKyselyRepository interface (the port), not the concrete KyselyRepository class. The token allows NestJS DI to resolve the concrete class at runtime while keeping the service fully decoupled from the infrastructure adapter. This makes unit tests simpler — mock the interface, not the class.


  • Database views: packages/backend/database/src/types/database.types.tsKyselyMigrationView, KyselySeedsView
  • Migrations folder: packages/backend/database/src/migrations/
  • Seeds folder: packages/backend/database/src/seeds/
  • Database module: apps/backend/src/database/
  • Connection pool documentation: docs/database/connection-pool-management.md