Contract-first, multi-stack API code generator.
Write .veld contracts → get typed backends, frontend SDKs, validation, and docs — instantly.
Documentation • Quick Start • Installation • What It Generates • Supported Stacks • Contract Syntax • CLI Reference • Why Veld
Veld is a contract-first code generator for APIs. You describe your data models and endpoints in .veld files, and Veld generates:
- Backend service interfaces with typed method signatures
- Route handlers with input validation, error handling, and correct HTTP status codes
-
Frontend SDKs with typed API clients using native
fetch - Validation schemas (Zod, Pydantic, or zero-dependency runtime validators)
- OpenAPI 3.0 specs, GraphQL schemas, and API documentation
- Database schemas (Prisma and SQL DDL)
- Dockerfiles and CI/CD pipelines (GitHub Actions, GitLab CI)
No runtime dependencies in generated code. No framework lock-in. Works with any compatible library.
# npm
npm install @maayn/veld
# pip
pip install maayn-veld
# Homebrew
brew install maayn-veld/tap/maayn-veld
# Go
go install github.com/Adhamzineldin/Veld/cmd/veld@latestSee the full list of supported packages in the Installation section.
mkdir my-api && cd my-api
veld initThe interactive wizard lets you pick your backend and frontend stack:
Veld — project setup
Backend — which server runtime?
1 csharp
2 go
3 java
4 node-js
5 node-ts (default)
6 php
7 python
8 rust
Choose [5]: █
This creates:
my-api/
├── veld/
│ ├── veld.config.json ← configuration
│ ├── app.veld ← entry point
│ ├── models/ ← data types
│ │ ├── user.veld
│ │ ├── auth.veld
│ │ └── common.veld
│ └── modules/ ← API endpoints
│ ├── users.veld
│ └── auth.veld
└── generated/ ← created on first generate
veld generateThat's it. Your typed backend interfaces, route handlers, frontend SDK, and validation are ready.
veld watchAuto-regenerates on every file save with 500ms debounce.
// models/user.veld
model User {
description: "A registered user"
id: uuid
email: string
name: string
role: Role @default(user)
bio?: string // optional
tags: string[] // array
settings: Map<string, string> // record/map
}
model AdminUser extends User {
permissions: string[]
}
enum Role { admin user guest }
// modules/users.veld
import @models/user
module Users {
description: "User management"
prefix: /api/v1
action ListUsers {
method: GET
path: /users
output: User[]
query: PaginationQuery
}
action GetUser {
method: GET
path: /users/:id
output: User
errors: [NotFound, Forbidden]
}
action CreateUser {
method: POST
path: /users
input: CreateUserInput
output: User
errors: [Conflict, ValidationFailed]
middleware: RequireAuth
}
action DeleteUser {
method: DELETE
path: /users/:id
middleware: RequireAuth
}
}
| Veld Type | Description |
|---|---|
string |
Text |
int |
Integer number |
float |
Floating point number |
bool |
Boolean |
date |
Date string (YYYY-MM-DD) |
datetime |
ISO 8601 datetime |
uuid |
UUID string |
T[] |
Array of T |
Map<string, V> |
Key-value record |
ModelName |
Reference to another model |
-
extends— model inheritance -
@default(value)— default field values -
@deprecated "reason"— deprecation on fields and actions -
@example("value")— example values for documentation and OpenAPI -
@unique— unique constraint (surfaced in DB schemas and OpenAPI) -
@index— index hint (surfaced in DB schemas) -
@relation(Model)— foreign key relation (surfaced in Prisma schema) -
errors: [NotFound, Forbidden]— typed error codes per action -
middleware: AuthGuard— middleware declarations -
method: WS— WebSocket endpoint support -
stream: MessageType— WebSocket message typing -
/* block comments */— multi-line block comments
| Name | Language | Output | Aliases |
|---|---|---|---|
node-ts |
Node.js | TypeScript (.ts) |
node |
node-js |
Node.js | JavaScript + JSDoc (.js) |
js, javascript
|
python |
Python | Python + Pydantic | |
go |
Go | Go + Chi router | |
rust |
Rust | Rust + Actix | |
java |
Java | Java + Spring | |
csharp |
C# | C# + ASP.NET | |
php |
PHP | PHP + PSR-15 |
Extra backend targets:
| Name | Description |
|---|---|
openapi |
Export OpenAPI 3.0 spec |
database |
Generate Prisma/SQL schemas |
dockerfile |
Generate Dockerfile |
cicd |
Generate CI/CD pipelines (GitHub Actions, GitLab CI) |
env |
Generate environment variable templates |
scaffold-tests |
Scaffold test files for the backend |
| Name | Language | Aliases |
|---|---|---|
typescript |
TypeScript fetch SDK | ts |
javascript |
JavaScript + JSDoc fetch SDK | js |
react |
React hooks | react-hooks |
vue |
Vue composables | |
angular |
Angular services | |
svelte |
Svelte stores | |
dart |
Dart HTTP client | flutter |
kotlin |
Kotlin HTTP client | |
swift |
Swift HTTP client | |
types-only |
Types only, no SDK | |
none |
Skip frontend generation |
generated/
├── index.ts # Barrel export
├── package.json # @veld/generated package
├── types/
│ ├── users.ts # TypeScript interfaces per module
│ └── index.ts # Barrel re-export
├── interfaces/
│ └── IUsersService.ts # Service contract — you implement this
├── routes/
│ └── users.routes.ts # Route handlers with try/catch + validation
├── errors/
│ └── users.errors.ts # Typed error classes + factory functions
├── schemas/
│ └── schemas.ts # Zod validation schemas
└── _validators.ts # Zero-dep runtime validators (opt-in)
generated/client/
├── _internal.ts # VeldApiError, HTTP helpers
├── usersApi.ts # Typed SDK methods
├── api.ts # Barrel + unified `api` object
├── types.ts # Re-exported types
├── errors.ts # Re-exported errors
└── package.json # @veld/client package
Every route handler includes:
- try/catch error handling
- Input validation (Zod, Pydantic, or zero-dep validators)
- Correct status codes: POST → 201, DELETE (no output) → 204, errors → appropriate 4xx/5xx
-
Path parameter extraction from Express-style
:idparams - Middleware wiring from contract declarations
import { api } from '@veld/client';
import { isErrorCode } from '@veld/client/errors';
// Typed — IDE autocomplete for methods, params, and return types
const users = await api.Users.listUsers();
const user = await api.Users.getUser('user-123');
try {
await api.Users.createUser({ email: 'a@b.com', name: 'Alice' });
} catch (err) {
if (isErrorCode(err, api.Users.errors.createUser.conflict)) {
console.log('User already exists');
}
}| Command | Description |
|---|---|
veld init |
Interactive project setup |
veld generate |
Generate all output |
veld generate --dry-run |
Preview without writing files |
veld generate --incremental |
Skip unchanged modules (dev only) |
veld generate --strict |
Exit non-zero on breaking changes (CI/CD) |
veld generate --validate |
Emit zero-dep runtime validators |
veld generate --setup |
Auto-configure project imports after generation |
veld watch |
Auto-regenerate on file changes |
veld validate |
Check contract for structural errors |
veld lint |
Analyse contract quality (unused models, missing descriptions, etc.) |
veld diff |
Detect breaking changes between versions |
veld openapi |
Export OpenAPI 3.0 spec |
veld openapi -o spec.json |
Write OpenAPI spec to file |
veld graphql |
Export GraphQL SDL schema |
veld schema |
Generate database schema (Prisma/SQL) |
veld docs |
Generate API documentation |
veld ast |
Dump AST as JSON |
veld clean |
Remove generated output |
veld setup |
Auto-configure project imports (tsconfig, package.json) |
veld lsp |
Start the Language Server Protocol server |
veld completion |
Generate shell completions (bash/zsh/fish/powershell) |
veld generate --backend=node-ts --frontend=react
veld generate --backend=python --frontend=dart
veld generate --out=./src/generated
veld generate --validate # enable runtime validators
veld generate --force # skip breaking change prompts
veld openapi -o openapi.json # write spec to file
veld lint --exit-code # fail on any lint issue (CI/CD)
veld schema --format=prisma # Prisma schema output
veld schema --format=sql # SQL DDL outputveld/veld.config.json:
{
"input": "app.veld",
"backend": "node-ts",
"frontend": "typescript",
"out": "../generated",
"baseUrl": "",
"validate": false,
"aliases": {
"models": "models",
"modules": "modules"
}
}| Field | Default | Description |
|---|---|---|
input |
required | Entry .veld file |
backend |
node-ts |
Backend emitter |
frontend |
typescript |
Frontend emitter |
out |
./generated |
Output directory |
baseUrl |
"" |
Baked into frontend SDK (empty = process.env.VELD_API_URL) |
validate |
false |
Generate zero-dep runtime validators |
aliases |
built-in | Import alias mappings |
veld generate (no flags) searches for config in order:
./veld.config.json./veld/veld.config.json
CLI flags always override config file values.
Veld supports two import styles:
import @models/user // Alias-based (recommended)
import "./models/user.veld" // Relative path (legacy)
Aliases are resolved from the project root via the aliases config. Built-in aliases include: models, modules, types, enums, schemas, services, lib, common, shared.
| Problem | Veld's Answer |
|---|---|
| Backend and frontend types drift apart | Single source of truth — one contract, all stacks |
| Writing boilerplate route handlers | Generated with validation, error handling, correct status codes |
| Frontend SDK maintenance | Auto-generated typed clients with error matching |
| Runtime shape violations | Zero-dep validators catch contract mismatches at runtime |
| OpenAPI spec goes stale | Generated from the same contract, always in sync |
| Switching frameworks | Framework-agnostic — generated code uses router: any, native fetch
|
- Zero runtime dependencies in generated output (by default)
- Framework-agnostic — works with Express, Fastify, Hono, Flask, Chi, Actix, or anything else
- Deterministic — same input always produces identical output
- Never touches business logic — Veld generates interfaces, you write implementations
-
Contract-first — the
.veldfile is the single source of truth
| Editor | How to Install | Features |
|---|---|---|
| VS Code | Search "Veld" in Extensions, or code --install-extension adhamzineldin.veld-vscode
|
Syntax highlighting, diagnostics, completions, hover, go-to-definition |
| JetBrains | Settings → Plugins → Marketplace → "Veld" | Syntax highlighting, error highlighting, completions, navigation |
| Any LSP editor | Run veld lsp as language server for .veld files |
Diagnostics, completions, hover, go-to-definition |
vim.api.nvim_create_autocmd('FileType', {
pattern = 'veld',
callback = function()
vim.lsp.start({
name = 'veld',
cmd = { 'veld', 'lsp' },
root_dir = vim.fs.dirname(
vim.fs.find({ 'veld.config.json' }, { upward = true })[1]
),
})
end,
})Veld is published to multiple package managers via automated CI/CD. Pick whichever fits your workflow:
npm install @maayn/veld
# Or run directly without installing
npx @maayn/veld generatepip install maayn-veld
# Then use the CLI
veld generatebrew install maayn-veld/tap/maayn-veld
veld generatego install github.com/Adhamzineldin/Veld/cmd/veld@latest
veld generatecomposer require maayn/veld
# The binary is available after install
vendor/bin/veld generatePre-built binaries for Linux, macOS, and Windows (amd64 & arm64) are attached to every GitHub Release. Download the archive for your platform, extract, and add veld to your PATH.
All packages and plugins are published automatically when a version tag (e.g.
v0.3.0) is pushed.
veld/ ← Go source
├── cmd/veld/ ← CLI entry point (Cobra)
├── internal/
│ ├── ast/ ← AST data structures
│ ├── lexer/ ← Tokenizer
│ ├── parser/ ← Recursive descent parser
│ ├── validator/ ← Semantic validation
│ ├── loader/ ← File loading + import resolution
│ ├── config/ ← Config file + flag merging
│ ├── emitter/ ← Code generators
│ │ ├── backend/ ← node-ts, node-js, python, go, rust, java, csharp, php
│ │ └── frontend/ ← typescript, javascript, react, vue, angular, svelte, dart, kotlin, swift
│ ├── lsp/ ← Language Server Protocol
│ └── schema/ ← Database schema generators
├── website/ ← Website + docs (React + Vite)
├── editors/ ← VS Code extension + JetBrains plugin
└── docs/ ← Additional documentation
# Build
go build -o veld ./cmd/veld
# Test
go test ./...
# Lint
go vet ./...Emitters self-register via init(). Adding a new backend or frontend emitter is one package + one blank import in cmd/veld/main.go.
MIT
