Contract-First Design
FORGE takes a contract-first approach to application generation. Every application produced by FORGE adheres to strict contracts that define API endpoints, database schema, permissions, and frontend pages. These contracts act as the specification layer between backends and frontends, ensuring that any supported backend can power any supported frontend without modification.
The contract defines what must exist. The implementation defines how it works.
Why Contracts?
When generating full-stack applications across multiple backend frameworks (Rust, Laravel, FastAPI, Node.js) and multiple frontend frameworks (Next.js, Nuxt.js, Angular), consistency is non-negotiable. Without contracts, each backend would implement slightly different API shapes, each frontend would expect different response formats, and swapping one technology for another would require rewriting integration code.
TIP
Think of contracts like a building blueprint. Whether the house is built with brick, wood, or steel, the floor plan stays the same. Rooms are in the same places, doors open the same way, and plumbing connects identically.
FORGE CLI
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Provider │ │ Backend │ │ Frontend │
│ Contracts │ │ Contracts │ │ Contracts │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
┌────┬──┴──┐ ┌─────┼─────┐ ┌────┼────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
Twilio Uni More Rust Laravel … Next Nuxt AngularAPI Contracts
API contracts define the exact endpoint paths, HTTP methods, request bodies, and response shapes that every generated backend must implement. The source of truth is an OpenAPI specification that all backends conform to.
Endpoint Definitions
Every FORGE application exposes the same API surface regardless of backend:
AUTH
├── POST /api/v1/auth/register → { data: { user, token } }
├── POST /api/v1/auth/login → { data: { user, token } }
├── POST /api/v1/auth/logout → { message }
├── POST /api/v1/auth/refresh → { data: { token } }
└── GET /api/v1/auth/me → { data: { user } }
ADMIN (requires authentication + permissions)
├── GET /api/v1/admin/users → { data: [...], meta: {...} }
├── POST /api/v1/admin/users → { data: user }
├── GET /api/v1/admin/users/:id → { data: user }
├── PUT /api/v1/admin/users/:id → { data: user }
├── DELETE /api/v1/admin/users/:id → { message }
├── ... (same pattern for roles, contents, menus, settings, etc.)
PUBLIC (no authentication required)
├── GET /api/v1/contents/:slug → { data: content }
└── GET /api/v1/menus → { data: [...] }Response Format
All backends return responses in a consistent envelope:
// Success (single resource)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "Ahmed"
}
}
// Success (list with pagination)
{
"data": [{ ... }, { ... }],
"meta": {
"total": 42,
"page": 1,
"per_page": 15
}
}
// Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"email": ["The email field is required"]
}
}
}Standard Error Codes
Every backend uses the same set of error codes:
| Code | HTTP Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 422 | Request body failed validation |
UNAUTHORIZED | 401 | Missing or invalid authentication |
FORBIDDEN | 403 | Authenticated but lacks permission |
NOT_FOUND | 404 | Resource does not exist |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Unexpected server error |
WARNING
Backends must never return raw framework errors. All errors must be wrapped in the standard error envelope. This ensures frontends can parse every response with a single error handler.
OpenAPI as Source of Truth
The API contract is formally defined as an OpenAPI 3.0 specification:
# contracts/api/openapi.yaml
openapi: 3.0.3
info:
title: FORGE Application API
version: 1.0.0
paths:
/api/v1/auth/login:
post:
summary: Login user
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email, password]
properties:
email:
type: string
format: email
password:
type: string
minLength: 8
responses:
'200':
description: Login successful
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'422':
$ref: '#/components/responses/ValidationError'TypeScript types for the frontend are generated directly from this OpenAPI spec, guaranteeing type-safe API consumption.
Database Contracts
Database contracts define the required tables, columns, types, and indexes that every generated application must have. While column naming may vary slightly by backend convention (snake_case vs camelCase), the logical schema is identical.
Required Tables
Every FORGE application includes these base tables:
| Table | Purpose |
|---|---|
languages | Supported application languages |
users | User accounts |
roles | Named role definitions |
permissions | Granular permission entries |
role_user | Many-to-many: user-role assignment |
permission_role | Many-to-many: role-permission assignment |
password_resets | Password reset tokens |
otp_codes | One-time password codes (if OTP auth enabled) |
settings | Key-value application settings |
translations | Translatable UI strings |
api_keys | External API key management |
audit_logs | Action audit trail |
media | Polymorphic file attachments |
contents | CMS content pages |
menus | Navigation menu structures |
Column Contracts
Each table has required columns. For example, users:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(50) UNIQUE,
password VARCHAR(255) NOT NULL,
avatar VARCHAR(500),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);TIP
All tables use UUID primary keys and include created_at / updated_at timestamps. This convention is enforced across every backend generator.
Index Contracts
Performance-critical indexes are part of the contract:
-- Users
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_phone ON users(phone);
-- Contents
CREATE INDEX idx_contents_slug ON contents(slug);
CREATE INDEX idx_contents_type ON contents(type);
CREATE INDEX idx_contents_status ON contents(status);
-- Audit logs
CREATE INDEX idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at);Permission Contracts
FORGE uses a role-based access control (RBAC) system. The permission contract defines which permissions must exist for each module and how they map to API endpoints.
Permission Naming Convention
Permissions follow the pattern {module}.{action}:
users.view users.create users.edit users.delete
roles.view roles.create roles.edit roles.delete
permissions.view
contents.view contents.create contents.edit contents.delete
menus.view menus.create menus.edit menus.delete
settings.view settings.edit
translations.view translations.edit
api_keys.view api_keys.create api_keys.edit api_keys.delete
audit.viewPermission-to-Endpoint Mapping
Each admin endpoint requires a specific permission:
| Endpoint | Required Permission |
|---|---|
GET /api/v1/admin/users | users.view |
POST /api/v1/admin/users | users.create |
PUT /api/v1/admin/users/:id | users.edit |
DELETE /api/v1/admin/users/:id | users.delete |
GET /api/v1/admin/contents | contents.view |
POST /api/v1/admin/contents | contents.create |
PUT /api/v1/admin/contents/:id | contents.edit |
DELETE /api/v1/admin/contents/:id | contents.delete |
Default Roles
Every generated application seeds two roles:
| Role | Permissions |
|---|---|
super_admin | All permissions (wildcard) |
admin | All permissions except permissions.view, api_keys.*, audit.view |
WARNING
The super_admin role should be assigned sparingly. In production, most administrators should use the admin role or custom roles with specific permissions.
Page Contracts
Page contracts define which frontend pages must exist in every generated application. This ensures that regardless of the frontend framework, the user-facing application has complete coverage.
Required Public Pages
/ Home page
/login User login
/register User registration
/password/forgot Forgot password
/password/reset Reset password with token
/:slug Dynamic CMS content pageRequired Protected Pages
/profile View profile (requires auth)
/profile/edit Edit profile (requires auth)
/profile/password Change password (requires auth)Required Admin Pages
/admin Dashboard
/admin/users User list (users.view)
/admin/users/create Create user (users.create)
/admin/users/:id/edit Edit user (users.edit)
/admin/roles Role list (roles.view)
/admin/roles/create Create role (roles.create)
/admin/roles/:id/edit Edit role (roles.edit)
/admin/permissions Permission list (permissions.view)
/admin/contents Content list (contents.view)
/admin/contents/create Create content (contents.create)
/admin/contents/:id View content (contents.view)
/admin/contents/:id/edit Edit content (contents.edit)
/admin/menus Menu list (menus.view)
/admin/menus/create Create menu (menus.create)
/admin/menus/:id/edit Edit menu (menus.edit)
/admin/settings App settings (settings.view)
/admin/translations Translation mgmt (translations.view)
/admin/api-keys API key mgmt (api_keys.view)
/admin/audit Audit logs (audit.view)Each admin page enforces its required permission. If a user navigates to a page they lack permission for, the frontend displays an "Access Denied" view without making the API call.
Validating Contracts
FORGE provides CLI commands to verify that a generated application conforms to its contracts.
API Validation
# Verify all required endpoints exist and return correct shapes
forge validate:api
# Validate a specific backend
forge validate:api --backend=rustThe validator starts the backend server, sends requests to every contracted endpoint, and verifies:
- The endpoint exists and does not return 404
- The response matches the expected JSON schema
- Error responses use the standard error envelope
- Authentication and permission checks are enforced
Page Validation
# Verify all required pages exist in the frontend
forge validate:pages
# Validate a specific frontend
forge validate:pages --frontend=nextjsThe page validator checks that:
- Every required route file exists in the project
- Protected pages include authentication guards
- Admin pages include permission checks
- Public pages are accessible without authentication
Full Contract Test Suite
# Run the complete contract test suite
forge test:contractsThis runs integration tests that exercise the full stack: the backend serves the API, and automated tests verify every contracted behavior end-to-end.
TIP
Run forge test:contracts before deploying to production. It catches contract violations that unit tests might miss, such as a missing permission check on an admin endpoint.
Benefits
| Benefit | Description |
|---|---|
| Interchangeable backends | Start with Rust, switch to Laravel later -- same API, same frontend |
| Interchangeable frontends | Start with Next.js, add Nuxt.js later -- same API, same behavior |
| Swappable providers | Switch from Twilio to Unifonic with a config change |
| Testability | Mock any provider or backend behind the contract interface |
| Auto-generated docs | OpenAPI spec produces API documentation automatically |
| Type safety | TypeScript types generated from OpenAPI for frontend consumption |
| Upgrade safety | Contract tests catch breaking changes before they reach production |