The high-performance, event-driven engine powering the Schematica platform.
Schematica is a collaborative API design and documentation workspace. This repository contains the Backend API, engineered to provide a robust, scalable foundation for managing OpenAPI specifications, handling real-time concurrency, and enforcing granular access control.
It is built as a showcase of modern backend engineering principles, prioritizing Type Safety, Clean Architecture, and Developer Experience.
Note: This repository works in tandem with the Schematica Frontend. This project was originally built as an internal tool to solve specific workflow friction points at my previous company. It is now provided as "source-available" for educational purposes.
The Schematica API avoids the "Spaghetti Monolith" trap by adopting a Modular, Event-Driven Architecture.
The application structure mirrors the business domain. Code is organized into cohesive modules (ProjectsModule, LockingModule) rather than technical layers. This ensures that related logic sits together, making the codebase intuitive to navigate and easy to scale.
A core principle of this system is the strict separation of Primary Business Logic from Side Effects. We utilize @nestjs/event-emitter to achieve this decoupling.
- The Problem: In many apps, a
createProjectservice method grows to 100 lines because it has to send emails, write audit logs, and notify Slack. - Our Solution: The
ProjectsServiceperforms only the database transaction and emits a typed event (PROJECT_CREATED). - The Benefit: Independent Listeners handle the side effects. If the Notification Service fails, the Audit Log is still written, and the User's request still succeeds. This results in a highly resilient system.
Security is implemented in two layers for defense-in-depth:
- Coarse-Grained (
RolesGuard): Protects entire routes based on a user's system-wide role (e.g., only anadmincan access/admin/*). - Fine-Grained (
AccessControlService): Implements Attribute-Based Access Control (ABAC). It evaluates complex conditions at runtimeβchecking Project Ownership, Team Membership, and Deny Listsβto authorize specific resources.
For features requiring high-frequency updates (like Collaborative Editing Locks), we bypass the database entirely. The LockingService manages ephemeral state using in-memory structures, ensuring the real-time experience remains distinct from persistent storage latency.u
- Hybrid Auth: Supports both Local Strategy (Bcrypt hashing) and OAuth2 (GitLab) via Passport.js.
- Secure Sessions: Uses
fastify-secure-sessionwith@fastify/cookiefor robust session management. - ABAC (Attribute-Based Access Control): Beyond simple Roles (
adminvsuser), we implement fine-grained guards (ProjectOwnerGuard,ProjectViewerGuard) that evaluate ownership and team membership at runtime. - Environment Vault: Project secrets (API Keys, Tokens) are encrypted at rest using
AES-256-GCMbefore entering the database.
- Pessimistic Locking: Prevents "last-write-wins" race conditions. When a user edits an endpoint, a WebSocket event locks that resource globally. Other users see the lock status update instantly via
Socket.IO. - Live Notifications: mentions (
@username) in notes trigger instant alerts to connected clients.
- Spectral Linting: Integrated
@stoplight/spectralengine to lint OpenAPI specs against a custom ruleset (.spectral.yaml) on every save or import. - Spec Reconciliation: A smart diffing engine that compares an imported OpenAPI JSON against the database state, determining exactly which endpoints to Create, Update, or Delete (Soft Sync).
- Zero-Config Mocking: The API can instantly generate mock responses for any endpoint defined in a project.
- Faker Integration: Leverages
json-schema-fakerand@faker-js/fakerto generate realistic, schema-compliant data (names, emails, UUIDs) based on the OpenAPI schema definition. - Locale & State Support: Clients can request specific HTTP status codes (e.g., test a
404) or locales (e.g.,fa,en) via headers.
For a quick start, import the official Postman Collection. It includes environment variables and pre-configured requests for Auth, Projects, and Mocking.
We chose a stack that balances developer ergonomics with raw performance.
| Category | Technology | Reasoning |
|---|---|---|
| Runtime | Bun | Chosen for its ultra-fast startup time (critical for dev loops) and native TypeScript support. |
| Framework | NestJS | Provides the modular structure and dependency injection container needed for enterprise-grade apps. |
| HTTP Adapter | Fastify | Replaces Express.js to maximize throughput and minimize overhead. |
| Database | SQLite (Dev) | Keeping the DB portable allows for zero-setup ease of use. Can be swapped for PostgreSQL in production via Prisma. |
| ORM | Prisma | Best-in-class type safety. The generated client ensures DB queries align perfectly with TypeScript interfaces. |
| Real-time | Socket.IO | Manages WebSocket namespaces and rooms for granular update broadcasting. |
| Documentation | Swagger / OpenAPI | The API is self-documenting. We use class-validator decorators to drive both runtime validation and Swagger generation. |
| Linting | Spectral | An open-source JSON/YAML linter used to govern API design quality programmatically. |
The directory structure follows Domain-Driven Design (DDD) principles. We avoid grouping files by type (controllers/services) and instead group them by Module/Domain.
src/
βββ access-control/ # π‘οΈ ABAC Logic (Permissions service)
βββ api-linting/ # π Spectral wrapper service for OAS validation
βββ audit/ # π Event listeners for Audit Logging
βββ auth/ # π Passport Strategies (JWT, Local, GitLab)
βββ changelog/ # π Event listeners for Changelog generation
βββ common/ # π§± Shared Kernel (Guards, Interceptors, Filters, Utils)
βββ config/ # βοΈ Type-safe configuration namespace
βββ encryption/ # π AES-256-GCM encryption service for Secrets
βββ locking/ # π¦ In-Memory resource locking (Services + Gateway)
βββ mock-server/ # π Dynamic Mocking Engine (Faker + JSON Schema)
βββ notifications/ # π Notification distribution logic
βββ projects/ # π¦ Core Domain: Projects (Aggregates Endpoints, Envs)
β βββ endpoints/ # - Sub-domain: API Endpoints
β βββ environments/ # - Sub-domain: Env Variables
β βββ spec-builder/ # - Logic to assemble DB records into OpenAPI JSON
β βββ spec-reconcile/ # - Logic to diff JSON against DB records
βββ main.ts # π Application Bootstrap
Security is implemented in layers for defense-in-depth:
- Route Level (RBAC): The
RolesGuardprotects entire controllers based on system roles.@Roles(Role.admin) // Only Admins can touch this @Controller('admin/users')
- Resource Level (ABAC): The
AccessControlServiceperforms logic-heavy checks. It inspects theProject, theUser, and theirTeamMembershipto determine access (Owner vs. Viewer).// Inside ProjectOwnerGuard const canOwn = await this.accessControlService.canOwnProject(user, projectId);
To keep business logic clean, we strictly separate Actions from Reactions using EventEmitter2.
- Scenario: A user updates an endpoint's status.
- Service Layer: Updates the database record. Emits
EndpointEvent.STATUS_UPDATED. Returns the result. - Audit Listener: Catches event -> Writes to
AuditLogtable. - Changelog Listener: Catches event -> Formats a human-readable message ("User X changed status to Published") -> Writes to
Changelogtable. - Notification Listener: Catches event -> Checks for watchers -> Dispatches WebSocket message.
Benefit: If the Audit Logger fails, the User's request still succeeds. The system is loosely coupled and highly resilient.
While the Frontend and Backend are developed in separate repositories for clear separation of concerns, they are designed to be deployed as a Single Unit.
The AppModule is configured to serve static assets from the public/ directory (excluding the /api prefix).
- Workflow:
- Build the Schematica Frontend.
- Place the resulting
dist/files intoschematica-api/public/. - Start the NestJS server.
- Result: The NestJS app serves the API on
/api/v2/...and the React SPA on/. - Why? This eliminates CORS issues in production, simplifies deployment (one container to manage), and allows cookies to be shared securely between the API and the UI.
Importing an OpenAPI file isn't just a database overwrite. The SpecReconciliationService acts as a specialized "Diff Engine":
- Normalize: It flattens the incoming JSON spec into a map of
method:pathkeys. - Compare: It fetches existing endpoints from the DB.
- Decision Matrix:
- Match: Compare signatures. If different, mark for Update.
- New: Mark for Create.
- Missing: Mark for Delete.
- Execute: Runs the calculated changes inside a single Prisma Transaction to ensure atomicity.
Follow these steps to get the Schematica platform running locally.
- Bun (v1.1+) - The runtime and package manager.
- Git - Version control.
- (Optional) Docker - If you prefer running a PostgreSQL instance instead of the default SQLite dev database.
# Clone the repository
git clone https://github.com/Amir-Zouerami/schematica-api.git
cd schematica-api
# Install dependencies using Bun
bun installCopy the example configuration file. The default settings are tuned for local development.
cp .env.example .envπ Critical Security Note: The
.envfile contains theENCRYPTION_KEY. This key is used to encrypt/decrypt sensitive Environment Secrets (API Keys) in the database. If you change this key, all previously encrypted secrets in the DB will become unreadable.
We use Prisma to manage the schema and seeding. The seed script is comprehensiveβit doesn't just add users; it constructs an entire mock ecosystem (Projects, Teams, Endpoints, and encrypted Secrets).
# Run migrations and seed the database
bun run migrate:resetWhat gets seeded?
- Users:
amir.zouerami(Admin),brooklyn.lee(Member),guest.user(Guest). - Password:
password123(for all seeded users). - Projects:
- Project Nova: A full public-facing API spec.
- Project Apollo: An internal microservice spec.
- Teams: Engineering hierarchy (Backend, Frontend, Leadership).
# Start in development mode (Hot Reload)
bun run start:dev
# Start in production mode
bun run start:prodThe API will be available at http://localhost:3000/api/v2.
The Swagger documentation is at http://localhost:3000/docs.
To experience the full platform, you need to serve the UI. This backend is configured to serve the Single Page Application (SPA) static files from the /public directory.
- Clone and build the Schematica Frontend.
git clone https://github.com/Amir-Zouerami/schematica-frontend.git cd schematica-frontend bun install bun run build - Copy the contents of the frontend
dist/folder into the backendpublic/folder.# From the frontend directory cp -r dist/* ../schematica-api/public/
- Restart the NestJS server. Access the app at
http://localhost:3000.
Note: The API routes are prefixed with /api/v2, so they do not conflict with the frontend routing.
This project uses SQLite in production. While this simplifies infrastructure, it requires specific attention to Data Persistence.
You MUST mount a Persistent Volume for the database file. If you do not, your database (and all user data) will be permanently wiped every time the application restarts or re-deploys.
Example Configuration:
- Mount a volume to
/datainside your container. - Set your
DATABASE_URLto point to that volume.
# Point to the mounted volume directory
DATABASE_URL="file:/data/production.db"Create a specific .env.prod file for production. This file is automatically picked up by the production scripts (start:prod, migrate:prod).
Ensure these variables are set in your production environment secrets:
| Variable | Value | Description |
|---|---|---|
NODE_ENV |
production |
Optimizes NestJS for performance. |
DATABASE_URL |
file:/data/prod.db |
Must be on a persistent volume. |
ENCRYPTION_KEY |
(32-byte hex) | CRITICAL. Used to encrypt Project Secrets. If lost, secrets cannot be recovered. |
JWT_SECRET |
(Random string) | A strong, long random string for signing auth tokens. |
INITIAL_ADMIN_USERNAME |
admin |
The username for the system super-admin. |
INITIAL_ADMIN_PASSWORD |
securePass! |
The initial password for the super-admin. |
In production, we do not use the development seed. Instead, we use a specialized Production Seed that safely bootstraps the Root Admin without overwriting data.
Run these commands in your deployment pipeline (or Docker CMD):
# 1. Install & Build
bun install --frozen-lockfile
bun run build
# 2. Database Bootstrapping
# Applies schema changes and ensures Root Admin exists.
bun run migrate:prod
bun run seed:prod
# 3. Start the Server
bun run start:prodIf you want to simulate the production build locally, simply create a .env.prod file and use the dedicated scripts:
# These scripts automatically load .env.prod
bun run migrate:prod
bun run seed:prod
bun run start:prodTo reset the production database (wipes data and restores Admin user only):
bun run prod:danger:reset- Navigate to your deployment URL.
- Log in using the credentials you defined in
INITIAL_ADMIN_USERNAMEandINITIAL_ADMIN_PASSWORD. - You now have full Admin access to create teams, projects, and invite other users.
This project is provided as "Source-Available" software.
It serves three primary purposes:
- A Portfolio Piece: Demonstrating high-level architectural decisions, strict typing, and modern backend patterns.
- Helping The Community: It gives back to the community i hold so dearly, the open-source community. If you need such a platform for you company, feel free to use it.
- A Reference Implementation: Helping other developers learn how to build robust, event-driven systems with NestJS and Prisma.
This was originally an internal tool built for a specific company context and it's being actively used to this day. While I have open-sourced the code for educational purposes, I am not maintaining it as a community-driven project.
You are highly encouraged to:
- π± Fork the repository.
- π§ Modify it to fit your own needs.
- π‘ Use the patterns found here in your own projects.
This project is licensed under the GNU General Public License v3.0.
