# Varavel Definition Language
> VDL is an open-source, type-safe, multi-language, and easily extensible schema definition language and code generation toolchain.
This file contains the LLM-readable Markdown content for the full site.
---
Source: https://vdl.varavel.com/
Markdown: https://vdl.varavel.com/index.md
Content file: content/_index.md
# Varavel Definition Language
> VDL is an open-source, type-safe, multi-language, and easily extensible schema definition language and code generation toolchain.
Canonical HTML: https://vdl.varavel.com/
Markdown: https://vdl.varavel.com/index.md
{{ header_base(
container="lg",
menu="Overview|/docs/essentials/about/,Language|/docs/language/start-here/,Plugins|/docs/guides/plugins/,Reference|/docs/reference/spec/",
cta_text="Install",
cta_url="/docs/guides/installation/"
) }}
{{ hero_split(
container="lg",
eyebrow="Contract-first generation",
title="One human-readable schema. Type-safe code across your stack.",
description="VDL lets teams write compact contracts that humans can review and machines can validate. From one .vdl file, generate type-safe models, RPC clients and servers, schemas, docs, and custom artifacts for multiple languages.",
action_1_text="Get started",
action_1_url="/docs/",
action_2_text="See available plugins",
action_2_url="/docs/guides/plugins/",
caption="Open source, deterministic, and designed for normal development and CI workflows.",
panel_label="How it works",
panel_title="Readable contract to type-safe outputs",
panel_desc="A VDL project starts with declarations, not framework code. The analyzer enforces the model, then plugins generate the files each application needs.",
metric_1_value=".vdl",
metric_1_label="source files",
metric_2_value="Plugin",
metric_2_label="transforms IR",
metric_3_value="Code",
metric_3_label="type-safe outputs",
check_1="Write contracts that are easy to read in code review",
check_2="Validate references, naming, spreads, literals, and required type relationships",
check_3="Generate type-safe code for frontend, backend, services, and tooling"
) }}
{{ metrics_strip(
container="lg",
label="What VDL gives you",
stat_1_value="Readable",
stat_1_label="contracts for humans",
stat_2_value="Type-safe",
stat_2_label="generated code",
stat_3_value="Multi-lang",
stat_3_label="plugin outputs",
stat_4_value="CI-ready",
stat_4_label="format and checks"
) }}
{{ features_grid(
container="lg",
eyebrow="The core idea",
title="Keep the contract simple. Move the output logic into plugins.",
description="VDL separates what your systems agree on from how each toolchain consumes it. The source stays readable, while generated artifacts stay precise and type-safe.",
columns="3",
item_1_icon="braces",
item_1_title="Describe the shared model",
item_1_desc="Use type, enum, const, include, docstrings, arrays, maps, inline objects, references, and spreads to describe the contract itself in a review-friendly format.",
item_2_icon="workflow",
item_2_title="Add domain intent",
item_2_desc="Use annotations such as @rpc, @event, @deprecated, or your own team-specific tags without changing the language grammar.",
item_3_icon="plug",
item_3_title="Generate type-safe outputs",
item_3_desc="Official and custom easy to write plugins receive the resolved IR and generate language-specific code, schemas, docs, or internal artifacts."
) }}
{{ steps_cards(
container="lg",
eyebrow="Workflow",
title="A straightforward path from schema to artifacts",
description="The day-to-day loop is intentionally small: write the contract, configure plugins, and generate checked output.",
columns="3",
item_1_icon="terminal",
item_1_title="Install the CLI",
item_1_desc="Install VDL with the shell installer, Homebrew, PowerShell, npm, Docker, or release binaries.",
item_1_href="/docs/guides/installation/",
item_1_link="Install VDL",
item_2_icon="braces",
item_2_title="Model the contract",
item_2_desc="Create readable .vdl files for shared types, HTTP RPC operations, event payloads, documentation, and metadata.",
item_2_href="/docs/language/start-here/",
item_2_link="Learn the language",
item_3_icon="zap",
item_3_title="Run generation",
item_3_desc="Point vdl.config.vdl at your schema and plugins, then run vdl generate locally or in CI.",
item_3_href="/docs/guides/configuration/",
item_3_link="Configure a project"
) }}
{{ features_grid(
container="lg",
bg="base-200",
eyebrow="Use cases",
title="Use VDL anywhere systems need to agree on data",
description="VDL is most useful when several parts of a product need the same contract, but each part needs it in its own language or format.",
columns="3",
item_1_icon="box",
item_1_title="Share types across apps",
item_1_desc="Generate type-safe models for frontend, backend, workers, and microservices from the same source contract.",
item_2_icon="code-xml",
item_2_title="Build type-safe HTTP RPC",
item_2_desc="Model request and response shapes once, then generate typed clients, servers, and OpenAPI documents for frontend-to-backend communication.",
item_3_icon="workflow",
item_3_title="Coordinate microservices",
item_3_desc="Keep service boundaries explicit with shared schemas, generated packages, and stable contracts that can be checked in CI.",
item_4_icon="book-open",
item_4_title="Document and validate data",
item_4_desc="Generate JSON Schema, schema explorers, metadata exports, and documentation from docstrings and resolved types.",
item_5_icon="plug",
item_5_title="Standardize event contracts",
item_5_desc="Use @event declarations to generate subject builders and catalogs for event-driven systems.",
item_6_icon="terminal",
item_6_title="Private team artifacts",
item_6_desc="Write custom plugins for your framework conventions, templates, catalogs, policies, or platform-specific glue code."
) }}
{{ resource_cards(
container="lg",
eyebrow="Read next",
title="Choose the next step that matches your goal",
description="The documentation starts with concepts, then moves into installation, configuration, plugin usage, and reference details.",
item_1_type="Overview",
item_1_title="About VDL",
item_1_desc="Understand the project philosophy, the plugin-first model, and common use cases.",
item_1_href="/docs/essentials/about/",
item_1_meta="Read overview",
item_2_type="Language",
item_2_title="Start Here",
item_2_desc="Learn the VDL syntax in order, from files and comments to naming and validation.",
item_2_href="/docs/language/start-here/",
item_2_meta="Learn syntax",
item_3_type="Project setup",
item_3_title="Project Configuration",
item_3_desc="Configure vdl.config.vdl, plugin sources, output directories, remotes, hooks, and lock files.",
item_3_href="/docs/guides/configuration/",
item_3_meta="Configure generation",
item_4_type="Extension",
item_4_title="Creating Plugins",
item_4_desc="Build custom JavaScript or TypeScript plugins that consume the resolved VDL IR.",
item_4_href="/docs/guides/creating-plugins/",
item_4_meta="Write a plugin"
) }}
{{ cta_banner(
container="lg",
eyebrow="Ready to try it",
title="Install VDL, write one schema, and generate your first outputs.",
description="Start small: define a type, add a plugin, and run the generator. The same workflow scales to shared models, RPC services, event contracts, documentation, and private platform tooling.",
action_1_text="Install VDL",
action_1_url="/docs/guides/installation/",
action_2_text="Open the language guide",
action_2_url="/docs/language/start-here/",
caption="Generated files are regular files: inspect them, test them, commit them, or regenerate them in CI."
) }}
{{ footer_simple(
container="lg",
links="Docs|/docs/,GitHub|https://github.com/varavelio/vdl,Varavel|https://varavel.com",
show_github="false"
) }}
---
Source: https://vdl.varavel.com/docs/
Markdown: https://vdl.varavel.com/docs/index.md
Content file: content/docs/_index.md
# Documentation
> Documentation for the VDL language, CLI, plugin system, and reference behavior.
Canonical HTML: https://vdl.varavel.com/docs/
Markdown: https://vdl.varavel.com/docs/index.md
---
Source: https://vdl.varavel.com/docs/essentials/
Markdown: https://vdl.varavel.com/docs/essentials/index.md
Content file: content/docs/essentials/_index.md
# Essentials
> Start here to understand what VDL is and where it fits.
Canonical HTML: https://vdl.varavel.com/docs/essentials/
Markdown: https://vdl.varavel.com/docs/essentials/index.md
Start here if you want the short version of what VDL is, why it exists, and how it fits into contract-driven development.
---
Source: https://vdl.varavel.com/docs/essentials/about/
Markdown: https://vdl.varavel.com/docs/essentials/about/index.md
Content file: content/docs/essentials/about.md
# About VDL
> Learn what VDL is, what it provides, and why the core language stays small.
Canonical HTML: https://vdl.varavel.com/docs/essentials/about/
Markdown: https://vdl.varavel.com/docs/essentials/about/index.md
## A Definition Language For Data Contracts
VDL is a schema-first language and toolchain for defining structured data contracts once and generating useful artifacts from them.
The core language models data, documentation, constants, enums, annotations, and composition. API, RPC, event, documentation, schema-generation, and custom internal workflows are built on top through plugins.
## What VDL Provides
VDL is built around three parts.
1. **A compact definition language:** `.vdl` files describe typed data with `type`, `enum`, `const`, `include`, annotations, arrays, maps, inline objects, literals, documentation blocks, and spreads.
2. **Developer tooling:** the CLI formats schemas, compiles them to JSON IR, runs plugin generation, and exposes an LSP for editor diagnostics, completion, hover, definitions, references, rename, document symbols, formatting, and links.
3. **A plugin-first generator runtime:** plugins receive the resolved VDL IR and generate the target artifacts your project needs.
## Why The Core Is Small
The language keeps domain semantics out of the grammar. Instead, annotations describe intent and plugins decide what to do with that intent.
For example:
- `@rpc` can mark a type as an RPC service.
- `@proc` and `@stream` can mark RPC operations.
- `@event("subject.{id}")` can mark event payload contracts.
- `@deprecated` can be interpreted by a JSON Schema plugin.
- Your own annotations can drive your own internal generators.
This lets VDL support many domains without turning the parser into a collection of framework-specific features.
## Common Use Cases
- Generate shared Go and TypeScript data models.
- Export JSON Schema for validation, forms, docs, or external integrations.
- Generate RPC clients, servers, and OpenAPI documents from annotation-based service contracts.
- Generate event subject builders and event catalogs.
- Publish static schema explorers for internal or external documentation.
- Export VDL IR as JSON for runtime metadata and custom tooling.
- Build private plugins that encode your team's framework conventions.
## Project Philosophy
- **Schema as source of truth:** contracts should be defined once and reused everywhere.
- **Small language, extensible semantics:** core syntax stays stable while plugins evolve domain behavior.
- **Readable contracts:** schemas should be understandable during code review without generator knowledge.
- **Strong tooling:** diagnostics, formatting, LSP features, and deterministic IR are core to the workflow.
- **Practical generation:** generated files are regular text files that can be inspected, tested, committed, and integrated into existing stacks.
---
Source: https://vdl.varavel.com/docs/language/
Markdown: https://vdl.varavel.com/docs/language/index.md
Content file: content/docs/language/_index.md
# Language Guide
> Learn how to write VDL schemas from first principles.
Canonical HTML: https://vdl.varavel.com/docs/language/
Markdown: https://vdl.varavel.com/docs/language/index.md
This guide teaches the VDL language step by step. Read the pages in order if you are new to VDL, or jump directly to the topic you need.
---
Source: https://vdl.varavel.com/docs/language/start-here/
Markdown: https://vdl.varavel.com/docs/language/start-here/index.md
Content file: content/docs/language/start-here.md
# Start Here
> Learn how to write VDL schemas from first principles.
Canonical HTML: https://vdl.varavel.com/docs/language/start-here/
Markdown: https://vdl.varavel.com/docs/language/start-here/index.md
## What You Will Learn
This guide teaches the VDL language step by step. It is written for readers who are new to VDL and want to understand how to write schemas confidently.
VDL is small on purpose. A schema is made from a few building blocks:
- files and comments
- `include` statements
- docstrings
- `type` declarations
- field types
- `enum` declarations
- `const` declarations
- annotations
- spreads and references
Once you understand these pieces, you can model data contracts, RPC services, events, configuration files, JSON schemas, generated code packages, and custom internal contracts.
## A Tiny Schema
```vdl
"""
Represents a user account.
"""
type User {
id string
email string
displayName? string
createdAt datetime
}
enum UserStatus {
Active = "active"
Suspended = "suspended"
}
const defaultPageSize = 25
```
This file defines three things:
- `User`, an object type with fields
- `UserStatus`, a finite set of allowed values
- `defaultPageSize`, a reusable constant value
## How To Read VDL Syntax
VDL intentionally avoids punctuation that is not needed.
Fields do not use colons:
```vdl
type Product {
id string
price float
}
```
Object literal entries do not use colons or commas:
```vdl
const serverConfig = {
host "localhost"
port 8080
tls false
}
```
Array literal items are separated by whitespace:
```vdl
const roles = ["admin" "editor" "viewer"]
```
If you come from JSON, TypeScript, Go, or YAML, this is the main habit to learn: VDL is whitespace-friendly and declaration-oriented.
## Top-Level Declarations
At the top level, a `.vdl` file can contain:
```vdl
include "./shared.vdl"
""" Standalone documentation. """
type User {
id string
}
enum Status {
Active
}
const version = "1.0.0"
```
Only `type`, `enum`, and `const` create named symbols. Includes and standalone docstrings help compose and document the schema.
## Recommended Learning Path
Read these pages in order:
1. [Files and Comments](/docs/language/files/)
2. [Includes](/docs/language/includes/)
3. [Docstrings](/docs/language/docstrings/)
4. [Types](/docs/language/types/)
5. [Field Types](/docs/language/field-types/)
6. [Enums](/docs/language/enums/)
7. [Constants and Literals](/docs/language/constants/)
8. [Annotations](/docs/language/annotations/)
9. [Spreads and References](/docs/language/spreads-references/)
10. [Naming and Validation](/docs/language/naming-validation/)
For exact grammar-level detail, see the [VDL specification](/docs/reference/spec/).
---
Source: https://vdl.varavel.com/docs/language/files/
Markdown: https://vdl.varavel.com/docs/language/files/index.md
Content file: content/docs/language/files.md
# Files and Comments
> How VDL files are structured and how comments work.
Canonical HTML: https://vdl.varavel.com/docs/language/files/
Markdown: https://vdl.varavel.com/docs/language/files/index.md
## File Extension
VDL schemas are written in files ending with `.vdl`.
```text
schema.vdl
auth.vdl
events.vdl
```
Regular schema files should use lowercase names with numbers or underscores only:
```text
user.vdl
order_events.vdl
api_v1.vdl
```
The special file name `vdl.config.vdl` is reserved for project generation configuration.
## File Contents
A VDL file can contain:
- `include` statements
- standalone docstrings
- `type` declarations
- `enum` declarations
- `const` declarations
Example:
```vdl
include "./shared.vdl"
""" Documentation for this schema. """
type User {
id string
}
enum UserStatus {
Active
Suspended
}
const apiVersion = "1.0.0"
```
## Single-Line Comments
Use `//` for a comment that runs to the end of the line.
```vdl
// User-facing account data.
type Account {
id string // Stable account identifier.
email string
}
```
Comments are ignored by the compiler. They are useful for readers, but they do not appear in the generated IR as documentation.
Use docstrings when you want documentation to be part of the schema model.
## Block Comments
Use `/* ... */` for multi-line comments.
```vdl
/*
This schema is shared by the billing and support systems.
Keep field names stable because generated clients depend on them.
*/
type Customer {
id string
email string
}
```
Block comments can appear anywhere whitespace can appear.
## Comments vs Docstrings
Use comments for notes to humans reading the source file:
```vdl
// Internal note: this name must match the external system.
type ExternalAccount {
id string
}
```
Use docstrings for documentation that plugins should see:
```vdl
"""
Public account information returned to clients.
"""
type PublicAccount {
id string
displayName string
}
```
Plugins receive docstrings through the VDL IR. Comments are discarded.
## Whitespace
VDL is flexible with whitespace. These are equivalent:
```vdl
type User { id string email string }
```
```vdl
type User {
id string
email string
}
```
Prefer the second style. It is easier to read and matches the formatter.
---
Source: https://vdl.varavel.com/docs/language/includes/
Markdown: https://vdl.varavel.com/docs/language/includes/index.md
Content file: content/docs/language/includes.md
# Includes
> Split VDL schemas across files with include statements.
Canonical HTML: https://vdl.varavel.com/docs/language/includes/
Markdown: https://vdl.varavel.com/docs/language/includes/index.md
## Why Includes Exist
As schemas grow, it is useful to split them into focused files.
For example:
```text
schema.vdl
shared.vdl
events.vdl
```
Use `include` to bring definitions from another `.vdl` file into the same compilation context.
## Basic Syntax
```vdl
include "./shared.vdl"
type UserSession {
token string
account Account
}
```
The included file can define types, enums, constants, or documentation used by the current file.
Example `shared.vdl`:
```vdl
type Account {
id string
email string
}
```
Example `schema.vdl`:
```vdl
include "./shared.vdl"
type Session {
account Account
token string
}
```
`Session.account` can use `Account` because `schema.vdl` includes `shared.vdl`.
## Relative Paths
Include paths are resolved relative to the file that contains the `include` statement.
```vdl
include "../shared/types.vdl"
include "./events/user_events.vdl"
```
This makes each file self-contained: moving the entry point does not change how nested includes are resolved.
## Include Graphs
Includes are recursive. If `schema.vdl` includes `orders.vdl`, and `orders.vdl` includes `money.vdl`, all three files are analyzed together.
```vdl
include "./orders.vdl"
type Checkout {
order Order
}
```
Each file is processed once. Circular includes are reported as diagnostics.
## File Name Rules
Regular included schema files should have names matching this pattern:
```text
[a-z0-9_]+.vdl
```
Good names:
```text
shared.vdl
order_events.vdl
api_v1.vdl
```
Avoid names like:
```text
Shared.vdl
order-events.vdl
schema.backup.vdl
```
## Config Files Are Not Included
`vdl.config.vdl` is the project generation configuration file. It cannot be included as a normal schema file.
Keep language schemas and generation configuration separate:
```text
schema.vdl # Your contracts
vdl.config.vdl # Plugin generation configuration
```
## Practical Advice
- Put shared primitives, aliases, and base objects in files like `shared.vdl`.
- Put event contracts in files like `events.vdl` or `user_events.vdl`.
- Keep the main entry file small and readable.
- Prefer shallow include graphs when possible.
---
Source: https://vdl.varavel.com/docs/language/docstrings/
Markdown: https://vdl.varavel.com/docs/language/docstrings/index.md
Content file: content/docs/language/docstrings.md
# Docstrings
> Document VDL schemas with inline and external Markdown docs.
Canonical HTML: https://vdl.varavel.com/docs/language/docstrings/
Markdown: https://vdl.varavel.com/docs/language/docstrings/index.md
## What Docstrings Are
Docstrings are triple-quoted documentation blocks.
```vdl
"""
Represents a user account in the product.
"""
type User {
id string
}
```
Unlike comments, docstrings are part of the schema model. Plugins can use them to generate docs, comments, JSON Schema descriptions, explorers, and metadata.
## Declaration Docstrings
Place a docstring immediately before a `type`, `enum`, or `const` to attach it to that declaration.
```vdl
""" A product that can be listed in the catalog. """
type Product {
id string
name string
}
""" Lifecycle state of a product. """
enum ProductStatus {
Draft
Published
}
""" Current public API version. """
const apiVersion = "1.0.0"
```
## Field Docstrings
Place a docstring immediately before a field to document that field.
```vdl
type User {
""" Stable unique identifier. """
id string
""" Email address used for account notifications. """
email string
}
```
Field docstrings also work inside inline objects.
```vdl
type Checkout {
payment {
""" Amount charged in cents. """
amountCents int
}
}
```
## Enum Member Docstrings
Enum members can have docstrings too.
```vdl
enum InvoiceStatus {
""" The invoice has been created but not sent. """
Draft
""" The invoice has been paid in full. """
Paid
}
```
The docstring attaches to the named member that follows it.
## Standalone Docstrings
A docstring at the top level can stand alone as schema documentation.
```vdl
"""
# Billing Schema
This schema contains shared billing contracts used by services and jobs.
"""
type Invoice {
id string
}
```
A blank line separates a standalone docstring from the next declaration.
## Organizing Field Docstrings
Inside object type bodies, a docstring attaches to the field that follows it. This can also be used to make groups of fields easier to scan, but remember that the docstring belongs to the next field.
```vdl
type Product {
"""
Identifiers
"""
id string
sku string
"""
Public catalog data
"""
name string
description? string
}
```
In this example, `""" Identifiers """` documents `id`, and `""" Public catalog data """` documents `name`.
Use grouped wording sparingly. Most fields should be documented directly with field-specific descriptions.
## External Markdown Files
If a docstring contains only a relative `.md` path, VDL treats it as a reference to an external Markdown file.
```vdl
""" ./docs/product.md """
type Product {
id string
name string
}
```
The path is resolved relative to the `.vdl` file that contains the docstring.
Standalone external docs are also supported:
```vdl
""" ./docs/overview.md """
type Account {
id string
}
```
## Good Docstring Style
- Explain what a contract means, not just its name.
- Mention units when fields are numeric.
- Mention formats when strings carry structured values.
- Keep generated-code readers in mind.
Good:
```vdl
type Payment {
""" Amount charged in the smallest currency unit, such as cents. """
amountMinor int
}
```
Less useful:
```vdl
type Payment {
""" Amount. """
amountMinor int
}
```
---
Source: https://vdl.varavel.com/docs/language/types/
Markdown: https://vdl.varavel.com/docs/language/types/index.md
Content file: content/docs/language/types.md
# Types
> Define object types, aliases, arrays, maps, and inline objects.
Canonical HTML: https://vdl.varavel.com/docs/language/types/
Markdown: https://vdl.varavel.com/docs/language/types/index.md
## What Types Are
`type` declarations define reusable data shapes.
The most common form is an object type:
```vdl
type User {
id string
email string
}
```
But a type can also be an alias for another type expression:
```vdl
type UserId string
type Tags string[]
type Metadata map[string]
```
## Object Types
Object types use braces and contain fields.
```vdl
type Product {
id string
name string
price float
active bool
}
```
Each field is written as:
```text
fieldName TypeName
```
There are no colons and no commas.
## Optional Fields
Add `?` after the field name to make a field optional.
```vdl
type UserProfile {
userId string
displayName? string
avatarUrl? string
}
```
Optional means the field may be absent in generated representations, depending on the target plugin.
## Type Aliases
A type can name any valid field type.
```vdl
type UserId string
type UserIds string[]
type UserMetadata map[string]
```
Aliases are useful when you want semantic names without repeating raw primitives everywhere.
```vdl
type EmailAddress string
type User {
id string
email EmailAddress
}
```
## Object Types Can Reference Other Types
```vdl
type Address {
line1 string
line2? string
city string
country string
}
type Customer {
id string
billingAddress Address
shippingAddress? Address
}
```
References must point to declared types or enums.
## Inline Objects
Use an inline object when a nested shape is only useful in one place.
```vdl
type CheckoutSession {
id string
payment {
provider string
amountCents int
currency string
}
}
```
Inline objects can contain fields, nested inline objects, and spreads.
## Empty Objects
An object type can be empty, but it is rarely useful.
```vdl
type EmptyObject {
}
```
Prefer adding fields when the contract has meaningful data.
## Field Names Must Be Unique
Within an object or inline object, each field name must be unique.
Good:
```vdl
type Product {
id string
sku string
}
```
Invalid:
```vdl
type Product {
id string
id int
}
```
## Naming
Type names should use `PascalCase`:
```vdl
type UserProfile {
displayName string
}
```
Field names should use `camelCase`:
```vdl
type UserProfile {
displayName string
createdAt datetime
}
```
The analyzer reports diagnostics when names do not follow these conventions.
---
Source: https://vdl.varavel.com/docs/language/field-types/
Markdown: https://vdl.varavel.com/docs/language/field-types/index.md
Content file: content/docs/language/field-types.md
# Field Types
> Learn primitives, arrays, maps, custom types, enums, and inline object field types.
Canonical HTML: https://vdl.varavel.com/docs/language/field-types/
Markdown: https://vdl.varavel.com/docs/language/field-types/index.md
## Field Type Overview
Every field has a type.
```vdl
type Example {
name string
count int
}
```
VDL supports:
- primitive types
- custom types
- enums
- arrays
- maps
- inline objects
## Primitive Types
| VDL type | Meaning |
| ---------- | ------------------------------- |
| `string` | Text |
| `int` | 64-bit signed integer |
| `float` | 64-bit floating point number |
| `bool` | `true` or `false` |
| `datetime` | ISO 8601 date-time string value |
Example:
```vdl
type PrimitiveExample {
name string
age int
score float
active bool
createdAt datetime
}
```
## Custom Types
Fields can reference other declared types.
```vdl
type Address {
city string
country string
}
type User {
id string
address Address
}
```
## Enum Fields
Fields can reference enums.
```vdl
enum OrderStatus {
Pending
Paid
Shipped
}
type Order {
id string
status OrderStatus
}
```
## Arrays
Arrays use `[]` after the element type.
```vdl
type Collection {
tags string[]
scores int[]
orders Order[]
}
type Order {
id string
}
```
Multidimensional arrays repeat the suffix:
```vdl
type MatrixData {
values float[][]
}
```
## Maps
Maps use `map[T]`, where `T` is the value type.
```vdl
type Metrics {
counters map[int]
labels map[string]
}
```
Maps can contain custom types:
```vdl
type User {
id string
email string
}
type UserDirectory {
usersById map[User]
}
```
Maps can also contain arrays or nested maps:
```vdl
type ComplexMapExample {
tagsByGroup map[string[]]
matrixByName map[float[][]]
}
```
## Inline Objects
Inline objects are anonymous structured field types.
```vdl
type SearchResult {
id string
metadata {
score float
source string
}
}
```
Use inline objects when the nested shape does not need a reusable name.
Use a named type when the shape is reused or important enough to document on its own.
## Optional Field Types
Optionality belongs to the field name, not the type.
```vdl
type User {
displayName? string
tags? string[]
profile? {
bio? string
}
}
```
The `?` always appears after the field name.
## Recursive Types
VDL can model recursive structures when a cycle is broken by an optional field.
```vdl
type TreeNode {
id string
children? TreeNode[]
}
```
Direct required cycles are rejected because they cannot be represented as finite data.
Invalid:
```vdl
type A {
b B
}
type B {
a A
}
```
Make one side optional to model a nullable or absent link.
---
Source: https://vdl.varavel.com/docs/language/enums/
Markdown: https://vdl.varavel.com/docs/language/enums/index.md
Content file: content/docs/language/enums.md
# Enums
> Define finite sets of named values.
Canonical HTML: https://vdl.varavel.com/docs/language/enums/
Markdown: https://vdl.varavel.com/docs/language/enums/index.md
## What Enums Are
Enums define a named set of possible values.
```vdl
enum OrderStatus {
Pending
Paid
Shipped
}
```
Use enums when a field should accept only one value from a known list.
```vdl
type Order {
id string
status OrderStatus
}
```
## Implicit String Values
When a member has no explicit value, its value is the member name.
```vdl
enum Status {
Active
Disabled
}
```
This is equivalent to:
```vdl
enum Status {
Active = "Active"
Disabled = "Disabled"
}
```
## Explicit String Values
Use explicit strings when the wire value should differ from the member name.
```vdl
enum ProductStatus {
Draft = "draft"
Published = "published"
Archived = "archived"
}
```
This is common when generated code should use nice member names while serialized data uses lowercase values.
## Integer Enums
Enums can use integer values.
```vdl
enum Priority {
Low = 1
Medium = 2
High = 3
}
```
For integer enums, every member must have an explicit integer value.
Invalid:
```vdl
enum Priority {
Low = 1
Medium
High = 3
}
```
## Do Not Mix Value Kinds
An enum must be consistently string-based or integer-based.
Invalid:
```vdl
enum MixedStatus {
Active = "active"
Disabled = 2
}
```
## Unique Names And Values
Member names must be unique.
Invalid:
```vdl
enum Status {
Active
Active = "active"
}
```
Member values must also be unique.
Invalid:
```vdl
enum Status {
Active = "enabled"
Enabled = "enabled"
}
```
## Enum Member Docstrings
```vdl
enum InvoiceStatus {
""" The invoice is editable and has not been sent. """
Draft
""" The invoice has been paid in full. """
Paid
}
```
## Enum Member Annotations
```vdl
enum FeatureState {
@deprecated("Use Enabled instead.")
Active = "active"
Enabled = "enabled"
}
```
Annotations on enum members are available to plugins.
## Enum Spreads
Enums can reuse members from another enum with `...Name`.
```vdl
enum StandardRole {
Viewer = "viewer"
Editor = "editor"
}
enum WorkspaceRole {
...StandardRole
Owner = "owner"
}
```
Enum spreads must reference another enum and must use the `Name` form, not `Name.Member`.
## Naming
Enum names and member names should use `PascalCase`.
```vdl
enum PaymentStatus {
Pending
Completed
Failed
}
```
---
Source: https://vdl.varavel.com/docs/language/constants/
Markdown: https://vdl.varavel.com/docs/language/constants/index.md
Content file: content/docs/language/constants.md
# Constants and Literals
> Define reusable dynamic literal values with const declarations.
Canonical HTML: https://vdl.varavel.com/docs/language/constants/
Markdown: https://vdl.varavel.com/docs/language/constants/index.md
## What Constants Are
Constants define reusable literal values.
```vdl
const apiVersion = "1.0.0"
const maxRetries = 3
const featureEnabled = true
```
Constants do not have explicit type annotations. VDL infers their value kind from the literal.
Correct:
```vdl
const timeoutMs = 2500
```
Invalid:
```text
const timeoutMs int = 2500
```
## Scalar Literals
VDL supports these scalar literal forms:
```vdl
const textValue = "hello"
const intValue = 42
const floatValue = 3.14
const trueValue = true
const falseValue = false
```
Strings use double quotes.
## Object Literals
Object literals use braces and space-separated key/value entries.
```vdl
const serverConfig = {
host "localhost"
port 8080
tls false
}
```
There are no colons and no commas.
Object keys must be unique inside the same object.
## Nested Object Literals
```vdl
const appConfig = {
server {
host "localhost"
port 8080
}
logging {
level "info"
}
}
```
## Array Literals
Array items are separated by whitespace.
```vdl
const roles = ["admin" "editor" "viewer"]
const retryBackoffMs = [100 250 500]
```
All items in an array literal must have the same value kind.
Invalid:
```vdl
const mixedValues = ["admin" 1 true]
```
## Constant References
Constants can reference other constants.
```vdl
const defaultTimeoutMs = 5000
const requestTimeoutMs = defaultTimeoutMs
```
References use the constant name directly.
## Enum Member References
Constants can reference enum members with `EnumName.MemberName`.
```vdl
enum ProductStatus {
Draft = "draft"
Published = "published"
}
const defaultStatus = ProductStatus.Draft
```
The referenced enum and member must exist.
## Object Spreads
Object literals can spread another constant object.
```vdl
const baseConfig = {
host "localhost"
port 8080
}
const productionConfig = {
...baseConfig
host "api.example.com"
}
```
Only constants whose value is an object literal can be used in object spreads.
Spreads use the `Name` form. `Name.Member` is not valid for object spreads.
## Constants In Annotations
Because annotation arguments are data literals, constants can also help share annotation values.
```vdl
const owner = "platform"
@meta({ team owner })
type ServiceConfig {
name string
}
```
Whether a plugin understands a particular annotation is plugin-specific.
## Naming
Constant names should use `camelCase`.
```vdl
const apiVersion = "1.0.0"
const maxPageSize = 100
```
---
Source: https://vdl.varavel.com/docs/language/annotations/
Markdown: https://vdl.varavel.com/docs/language/annotations/index.md
Content file: content/docs/language/annotations.md
# Annotations
> Attach metadata to VDL declarations, fields, and enum members.
Canonical HTML: https://vdl.varavel.com/docs/language/annotations/
Markdown: https://vdl.varavel.com/docs/language/annotations/index.md
## What Annotations Are
Annotations attach metadata to schema nodes.
```vdl
@deprecated
type OldUser {
id string
}
```
The VDL core does not hardcode most domain behavior. Plugins read annotations and decide what they mean.
For example, one plugin may interpret `@event`, another may interpret `@rpc`, and another may interpret `@deprecated`.
## Basic Syntax
An annotation starts with `@` and a name.
```vdl
@internal
type InternalUser {
id string
}
```
Annotations can also have one argument.
```vdl
@deprecated("Use NewUser instead.")
type OldUser {
id string
}
```
The argument must be a data literal: string, number, boolean, object, array, constant reference, or enum member reference.
## Multiple Annotations
You can stack annotations.
```vdl
@internal
@owner("platform")
type InternalConfig {
name string
}
```
Annotations attach to the declaration or member that immediately follows them.
## Declaration Annotations
Annotations can be attached to `type`, `enum`, and `const` declarations.
```vdl
@event("users.created.{userId}")
type UserCreated {
userId string
email string
}
@stable
enum Region {
Europe = "eu"
America = "us"
}
@config
const serviceName = "billing"
```
## Field Annotations
Fields can have annotations.
```vdl
type User {
@id
id string
@deprecated("Use displayName instead.")
name? string
displayName string
}
```
## Enum Member Annotations
Enum members can have annotations.
```vdl
enum Plan {
Free = "free"
@deprecated("Use Team instead.")
Startup = "startup"
Team = "team"
}
```
## Object Arguments
Object arguments are useful for structured metadata.
```vdl
@meta({ owner "platform" tier "gold" })
type Account {
id string
}
```
Object entries use VDL literal syntax: key followed by value, no colon, no comma.
## Array Arguments
```vdl
@tags(["public" "billing" "stable"])
type Invoice {
id string
}
```
Array literal items must be the same kind.
## Annotation Names
Annotation names should use `camelCase`.
```vdl
@generateClient
type PublicApi {
name string
}
```
Avoid underscores or PascalCase annotation names.
## Annotations Are Plugin Contracts
An annotation has no effect unless some tool or plugin understands it.
This is a feature. It lets teams create domain-specific contracts without changing the VDL grammar.
Examples:
- `@rpc` can mark a type as an RPC service.
- `@proc` can mark a request/response RPC operation.
- `@stream` can mark a server-sent stream operation.
- `@event("subject.{field}")` can mark an event payload and routing subject.
- `@deprecated` can mark generated code or schema output as deprecated.
Always check the plugin documentation for the annotations it supports.
---
Source: https://vdl.varavel.com/docs/language/spreads-references/
Markdown: https://vdl.varavel.com/docs/language/spreads-references/index.md
Content file: content/docs/language/spreads-references.md
# Spreads and References
> Reuse fields, enum members, and literal values safely.
Canonical HTML: https://vdl.varavel.com/docs/language/spreads-references/
Markdown: https://vdl.varavel.com/docs/language/spreads-references/index.md
## References
A reference points to a named VDL symbol.
References appear in several places:
- field types
- spreads
- constant values
- annotation arguments
## Type References
Fields can reference declared types.
```vdl
type UserId string
type User {
id UserId
}
```
Fields can also reference enums.
```vdl
enum Status {
Active
Disabled
}
type User {
status Status
}
```
## Constant References
Constants can reference other constants by name.
```vdl
const defaultLimit = 100
const pageSize = defaultLimit
```
## Enum Member References
Use `EnumName.MemberName` to reference an enum member in literal values.
```vdl
enum Status {
Active = "active"
Disabled = "disabled"
}
const defaultStatus = Status.Active
```
## Type Spreads
Type spreads copy fields from another object type.
```vdl
type AuditFields {
createdAt datetime
updatedAt datetime
}
type Article {
...AuditFields
id string
title string
}
```
After analysis, plugins see `Article` as if it had all fields directly.
Only object types can be spread into object types.
Invalid:
```vdl
type UserId string
type User {
...UserId
name string
}
```
## Inline Object Spreads
Inline objects can also spread object types.
```vdl
type Money {
amount int
currency string
}
type Order {
id string
payment {
...Money
provider string
}
}
```
## Field Conflicts
Spreads cannot introduce a field that already exists in the same object.
Invalid:
```vdl
type BaseUser {
id string
}
type User {
...BaseUser
id string
}
```
Rename one field or avoid the spread.
## Enum Spreads
Enum spreads copy members from another enum.
```vdl
enum StandardRole {
Viewer = "viewer"
Editor = "editor"
}
enum ProjectRole {
...StandardRole
Owner = "owner"
}
```
Enum spreads must reference enums, not enum members.
Invalid:
```vdl
enum Role {
Viewer = "viewer"
}
enum ProjectRole {
...Role.Viewer
Owner = "owner"
}
```
## Object Literal Spreads
Object literals can spread constant objects.
```vdl
const baseConfig = {
retries 3
timeoutMs 5000
}
const productionConfig = {
...baseConfig
timeoutMs 10000
}
```
Only object constants can be used this way.
## Spread Cycles
Spreads cannot form cycles.
Invalid:
```vdl
type A {
...B
a string
}
type B {
...A
b string
}
```
Keep spread hierarchies simple and acyclic.
## Name Forms
VDL supports two reference forms:
| Form | Used for |
| ------------- | --------------------------------------------- |
| `Name` | Types, constants, spreads |
| `Name.Member` | Enum member references in literal expressions |
Spreads must use `Name`, not `Name.Member`.
---
Source: https://vdl.varavel.com/docs/language/naming-validation/
Markdown: https://vdl.varavel.com/docs/language/naming-validation/index.md
Content file: content/docs/language/naming-validation.md
# Naming and Validation
> Learn the naming conventions and validation rules VDL applies.
Canonical HTML: https://vdl.varavel.com/docs/language/naming-validation/
Markdown: https://vdl.varavel.com/docs/language/naming-validation/index.md
## Why Naming Matters
VDL schemas are used to generate code in different languages. Consistent names make generated output predictable and pleasant to use.
The analyzer reports diagnostics when names do not follow the expected convention.
## Naming Conventions
| Element | Convention | Examples |
| ------------ | ------------ | ---------------------------- |
| Types | `PascalCase` | `User`, `OrderItem` |
| Enums | `PascalCase` | `OrderStatus`, `Region` |
| Enum members | `PascalCase` | `Pending`, `InProgress` |
| Fields | `camelCase` | `userId`, `createdAt` |
| Constants | `camelCase` | `apiVersion`, `maxPageSize` |
| Annotations | `camelCase` | `deprecated`, `generateCode` |
## PascalCase
PascalCase starts with an uppercase letter and does not use underscores.
Good:
```vdl
type UserProfile {
displayName string
}
enum OrderStatus {
Pending
}
```
Avoid:
```vdl
type user_profile {
displayName string
}
```
## camelCase
camelCase starts with a lowercase letter and does not use underscores.
Good:
```vdl
type UserProfile {
displayName string
createdAt datetime
}
const maxPageSize = 100
```
Avoid:
```vdl
type UserProfile {
display_name string
}
const MaxPageSize = 100
```
## File Names
Regular schema files should match:
```text
[a-z0-9_]+.vdl
```
Examples:
```text
schema.vdl
shared_types.vdl
events_v1.vdl
```
The configuration file must be named:
```text
vdl.config.vdl
```
## Global Name Uniqueness
Top-level type, enum, and constant names share one global namespace.
Invalid:
```vdl
type Status string
enum Status {
Active
}
```
Use unique names across declarations.
## Duplicate Fields
Fields must be unique within each object or inline object.
Invalid:
```vdl
type User {
id string
id int
}
```
## Undefined References
Every type reference must point to a declared type, enum, or primitive.
Invalid:
```vdl
type User {
address Address
}
```
Fix it by declaring `Address` or including the file that declares it.
```vdl
type Address {
city string
}
type User {
address Address
}
```
## Cycles
VDL rejects required type cycles.
Invalid:
```vdl
type Parent {
child Child
}
type Child {
parent Parent
}
```
Break the cycle with an optional field.
```vdl
type Parent {
child Child
}
type Child {
parent? Parent
}
```
## Practical Checklist
Before generating code, check that:
- file names are lowercase with underscores when needed
- types and enums use PascalCase
- fields and constants use camelCase
- all referenced types and enums exist
- object fields are unique
- enum member names and values are unique
- required type references do not form cycles
- spreads do not conflict or form cycles
---
Source: https://vdl.varavel.com/docs/guides/
Markdown: https://vdl.varavel.com/docs/guides/index.md
Content file: content/docs/guides/_index.md
# Guides
> Practical guides for installing, configuring, and extending VDL.
Canonical HTML: https://vdl.varavel.com/docs/guides/
Markdown: https://vdl.varavel.com/docs/guides/index.md
Use these guides to install the CLI, configure generation, connect editors, and work with official or custom plugins.
---
Source: https://vdl.varavel.com/docs/guides/installation/
Markdown: https://vdl.varavel.com/docs/guides/installation/index.md
Content file: content/docs/guides/installation.md
# Installation
> Complete installation options for the VDL CLI.
Canonical HTML: https://vdl.varavel.com/docs/guides/installation/
Markdown: https://vdl.varavel.com/docs/guides/installation/index.md
## Overview
Choose the installation method that best fits your operating system and workflow.
| Platform | Method | Command |
| ----------------- | ------------ | -------------------------------------------------------------- |
| **Linux / macOS** | Shell | curl -fsSL https://get.varavel.com/vdl | sh |
| **Linux / macOS** | Homebrew | `brew install varavelio/tap/vdl` |
| **Windows** | PowerShell | irm https://get.varavel.com/vdl.ps1 | iex |
| **Any** | NPM (local) | `npm install --save-dev @varavel/vdl` |
| **Any** | NPM (global) | `npm install --global @varavel/vdl` |
| **Any** | Docker | `docker run --rm varavel/vdl` |
| **Any** | Manual | [Download binaries](https://github.com/varavelio/vdl/releases) |
After installation, verify that the CLI is available:
```bash
vdl --version
```
## Linux and macOS
### Shell Installer
The shell installer is the quickest way to install VDL on Linux and macOS.
```bash
curl -fsSL https://get.varavel.com/vdl | sh
```
For more installation options using this installer, visit [https://get.varavel.com/vdl](https://get.varavel.com/vdl).
Install a specific version:
```bash
curl -fsSL https://get.varavel.com/vdl | VERSION=vx.x.x sh
```
Replace `vx.x.x` with the release tag you want to install, for example `v0.1.0`.
### Homebrew
Use Homebrew if you prefer managing CLI tools through taps.
```bash
brew install varavelio/tap/vdl
```
Install the latest prerelease:
```bash
brew install varavelio/tap/vdl-next
```
Install a specific version:
```bash
brew install varavelio/tap/vdl@x.x.x
```
Replace `x.x.x` with the version you want to install.
## Windows
Use the PowerShell installer on Windows.
```powershell
irm https://get.varavel.com/vdl.ps1 | iex
```
For more installation options using this installer, visit [https://get.varavel.com/vdl.ps1](https://get.varavel.com/vdl.ps1).
Install a specific version:
```powershell
$env:VERSION = "vx.x.x"; irm https://get.varavel.com/vdl.ps1 | iex
```
Replace `vx.x.x` with the release tag you want to install, for example `v0.1.0`.
## NPM
The npm package is cross-platform and works well when VDL should be pinned per project.
### Local Project Install
This is the recommended npm workflow for teams because it keeps every developer and CI job on the same VDL version.
```bash
npm install --save-dev @varavel/vdl
```
Then call VDL from package scripts, for example:
```json
{
"scripts": {
"vdl:generate": "vdl generate",
"vdl:format": "vdl format"
}
}
```
### Global Install
Use a global install when you want `vdl` available system-wide.
```bash
npm install --global @varavel/vdl
```
Install the latest prerelease:
```bash
npm install --global @varavel/vdl@next
```
Install a specific version:
```bash
npm install --global @varavel/vdl@x.x.x
```
For package details, visit the [npm package page](https://www.npmjs.com/package/@varavel/vdl).
## Docker
The official VDL image is available on both Docker Hub and the GitHub Container Registry. It provides a minimal, multi-arch container with the `vdl` binary at `/usr/local/bin/vdl`.
| Registry | Image |
| ------------------------- | ------------------------------------------------------------------------------ |
| Docker Hub | [`varavel/vdl`](https://hub.docker.com/r/varavel/vdl) |
| GitHub Container Registry | [`ghcr.io/varavelio/vdl`](https://github.com/varavelio/vdl/pkgs/container/vdl) |
The image supports `linux/amd64` and `linux/arm64`.
### Run directly
Call `vdl` commands without installing anything on your host, to work with files in your current directory, mount it as a volume:
```bash
docker run --rm -v "$(pwd):/workspace" -w /workspace varavel/vdl:latest generate
docker run --rm -v "$(pwd):/workspace" -w /workspace varavel/vdl:latest format
```
### Shell alias
If you prefer the native `vdl` experience without installing anything, add an alias to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.):
```bash
alias vdl='docker run --rm -v "$(pwd):/workspace" -w /workspace varavel/vdl:latest'
```
Once the alias is in place you can use `vdl` as if it were installed locally:
```bash
vdl init
vdl generate
vdl format
```
### Pin a version
Replace `latest` with a specific version tag to ensure reproducible builds across your team and CI:
```bash
docker run --rm varavel/vdl:0.1.0 version
```
```bash
alias vdl='docker run --rm -v "$(pwd):/workspace" -w /workspace varavel/vdl:0.1.0'
```
### Copy the binary into your own images
VDL is useful inside CI pipelines and builder images that need to invoke code generation. Copy the binary directly from the official image without compiling or installing anything:
```dockerfile
FROM your-base-image
COPY --from=varavel/vdl:latest /usr/local/bin/vdl /usr/local/bin/vdl
```
This places `vdl` on the default `PATH` of your image so it is immediately usable in downstream steps.
### Use the GitHub Container Registry
The same image is also published to GHCR. Prefer this registry when you need tighter integration with GitHub Actions or when Docker Hub pull rate limits are a concern:
```bash
docker run --rm ghcr.io/varavelio/vdl:latest format
```
```dockerfile
FROM your-base-image
COPY --from=ghcr.io/varavelio/vdl:latest /usr/local/bin/vdl /usr/local/bin/vdl
```
## Manual Downloads
You can download prebuilt binaries from the [VDL releases page](https://github.com/varavelio/vdl/releases).
Manual installation is useful when you need to:
- install VDL in an environment without package managers
- mirror binaries internally
- pin a binary in a custom CI image
- inspect release assets before installing
Download the archive for your operating system and architecture, extract it, and place the `vdl` binary somewhere on your `PATH`.
## Choosing A Method
- Use the **shell installer** for the fastest Linux/macOS setup.
- Use **Homebrew** if you already manage developer tools with Homebrew.
- Use the **PowerShell installer** on Windows.
- Use **local npm install** for project-pinned VDL versions in Node-based repositories.
- Use **Docker** when you want to run VDL without installing anything locally, or when you need to embed the VDL binary inside your own container images.
- Use **manual downloads** for custom distribution, offline environments, or CI images.
---
Source: https://vdl.varavel.com/docs/guides/cli/
Markdown: https://vdl.varavel.com/docs/guides/cli/index.md
Content file: content/docs/guides/cli.md
# CLI Commands
> Complete reference for the VDL command-line interface.
Canonical HTML: https://vdl.varavel.com/docs/guides/cli/
Markdown: https://vdl.varavel.com/docs/guides/cli/index.md
## Overview
The `vdl` CLI is the primary way to interact with VDL projects. It covers the full lifecycle: creating a project, writing and formatting schemas, generating code through plugins, compiling to inspect the intermediate representation, and running the language server for editor integration.
If you have not installed VDL yet, start with the [installation guide](/docs/guides/installation/).
## Quick Reference
| Command | Purpose |
| -------------- | ----------------------------------------------- |
| `vdl init` | Create a new VDL project with schema and config |
| `vdl format` | Format `.vdl` files in place |
| `vdl generate` | Run code generation from `vdl.config.vdl` |
| `vdl compile` | Compile a `.vdl` file and print its IR as JSON |
| `vdl lsp` | Start the VDL language server |
| `vdl version` | Show VDL version information |
## Global Behavior
A few things work everywhere, no matter which subcommand you use.
### Help
Every command supports `--help` and `-h`:
```bash
vdl --help
vdl init --help
vdl generate --help
```
Help output includes the VDL logo plus a description of the command and its flags.
### Version
Use `--version`, `-v`, or the `version` subcommand to see the installed VDL version:
```bash
vdl --version
vdl version
```
### Default Output
Running `vdl` with no subcommand shows the VDL logo and version.
## `vdl init`
Initialize a new VDL project in a directory.
```bash
vdl init
vdl init ./my-project
```
### What It Creates
Two files are written into the target directory:
| File | Purpose |
| ---------------- | ------------------------------------------------- |
| `schema.vdl` | A sample schema with types, constants, and RPCs |
| `vdl.config.vdl` | A commented config file ready for code generation |
The sample config includes commented-out plugin examples so you can uncomment the ones you need or add more as needed.
### Arguments
| Argument | Required | Description |
| -------- | -------- | ---------------------------------------------------------- |
| `path` | no | Target directory. Defaults to the current directory (`.`). |
## `vdl format`
Format `.vdl` files in place.
```bash
vdl format
vdl format ./schemas/**/*.vdl
vdl format ./schemas ./other
vdl format --verbose
```
### How It Works
The formatter uses VDL's own lexer and parser to normalize whitespace, indentation, inline spacing, and blank lines so every file follows a consistent style. Files are overwritten in place.
### Arguments
| Argument | Required | Description |
| ----------- | -------- | ----------------------------------------------------------- |
| `patterns` | no | Glob patterns or directory paths. Defaults to `./**/*.vdl`. |
| `--verbose` | no | Print each file path as it is formatted. |
### Pattern Behavior
- Plain paths like `./schemas` are treated as directories and expanded to `./schemas/**/*.vdl` automatically.
- Standard glob patterns use double-star syntax (`**`) for recursive matching.
- Files without a `.vdl` extension are skipped.
- Duplicate matches are deduplicated, so the same file is never formatted twice.
### Examples
Format every VDL file in the current project:
```bash
vdl format
```
Format a specific set of files with feedback:
```bash
vdl format ./api/*.vdl ./shared/*.vdl --verbose
```
Format everything inside a directory:
```bash
vdl format ./schemas
```
## `vdl generate`
Run code generation from a `vdl.config.vdl` project.
```bash
vdl generate
vdl generate ./my-project
vdl generate --check
```
### What It Does
The generation pipeline works like this:
1. VDL finds and reads `vdl.config.vdl`.
2. Pre-generation hooks run (if configured). The first failure stops the pipeline.
3. Each configured plugin source is resolved:
- **Local `.js` files** are loaded directly.
- **HTTPS URLs** are fetched and cached.
- **GitHub shorthands** like `owner/vdl-plugin-go@v0.1.3` resolve to a remote `dist/index.js` artifact, which is fetched and cached.
4. Each plugin's configured `.vdl` schema is analyzed and converted into the Intermediate Representation (IR).
5. All plugins execute concurrently in an embedded JavaScript runtime.
6. Output file paths are validated to stay inside their configured `outDir`.
7. Conflicting writes between plugins are detected and fail the run.
8. Output directories are cleaned (default) or merged, and generated files are written.
9. Post-generation hooks run (failures print warnings but do not roll back files).
10. The `vdl.lock` file is updated with remote plugin hashes.
### Arguments
| Argument | Required | Description |
| --------- | -------- | ------------------------------------------------------------------- |
| `path` | no | Directory or path to `vdl.config.vdl`. Defaults to `.` (cwd). |
| `--check` | no | Run the full pipeline but skip writing output files. Useful for CI. |
### Config File Discovery
When `path` points to a directory, VDL looks for a file named exactly `vdl.config.vdl` inside it. When `path` points directly to a file, that file is used. The config file must declare a `const config` with the generation settings.
For all configuration options, see the [project configuration guide](/docs/guides/configuration/).
### `--check` Mode
Use `--check` in CI to verify that generation would succeed without producing side effects:
```bash
vdl generate --check
```
In check mode, VDL runs the full pipeline—hooks, plugin resolution, schema analysis, plugin execution, and output validation—but skips writing files, updating `vdl.lock` and executing post generation hooks. If the pipeline fails, the command exits with a non-zero code, making it suitable for linting and CI workflows.
### Lock File
Remote plugin artifacts are cached and their content hashes are recorded in `vdl.lock`. Commit this file when your project depends on remote plugins. VDL uses it to detect unexpected changes in cached plugins.
## `vdl compile`
Compile a `.vdl` file and print its Intermediate Representation (IR) as formatted JSON to stdout.
```bash
vdl compile ./schema.vdl
```
### What It Produces
The IR is a machine-readable description of everything VDL understands about your schema: types, enums, constants, annotations, documentation, and relationships. It is the same IR that plugins receive as `input.ir`.
### Arguments
| Argument | Required | Description |
| -------- | -------- | ----------------------------------- |
| `file` | yes | Path to the `.vdl` file to compile. |
### Use Cases
- Inspect what your schema looks like after analysis, before generation.
- Debug why a plugin produces unexpected output.
- Pipe the IR into custom tooling:
```bash
vdl compile ./schema.vdl | jq '.types | length'
vdl compile ./schema.vdl > schema-ir.json
```
### Error Handling
If the schema has errors, diagnostics are printed to stderr and the command exits with code 1. No partial JSON is emitted.
## `vdl lsp`
Start the VDL language server.
```bash
vdl lsp
vdl lsp --log-path
```
### How It Works
The language server speaks the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) over stdin and stdout. It provides editor features for `.vdl` files:
- Go-to-definition
- Hover information
- Find references
- Rename
- Completions
- Document symbols
- Document links
You typically do not run `vdl lsp` directly. Your editor plugin starts it and communicates with it over stdio. See the [VS Code extension](https://github.com/varavelio/vdl/blob/main/editors/vscode/README.md) or [Neovim setup](https://github.com/varavelio/vdl/blob/main/editors/neovim/README.md) for editor-specific instructions.
### Arguments
| Argument | Required | Description |
| ------------ | -------- | ------------------------------------------------------------------ |
| `--log-path` | no | Print the path to the LSP log file instead of starting the server. |
### Log Files
The LSP writes detailed diagnostic logs to a file under the VDL logs directory (`~/.vdl/logs/` by default). Use `--log-path` to find the log file location, then inspect it when debugging editor behavior.
```bash
vdl lsp --log-path
# /home/you/.vdl/logs/vdl-lsp.log
cat /home/you/.vdl/logs/vdl-lsp.log
```
## `vdl version`
Show VDL version information.
```bash
vdl version
vdl --version
vdl -v
```
The output includes the VDL logo, version number, and links to the repository.
## The VDL Home Directory
VDL stores caches and logs under a home directory outside your project.
### Location
By default, the VDL home directory is `~/.vdl`. You can override it with the `VDL_HOME` environment variable:
```bash
export VDL_HOME=/custom/path/.vdl
```
If no user home directory is available, VDL falls back to your system temp directory.
### Contents
| Directory | Purpose |
| --------- | -------------------------------------------------- |
| `cache/` | Cached remote plugin artifacts for reproducibility |
| `logs/` | Log files written by the language server |
## Environment Variables
| Variable | Purpose |
| ------------------------- | -------------------------------------------------------------------------------- |
| `VDL_HOME` | Override the VDL home directory (default: `~/.vdl`). |
| `VDL_INSECURE_ALLOW_HTTP` | Allow `http://` URLs for local plugin development. Set to `true`. |
| `VDL_SKIP_HOST_HOOKS` | Skip pre-generation and post-generation hooks. Set to `true`. |
| `VDL_CLOUD` | Skip host hooks (for cloud CI environments where shell hooks are not supported). |
## Common Workflows
### Start A New Project
```bash
vdl init ./my-service
cd ./my-service
# Edit schema.vdl and vdl.config.vdl
vdl generate
```
### Format In CI
```bash
vdl format --verbose
git diff --exit-code || echo "Unformatted VDL files detected"
```
### Check Generation In CI
```bash
vdl generate --check
```
### Explore A Schema
```bash
# See how many types a schema has
vdl compile ./schema.vdl | jq '.types | length'
# List type names
vdl compile ./schema.vdl | jq '.types[].name'
# Inspect enum values
vdl compile ./schema.vdl | jq '.enums[] | {name, values: [.members[].name]}'
```
### Debug Plugin Behavior
```bash
# Compare the IR a plugin will receive
vdl compile ./schema.vdl > debug-ir.json
# Run generation in check mode
vdl generate --check
```
### Repeated Generation Workflow
```bash
# Format first, then generate
vdl format
vdl generate
```
## Troubleshooting
### `vdl generate` cannot find the config file
Make sure the file is named exactly `vdl.config.vdl` and that you are running the command from the correct directory. Pass an explicit path if needed:
```bash
vdl generate ./path/to/vdl.config.vdl
```
### `vdl generate` fails on a remote plugin
Check that the plugin version exists and that the repository is public (or configured with the correct `remotes` block). Remote plugins are cached under the VDL cache directory—clear it with:
```bash
rm -rf ~/.vdl/cache
```
### `vdl format` does not find any files
By default, `vdl format` looks for `./**/*.vdl`. If your VDL files use a different extension or are in an unexpected location, pass explicit patterns:
```bash
vdl format ./my-schemas/**/*.vdl
```
### The language server is not responding
Check the LSP logs:
```bash
vdl lsp --log-path # find the log file
cat # inspect it
```
Make sure your editor extension is configured to launch `vdl lsp` and not a different binary.
---
Source: https://vdl.varavel.com/docs/guides/editors/
Markdown: https://vdl.varavel.com/docs/guides/editors/index.md
Content file: content/docs/guides/editors.md
# Editor Integration
> Set up VDL support in your code editor.
Canonical HTML: https://vdl.varavel.com/docs/guides/editors/
Markdown: https://vdl.varavel.com/docs/guides/editors/index.md
## Overview
VDL works with any editor that supports the Language Server Protocol (LSP). First-class integrations are available for Visual Studio Code and it's forks.
## Visual Studio Code
The official VDL extension provides full language support for Visual Studio Code and any VSCode-based editor (VSCodium, Cursor, Windsurf, and others).
### Installation
Install the extension from either marketplace:
- [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=varavel.vdl)
- [Open VSX Registry](https://open-vsx.org/extension/varavel/vdl)
The extension is plug and play. The only requirement is having the `vdl` binary installed on your system. If you have not installed the CLI yet, follow the [installation guide](/docs/guides/installation/).
Extension source code: [editors/vscode](https://github.com/varavelio/vdl/tree/main/editors/vscode)
### Features
Once installed, opening any `.vdl` file gives you:
- Syntax highlighting
- Error highlighting and diagnostics
- Code autocompletion
- Auto-formatting on save
- Go-to-definition, hover info, find references, and rename
- Code snippets for types, fields, enums, constants, annotations, and more
For configuration options, available commands, and troubleshooting, see the extension page on the [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=varavel.vdl) or [Open VSX](https://open-vsx.org/extension/varavel/vdl).
## Neovim
VDL's built-in LSP works with Neovim's native LSP client. The language server provides go-to-definition, hover, references, rename, completions, document symbols, and document links for `.vdl` files.
Syntax highlighting is not included through the LSP and may be added in a future release.
The `vdl` binary must be installed and available on your `PATH`. For setup instructions, see the [Neovim integration README](https://github.com/varavelio/vdl/tree/main/editors/neovim).
## Other Editors
The `vdl lsp` command speaks the standard Language Server Protocol over stdin and stdout, so any editor with LSP support can integrate with it. Point your editor's LSP client to `vdl lsp` for `.vdl` files.
VDL's LSP does not provide syntax highlighting on its own. Some editors require a separate TextMate grammar ([vdl.tmLanguage.json](https://github.com/varavelio/vdl/blob/main/editors/vscode/language/vdl.tmLanguage.json)) or Tree-sitter parser for highlighting. Syntax highlighting for editors beyond VSCode is planned for a future release.
To get started, check your editor's documentation on configuring a [custom language server](https://microsoft.github.io/language-server-protocol/implementors/servers/).
---
Source: https://vdl.varavel.com/docs/guides/configuration/
Markdown: https://vdl.varavel.com/docs/guides/configuration/index.md
Content file: content/docs/guides/configuration.md
# Project Configuration
> Configure VDL generation with vdl.config.vdl.
Canonical HTML: https://vdl.varavel.com/docs/guides/configuration/
Markdown: https://vdl.varavel.com/docs/guides/configuration/index.md
## What `vdl.config.vdl` Does
`vdl.config.vdl` tells `vdl generate` which plugins to run, which schema each plugin should read, where generated files should be written, and which plugin-specific options should be passed in.
The file is itself written in VDL. VDL reads a constant named `config` from this file.
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./gen/ts"
}
]
}
```
The file must be named exactly:
```text
vdl.config.vdl
```
## Minimal Configuration
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-json-schema@v0.1.0"
schema "./schema.vdl"
outDir "./gen/json-schema"
}
]
}
```
This configuration:
- uses config format version `1`
- runs one plugin
- analyzes `./schema.vdl`
- writes generated files under `./gen/json-schema`
## Top-Level Fields
| Field | Required | Description |
| ------------- | -------- | --------------------------------------------------------------- |
| `version` | yes | Configuration format version. Currently use `1`. |
| `cleanOutDir` | no | Whether VDL cleans output directories before writing new files. |
| `plugins` | no | List of plugin runs to execute. |
| `remotes` | no | Authentication settings for private plugin hosts. |
| `hooks` | no | Host shell commands run before or after generation. |
## `version`
Set `version` to `1`.
```vdl
const config = {
version 1
}
```
VDL rejects unsupported config versions.
## `plugins`
`plugins` is an array of plugin configurations.
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
options {
package "contracts"
}
}
]
}
```
Each plugin entry has these fields:
| Field | Required | Description |
| ---------------- | -------- | ------------------------------------------------------------------- |
| `src` | yes | Plugin JavaScript source. |
| `schema` | yes | Path to the `.vdl` schema this plugin should process. |
| `outDir` | yes | Directory where plugin output files should be written. |
| `generateHeader` | no | Whether VDL adds generated-file header comments. Default is `true`. |
| `options` | no | Plugin-specific string options passed through as-is. |
## Plugin Sources
`src` can use one of three forms.
### GitHub Shorthand
```vdl
src "varavelio/vdl-plugin-go@v0.1.3"
```
This resolves to:
```text
https://raw.githubusercontent.com/varavelio/vdl-plugin-go/v0.1.3/dist/index.js
```
Rules:
- use `owner/repo@ref`
- the repository name must start with `vdl-plugin-`
- the plugin artifact must exist at `dist/index.js`
- prefer release tags or full commit hashes for reproducibility
### Local JavaScript File
```vdl
src "./plugins/my-plugin.js"
```
Local plugin paths must point to `.js` files and must start with `.` or `/`.
Use this while developing a plugin locally or for private in-repo generators.
### Remote HTTPS URL
```vdl
src "https://example.com/plugins/my-plugin/index.js"
```
Remote plugin URLs must point to `.js` files. HTTPS is required by default.
For local development, insecure HTTP can be enabled with `VDL_INSECURE_ALLOW_HTTP=true`.
## `schema`
`schema` points to the VDL file the plugin should compile.
```vdl
schema "./schema.vdl"
```
The path is resolved relative to `vdl.config.vdl` and must end with `.vdl`.
Different plugins can process the same schema:
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
}
{
src "varavelio/vdl-plugin-json-schema@v0.1.0"
schema "./schema.vdl"
outDir "./gen/json-schema"
}
]
}
```
## `outDir`
`outDir` is the root directory for files returned by a plugin.
```vdl
outDir "./gen/go"
```
Plugins return relative file paths like `types.go` or `models/user.ts`. VDL writes them under `outDir`.
VDL validates generated paths so plugins cannot write outside `outDir`.
If two plugin outputs try to write the same final file path in one generation run, generation fails.
## `options`
`options` is a map of string values passed to the plugin.
```vdl
options {
package "contracts"
strict "true"
importExtension "js"
}
```
All option values are strings. If a plugin option behaves like a boolean, pass values such as `"true"` or `"false"` according to that plugin's documentation.
Options are plugin-specific. Always check the plugin README or [available plugins guide](/docs/guides/plugins/).
## `generateHeader`
By default, VDL adds generated-file headers when writing plugin outputs.
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
generateHeader false
}
]
}
```
Use `generateHeader false` only when a target format cannot safely contain comments or when the plugin already handles headers.
## `cleanOutDir`
By default, VDL replaces configured output directories with the newly generated files.
```vdl
const config = {
version 1
cleanOutDir true
plugins [
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./gen/ts"
}
]
}
```
Set `cleanOutDir false` to merge generated files into existing directories without deleting files that are not part of the current output.
```vdl
const config = {
version 1
cleanOutDir false
plugins [
{
src "./plugins/custom.js"
schema "./schema.vdl"
outDir "./gen/custom"
}
]
}
```
Use merge mode carefully. Old generated files may remain if a plugin stops producing them.
## `remotes`
Use `remotes` when a plugin is hosted behind authentication.
```vdl
const config = {
version 1
remotes [
{
host "github.com/my-org/private-vdl-plugin"
auth {
github {
tokenEnv "GITHUB_TOKEN"
}
}
}
]
plugins [
{
src "my-org/private-vdl-plugin@v1.0.0"
schema "./schema.vdl"
outDir "./gen/private"
}
]
}
```
`host` should not include `https://` or `http://`.
When multiple remotes match a plugin URL, VDL uses the most specific host.
Supported authentication forms:
- GitHub token
- custom header
- bearer token
- basic auth
Each remote auth entry should define exactly one authentication method.
### GitHub Token
```vdl
remotes [
{
host "github.com/my-org/private-vdl-plugin"
auth {
github {
tokenEnv "GITHUB_TOKEN"
}
}
}
]
```
VDL reads the token from the named environment variable and sends it as a bearer token.
### Custom Header
```vdl
remotes [
{
host "plugins.example.com"
auth {
header {
nameEnv "PLUGIN_HEADER_NAME"
valueEnv "PLUGIN_HEADER_VALUE"
}
}
}
]
```
### Bearer Token
```vdl
remotes [
{
host "plugins.example.com"
auth {
bearer {
tokenEnv "PLUGIN_TOKEN"
}
}
}
]
```
### Basic Auth
```vdl
remotes [
{
host "plugins.example.com"
auth {
basic {
userEnv "PLUGIN_USER"
passEnv "PLUGIN_PASS"
}
}
}
]
```
## `hooks`
Hooks run shell commands on the host machine.
```vdl
const config = {
version 1
hooks {
preGenerate ["npm run check"]
postGenerate ["npm run format"]
}
plugins [
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./gen/ts"
}
]
}
```
Hook behavior:
- `preGenerate` commands run before plugins. The first failure stops generation.
- `postGenerate` commands run after files are written. Failures are printed as warnings and do not roll back generated files.
- commands run from the directory containing `vdl.config.vdl`
- hooks are skipped when `VDL_SKIP_HOST_HOOKS` or `VDL_CLOUD` is truthy
## Lock File
Remote plugin downloads are cached and recorded in `vdl.lock`.
The lock file stores hashes for remote plugin artifacts so VDL can detect unexpected changes.
Commit `vdl.lock` when your project depends on remote plugins.
## Complete Example
```vdl
const config = {
version 1
cleanOutDir true
hooks {
preGenerate ["npm run check"]
postGenerate ["npm run format"]
}
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
options {
package "contracts"
strict "true"
}
}
{
src "varavelio/vdl-plugin-json-schema@v0.1.0"
schema "./schema.vdl"
outDir "./gen/json-schema"
options {
outFile "schema.json"
root "User"
}
}
]
}
```
## Common Patterns
Generate data models and documentation from the same schema:
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./generated/types"
}
{
src "varavelio/vdl-plugin-explorer@v0.1.1"
schema "./schema.vdl"
outDir "./generated/docs"
}
]
}
```
Generate Go models and Go RPC code into separate packages:
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./internal/contracts"
options {
package "contracts"
}
}
{
src "varavelio/vdl-plugin-rpc-go@v0.1.2"
schema "./schema.vdl"
outDir "./internal/rpcclient"
options {
target "client"
package "rpcclient"
typesImport "example.com/project/internal/contracts"
}
}
]
}
```
## Troubleshooting
If `vdl generate` cannot find the config file, make sure the file is named exactly `vdl.config.vdl`.
If a plugin source fails to resolve, check whether `src` is a local `.js` path, an HTTPS `.js` URL, or a valid GitHub shorthand.
If a private plugin fails to download, check the matching `remotes` entry and the required environment variables.
If generated files disappear, check `cleanOutDir`. The default behavior is to replace output directories.
---
Source: https://vdl.varavel.com/docs/guides/plugins/
Markdown: https://vdl.varavel.com/docs/guides/plugins/index.md
Content file: content/docs/guides/plugins.md
# Available Plugins
> Official VDL plugins and what each one generates.
Canonical HTML: https://vdl.varavel.com/docs/guides/plugins/
Markdown: https://vdl.varavel.com/docs/guides/plugins/index.md
## Overview
VDL generates output through plugins. Each plugin receives the resolved VDL IR plus the plugin options from `vdl.config.vdl`, then returns files for VDL to write into the configured `outDir`.
This page summarizes the current official plugin ecosystem.
## Data Model Plugins
### `varavelio/vdl-plugin-go`
Generates Go data-model code from VDL types, enums, and constants.
Use it when you want Go structs, enum-like types, constants, strict JSON validation, optional-field pointer handling, and `datetime` mapped to `time.Time`.
Typical outputs include `types.go`, `enums.go`, `constants.go`, and `pointers.go`.
Common options:
| Option | Purpose |
| ----------------- | ------------------------------------------------------ |
| `package` | Go package name. |
| `genConsts` | Enable or disable constant generation. |
| `strict` | Enable stricter JSON validation. |
| `genPointerUtils` | Generate `Ptr`, `Val`, and `Or` helpers. |
| `jsonPackage` | Use a compatible JSON package such as `goccy/go-json`. |
Example:
```vdl
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
options {
package "contracts"
}
}
```
Repository: [varavelio/vdl-plugin-go](https://github.com/varavelio/vdl-plugin-go)
### `varavelio/vdl-plugin-ts`
Generates TypeScript data-model code from VDL types, enums, and constants.
Use it when you want typed interfaces, enum helpers, constants, `Date` hydration for `datetime`, and optional runtime validation helpers.
Typical outputs include `types.ts`, `enums.ts`, `index.ts`, and optionally `constants.ts`.
Common options:
| Option | Purpose |
| ----------------- | ------------------------------------------- |
| `genConsts` | Enable or disable constant generation. |
| `strict` | Generate runtime `validate(...)` helpers. |
| `importExtension` | Use `.js`, `.ts`, or extensionless imports. |
Example:
```vdl
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./gen/ts"
options {
importExtension "js"
}
}
```
Repository: [varavelio/vdl-plugin-ts](https://github.com/varavelio/vdl-plugin-ts)
### `varavelio/vdl-plugin-json-schema`
Generates JSON Schema Draft 2020-12 from VDL types and enums.
Use it when you need a standard machine-readable schema for validators, form builders, documentation systems, API gateways, or external integrations.
The plugin emits all top-level VDL types and enums under `$defs`, supports references between definitions, maps non-optional fields to JSON Schema `required`, and carries documentation and `@deprecated` metadata into the generated schema.
Common options:
| Option | Purpose |
| --------- | ------------------------------------------------------ |
| `outFile` | Output JSON filename. |
| `id` | Top-level JSON Schema `$id`. |
| `root` | Top-level `$ref` pointing to one generated definition. |
Example:
```vdl
{
src "varavelio/vdl-plugin-json-schema@v0.1.0"
schema "./schema.vdl"
outDir "./gen/json-schema"
options {
outFile "schema.json"
root "Account"
}
}
```
Repository: [varavelio/vdl-plugin-json-schema](https://github.com/varavelio/vdl-plugin-json-schema)
## RPC Plugins
RPC support is annotation-based. These plugins look for `@rpc` service types and `@proc` or `@stream` operation fields.
### `varavelio/vdl-plugin-rpc-go`
Generates Go RPC clients or servers from annotation-based VDL RPC services.
This plugin is RPC-only. It does not generate data models; pair it with `varavelio/vdl-plugin-go` and point `typesImport` to the generated Go type package when the RPC output is in a separate package.
For `target "client"`, it emits typed client builders, procedure and stream execution APIs, headers, interceptors, retry and timeout policies, reconnect policies, and stream hooks.
For `target "server"`, it emits typed handler registration APIs, middleware hooks, stream configuration, error handling, and `net/http` adapter helpers.
Common options:
| Option | Purpose |
| ------------- | ------------------------------------------------------------ |
| `target` | Required. `client` or `server`. |
| `typesImport` | Go import path for the package generated by `vdl-plugin-go`. |
| `package` | Generated Go package name. |
| `jsonPackage` | Compatible JSON package imported as `json`. |
Example:
```vdl
{
src "varavelio/vdl-plugin-rpc-go@v0.1.2"
schema "./schema.vdl"
outDir "./internal/client"
options {
target "client"
package "client"
typesImport "example.com/project/internal/types"
}
}
```
Repository: [varavelio/vdl-plugin-rpc-go](https://github.com/varavelio/vdl-plugin-rpc-go)
### `varavelio/vdl-plugin-rpc-ts`
Generates TypeScript RPC clients or servers from annotation-based VDL RPC services.
This plugin is RPC-only. It expects data models from `varavelio/vdl-plugin-ts` and references them through `typesImport`.
For `target "client"`, it emits a typed client with procedure and stream builders, headers, interceptors, retries, timeouts, reconnect policies, and stream controls.
For `target "server"`, it emits a typed server with procedure and stream registration APIs, middleware, error handlers, ping configuration, and Node.js plus Fetch-compatible HTTP adapters.
Common options:
| Option | Purpose |
| ----------------- | -------------------------------------------------------- |
| `target` | Required. `client` or `server`. |
| `typesImport` | Required import path to the generated TypeScript models. |
| `importExtension` | Internal generated import extension behavior. |
Example:
```vdl
{
src "varavelio/vdl-plugin-rpc-ts@v0.1.0"
schema "./schema.vdl"
outDir "./generated/client"
options {
target "client"
typesImport "../types/index.js"
importExtension "js"
}
}
```
Repository: [varavelio/vdl-plugin-rpc-ts](https://github.com/varavelio/vdl-plugin-rpc-ts)
### `varavelio/vdl-plugin-rpc-openapi`
Generates OpenAPI 3.0 documents for VDL RPC procedures and streams.
Use it when you want to publish an OpenAPI contract for your VDL RPC layer, import requests into tools such as Postman, or feed OpenAPI-compatible docs, gateway, validation, or generation workflows.
It emits one `POST` path per operation, groups operations by service, emits request and response envelopes, and includes non-RPC VDL types and enums under `components.schemas`.
Common options:
| Option | Purpose |
| ---------------- | --------------------------------------------------------- |
| `outFile` | OpenAPI output file, usually `.yaml`, `.yml`, or `.json`. |
| `playgroundFile` | Optional standalone HTML docs file. |
| `playgroundUi` | `swagger-ui`, `scalar`, or `stoplight-elements`. |
| `title` | OpenAPI `info.title`. |
| `version` | OpenAPI `info.version`. |
| `description` | OpenAPI `info.description`. |
| `baseUrl` | One or more comma-separated server URLs. |
Example:
```vdl
{
src "varavelio/vdl-plugin-rpc-openapi@v0.1.3"
schema "./schema.vdl"
outDir "./gen/openapi"
options {
outFile "openapi.yaml"
title "Messaging API"
version "1.0.0"
}
}
```
Repository: [varavelio/vdl-plugin-rpc-openapi](https://github.com/varavelio/vdl-plugin-rpc-openapi)
## Event Plugins
### `varavelio/vdl-plugin-events-go`
Generates Go subject builders and a centralized event catalog from VDL types annotated with `@event`.
This plugin is event-focused and only processes `@event` object types. It does not generate event payload structs; pair it with `varavelio/vdl-plugin-go` for payload models.
It validates event subject templates, checks `{fieldName}` placeholders against top-level primitive fields, supports repeated placeholders, formats non-string placeholders, and emits a typed `VDLEventCatalog` with `BuildSubject` functions.
Common options:
| Option | Purpose |
| --------- | -------------------------- |
| `package` | Generated Go package name. |
| `outFile` | Generated Go filename. |
Example:
```vdl
{
src "varavelio/vdl-plugin-events-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/events"
options {
package "events"
}
}
```
Repository: [varavelio/vdl-plugin-events-go](https://github.com/varavelio/vdl-plugin-events-go)
## Documentation And Metadata Plugins
### `varavelio/vdl-plugin-explorer`
Generates a standalone HTML explorer for VDL docs, RPCs, types, enums, and constants.
Use it when you want to share schema documentation with teammates, clients, or partners without running a server. The generated HTML embeds the schema data directly, includes search and navigation, supports light and dark themes, and works as a static file.
Common options:
| Option | Purpose |
| ------------ | ----------------------------------------------------- |
| `outFile` | Output HTML filename. |
| `rpcBaseUrl` | Base URL shown for generated RPC operation endpoints. |
Example:
```vdl
{
src "varavelio/vdl-plugin-explorer@v0.1.1"
schema "./schema.vdl"
outDir "./docs"
options {
outFile "schema-explorer.html"
}
}
```
Repository: [varavelio/vdl-plugin-explorer](https://github.com/varavelio/vdl-plugin-explorer)
### `varavelio/vdl-plugin-meta`
Exports the VDL Intermediate Representation as JSON.
Use it when you need reflection-like schema metadata at runtime, for internal tools, dynamic forms, custom validators, explorers, or downstream generators.
Common options:
| Option | Purpose |
| ------------------ | ----------------------------------------------- |
| `outFile` | Output JSON filename. |
| `includePositions` | Include source file, line, and column metadata. |
Example:
```vdl
{
src "varavelio/vdl-plugin-meta@v1.0.1"
schema "./schema.vdl"
outDir "./gen/meta"
options {
outFile "vdl-meta.json"
includePositions "false"
}
}
```
Repository: [varavelio/vdl-plugin-meta](https://github.com/varavelio/vdl-plugin-meta)
## Combining Plugins
You can run several plugins against the same schema in one `vdl generate` call.
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-go@v0.1.3"
schema "./schema.vdl"
outDir "./gen/go"
options { package "contracts" }
}
{
src "varavelio/vdl-plugin-json-schema@v0.1.0"
schema "./schema.vdl"
outDir "./gen/json-schema"
}
{
src "varavelio/vdl-plugin-explorer@v0.1.1"
schema "./schema.vdl"
outDir "./docs/schema"
}
]
}
```
Use separate `outDir` values unless you intentionally want related generated files in the same package. VDL prevents two plugin outputs from writing the same file path in one generation run.
## Version Note
The examples above use the versions documented in each plugin README at the time this page was written. Check each plugin repository for the latest release tag before pinning a new project.
---
Source: https://vdl.varavel.com/docs/guides/creating-plugins/
Markdown: https://vdl.varavel.com/docs/guides/creating-plugins/index.md
Content file: content/docs/guides/creating-plugins.md
# Creating Plugins
> A practical guide to writing VDL plugins.
Canonical HTML: https://vdl.varavel.com/docs/guides/creating-plugins/
Markdown: https://vdl.varavel.com/docs/guides/creating-plugins/index.md
> **Note:** You do not need Node.js or any JavaScript runtime installed on your machine. VDL executes plugin JavaScript code through [Goja](https://github.com/dop251/goja), an embedded ECMAScript runtime built into the VDL binary.
## The Short Version
A VDL plugin is just a JavaScript file that exports a `generate(input)` function.
```js
exports.generate = (input) => {
return {
files: [
{
path: "summary.txt",
content: `This schema has ${input.ir.types.length} types.\n`,
},
],
};
};
```
Then reference it from `vdl.config.vdl`:
```vdl
const config = {
version 1
plugins [
{
src "./plugins/summary.js"
schema "./schema.vdl"
outDir "./gen"
}
]
}
```
Run:
```bash
vdl generate
```
VDL compiles `schema.vdl`, calls your plugin function, and writes the returned files under `./gen`.
## How Plugins Work
The generation flow is simple.
1. VDL reads `vdl.config.vdl`.
2. VDL resolves each configured plugin source.
3. VDL analyzes the configured `schema` file.
4. VDL converts the valid schema into the generator-facing IR.
5. VDL executes the plugin's `generate(input)` function in a JavaScript runtime.
6. The plugin returns generated files or structured errors.
7. VDL validates output paths and writes files inside `outDir`.
The plugin input has this shape:
```ts
type PluginInput = {
version: string;
ir: IrSchema;
options: Record;
};
```
The plugin output has this shape:
```ts
type PluginOutput = {
files?: Array<{
path: string;
content: string;
}>;
errors?: Array<{
message: string;
position?: {
file: string;
line: number;
column: number;
};
}>;
};
```
`input.version` is the VDL version. `input.ir` is the fully resolved Intermediate Representation of the schema. `input.options` contains the key-value options from the plugin block in `vdl.config.vdl`.
Options are strings in the config schema. If you need booleans, numbers, lists, or enums, parse and validate them in your plugin.
## Minimal JavaScript Plugin
Create `plugins/models-list.js`:
```js
exports.generate = (input) => {
const lines = [];
lines.push("# VDL Types");
lines.push("");
for (const typeDef of input.ir.types) {
lines.push(`- ${typeDef.name}`);
}
return {
files: [
{
path: "models.md",
content: `${lines.join("\n")}\n`,
},
],
};
};
```
Configure it:
```vdl
const config = {
version 1
plugins [
{
src "./plugins/models-list.js"
schema "./schema.vdl"
outDir "./gen/docs"
}
]
}
```
Run:
```bash
vdl generate
```
Result:
```text
gen/docs/models.md
```
## Export Formats
VDL accepts any of these forms:
```js
exports.generate = (input) => ({ files: [] });
```
```js
module.exports.generate = (input) => ({ files: [] });
```
## Important Runtime Rules
- The plugin runs as JavaScript, not as a full Node.js process.
- Keep the release artifact self-contained; do not rely on runtime filesystem or npm package loading.
- `console.log`, `console.info`, `console.warn`, and `console.error` are available for plugin logs.
- Returned file paths must be relative and must stay inside `outDir`.
- If the plugin returns `errors`, VDL stops and does not write generated files.
- Throwing an unexpected error fails the plugin run.
- Remote plugins should be pinned to a release tag or full commit hash for reproducibility.
## Plugin Sources
`src` in `vdl.config.vdl` can point to several kinds of plugin artifact.
| Source kind | Example | Notes |
| ---------------- | ----------------------------------------- | ----------------------------------------- |
| Local `.js` file | `./plugins/my-plugin.js` | Must start with `.` or `/`. |
| HTTPS `.js` URL | `https://example.com/vdl-plugin/index.js` | Must point to a `.js` file. |
| GitHub shorthand | `varavelio/vdl-plugin-go@v0.1.3` | Resolves to `dist/index.js` in that repo. |
GitHub shorthand repositories must be named with the `vdl-plugin-` prefix. VDL caches remote plugin files and records hashes in `vdl.lock`.
## Authenticated Remote Plugins
Private plugin hosts can be configured through `remotes` in `vdl.config.vdl`. VDL matches each plugin URL against the most specific configured remote host and reads credentials from environment variables.
```vdl
const config = {
version 1
remotes [
{
host "github.com/my-org/private-vdl-plugin"
auth {
github {
tokenEnv "GITHUB_TOKEN"
}
}
}
]
plugins [
{
src "my-org/private-vdl-plugin@v1.0.0"
schema "./schema.vdl"
outDir "./gen/private"
}
]
}
```
Supported auth styles are GitHub token, custom header, bearer token, and basic auth.
## Using The Plugin SDK
Plain JavaScript is enough for small plugins. For serious plugins, use [`@varavel/vdl-plugin-sdk`](https://github.com/varavelio/vdl-plugin-sdk).
The SDK gives you:
- typed `definePlugin(...)` authoring API
- typed access to the VDL IR and plugin input/output contracts
- structured error helpers such as `PluginError`, `fail`, and `assert`
- utility imports for strings, arrays, RPC validation, and other common generator tasks
- testing builders for realistic IR fixtures
- a `vdl-plugin` CLI for type-checking and bundling plugins
- TypeScript configuration presets for app and test code
Minimal SDK plugin:
```ts
import { definePlugin } from "@varavel/vdl-plugin-sdk";
export const generate = definePlugin((input) => {
return {
files: [
{
path: "hello.txt",
content: `Hello from VDL ${input.version}\n`,
},
],
};
});
```
> **How exports work:** SDK plugins use `export const generate`, not `exports.generate`. The `vdl-plugin build` command converts the ESM export into `exports.generate` automatically when bundling into `dist/index.js`. The final artifact that VDL loads always uses the CommonJS `exports.generate` form.
Check and build:
```bash
npx vdl-plugin check
npx vdl-plugin build
```
`check` runs TypeScript without emitting files. `build` bundles the plugin into `dist/index.js`, which is the artifact VDL consumes for GitHub shorthand plugins.
## Starting From The Template
The fastest way to start a production-ready plugin is [`varavelio/vdl-plugin-template`](https://github.com/varavelio/vdl-plugin-template).
The template includes:
- `src/index.ts` with a minimal `definePlugin(...)` entrypoint
- the VDL plugin SDK
- TypeScript config
- `@varavel/gen` for structured text/code generation
- Biome and dprint for linting and formatting
- Vitest for tests
- a devcontainer
- a GitHub Actions CI workflow
- `dist/index.js` as the distributable plugin bundle
Basic workflow:
```bash
npm install
npm run check
npm run build
npm run test
```
When releasing a GitHub-hosted plugin:
1. Build with `npm run build`.
2. Commit source files and `dist/index.js`.
3. Create a release tag such as `v0.1.0`.
4. Users can reference `owner/vdl-plugin-name@v0.1.0` from `vdl.config.vdl`.
VDL plugins do not need to be published to npm. The GitHub release plus committed `dist/index.js` is enough for VDL to fetch the plugin.
## Error Handling
Return structured errors when the user's schema or options are invalid.
```ts
import { definePlugin, fail } from "@varavel/vdl-plugin-sdk";
export const generate = definePlugin((input) => {
const root = input.options.root;
if (!root) {
fail('Missing required option "root".');
}
const typeDef = input.ir.types.find((item) => item.name === root);
if (!typeDef) {
fail(`Unknown root type "${root}".`);
}
return {
files: [{ path: "root.txt", content: `${typeDef.name}\n` }],
};
});
```
For schema-specific errors, attach a source position when possible. VDL will print the diagnostic with file, line, and column information.
## Testing Plugins
The SDK testing entry point helps you build plugin inputs without manually writing a full IR object.
```ts
import {
field,
objectType,
pluginInput,
primitiveType,
schema,
typeDef,
} from "@varavel/vdl-plugin-sdk/testing";
const input = pluginInput({
options: { root: "User" },
ir: schema({
types: [
typeDef("User", objectType([field("id", primitiveType("string"))])),
],
}),
});
```
Pass the input to your plugin handler and assert generated files or returned errors.
## Practical Checklist
- Validate plugin options before generating output.
- Validate annotation models before assuming a shape such as RPC or events.
- Generate deterministic output so diffs stay clean.
- Keep generated file paths relative to `outDir`.
- Return structured errors for user mistakes.
- Use the SDK for TypeScript, typed IR access, bundling, and tests.
- Commit `dist/index.js` for GitHub-hosted plugins.
- Pin plugin versions in consuming projects.
---
Source: https://vdl.varavel.com/docs/reference/
Markdown: https://vdl.varavel.com/docs/reference/index.md
Content file: content/docs/reference/_index.md
# Reference
> Reference material for VDL language behavior, formatting, annotations, RPCs, and events.
Canonical HTML: https://vdl.varavel.com/docs/reference/
Markdown: https://vdl.varavel.com/docs/reference/index.md
Reference pages define the exact behavior of the VDL language, formatter, and official annotation models.
---
Source: https://vdl.varavel.com/docs/reference/spec/
Markdown: https://vdl.varavel.com/docs/reference/spec/index.md
Content file: content/docs/reference/spec.md
# Language Specification
> Varavel Definition Language (VDL) specification.
Canonical HTML: https://vdl.varavel.com/docs/reference/spec/
Markdown: https://vdl.varavel.com/docs/reference/spec/index.md
## Overview
VDL is a schema-first IDL for defining typed contracts that drive code generation across multiple targets. A VDL document is built from a compact core of declarations and extended through annotations.
The language is designed to keep parsing stable while allowing domain semantics to evolve without changing the core grammar.
## Language Model
The parser recognizes three declaration forms at the top level: `type`, `const`, and `enum`. Includes and docstrings are also valid top-level nodes.
Domain semantics are expressed with annotations. An annotation can be attached to declarations, fields, and enum members where supported by grammar.
## Core Syntax
```vdl
include "./shared.vdl"
""" Standalone documentation """
@tag
@meta({ owner "core" })
type User {
""" Field documentation """
id string
email? string
}
@config
const maxPageSize = 100
enum Status {
Active
Disabled
Archived = "archived"
}
```
## Comments
VDL supports both single-line and multi-line comments.
```vdl
// A single-line comment
/*
A multi-line comment
that can span multiple lines
*/
type Example {
// Comments can appear everywhere
value string
}
```
Comments are ignored by the parser and have no effect on schema semantics, generated code, or runtime behavior.
## Naming Conventions
VDL enforces naming conventions through formatter and tooling to keep generated code predictable.
| Element | Convention | Example |
| :-------------- | :----------- | :--------------------------- |
| Types and Enums | `PascalCase` | `UserProfile`, `OrderStatus` |
| Enum Members | `PascalCase` | `Pending`, `InProgress` |
| Type Members | `camelCase` | `userId`, `createdAt` |
| Constants | `camelCase` | `maxPageSize`, `apiVersion` |
| Annotations | `camelCase` | `rpc`, `internal` |
## Includes
Includes compose schemas across files.
```vdl
// auth.vdl
type Session {
token string
expiresAt datetime
}
// main.vdl
include "./auth.vdl"
type AuthInfo {
session Session
}
```
Included files are resolved by relative path from the current file. Definitions are loaded into the same compilation context, and each file is processed once.
## Declarations
### Type Declarations
`type` defines reusable structured data.
```vdl
type Product {
id string
title string
price float
tags? string[]
}
```
Type bodies accept field members (`[?] `) and spread members (`...`). Field members can have attached docstrings and annotations.
Type spreads reference complete type declaration names.
```vdl
type AuditMetadata {
createdAt datetime
updatedAt datetime
}
type Article {
...AuditMetadata
title string
content string
}
```
### Constant Declarations
`const` defines immutable values.
```vdl
const apiVersion = "1.2.0"
const maxRetries = 5
const featureEnabled = true
```
Constants are dynamic values:
```vdl
const timeoutMs = 2500
const serviceName = "billing"
```
### Enum Declarations
`enum` defines named finite sets. Members can be implicit string values, explicit strings, or explicit integers, as long as the enum remains type-consistent.
```vdl
enum OrderStatus {
Pending
Processing
Delivered
}
enum Priority {
Low = 1
Medium = 2
High = 3
}
```
Enum bodies support spread entries:
```vdl
enum AllRoles {
...StandardRoles
Admin = "admin"
}
```
Enum spreads reference complete enum declaration names.
Enum members also support docstrings and annotations on named members.
## Type System
### Primitive Types
| Type | JSON Equivalent | Description |
| :--------- | :-------------- | :------------------------- |
| `string` | string | UTF-8 encoded text |
| `int` | integer | 64-bit signed integer |
| `float` | number | 64-bit floating point |
| `bool` | boolean | Logical value (true/false) |
| `datetime` | string | ISO 8601 date-time string |
### Arrays
Arrays are written with `[]` suffix.
```vdl
type Example {
ids string[]
matrix int[][]
}
```
### Maps
Maps use bracket syntax.
```vdl
type Metrics {
counters map[int]
usersById map[User]
}
```
### Inline Object Types
Inline objects are anonymous structured types.
```vdl
type LocationEnvelope {
location {
latitude float
longitude float
}
}
```
## Annotations
Annotations are metadata nodes with optional argument values.
```vdl
@flag
@meta({ owner "platform" tier "gold" })
type User {
@id
id string
}
```
Annotation syntax:
```vdl
@name
@name()
```
Grammar supports annotation attachment on `type`, `const`, and `enum` declarations, on fields inside type members, and on named enum members.
Multiple annotations are allowed.
## Data Literals
Data literals are used by `const` values and annotation arguments.
### Scalar Literals
```vdl
"text"
42
3.14
true
false
defaultTimeout // Constant reference
Color.Red // Enum member reference
```
### Object Literals
Object entries are space-separated key/value pairs. Commas are not part of the syntax.
```vdl
const appConfig = {
host "localhost"
port 8080
tls true
}
```
Object spreads are valid and reference complete constants by name.
```vdl
const prodConfig = {
...baseConfig
port 443
}
```
### Array Literals
Array elements are also space-separated.
```vdl
const roles = ["admin" "editor" "viewer"]
const retryBackoffMs = [100 250 500 1000]
```
All elements in a data literal array must be of the same type.
## References
References are identifier-based values used in spreads and scalar literals.
```vdl
const defaultStatus = Status.Active
const retryLimit = maxRetries
type Session {
...BaseSession
}
```
A reference supports `Name` and `Name.Member` forms.
Spread members use the `Name` form. `Name.Member` is reserved for enum member references in constant values and constant data literals.
## Documentation
### Docstrings
Docstrings can be attached to declarations and members or used as standalone documentation nodes.
```vdl
"""
Represents a user in the system.
"""
type User {
""" Unique user identifier. """
id string
}
```
A blank line separates a standalone docstring from the next declaration in the same scope.
Inside type bodies, docstrings attach to the following field. Enum bodies attach docstrings to the following named member. Type and enum bodies do not support docstring-only entries.
### External Documentation Files
A docstring containing only a relative `.md` path is treated as an external documentation reference.
```vdl
""" ./docs/welcome.md """
""" ./docs/user.md """
type User {
id string
}
```
Paths are resolved relative to the `.vdl` file containing the docstring.
## Complete Example
```vdl
include "./common/types.vdl"
""" ./docs/catalog.md """
const apiVersion = "1.0.0"
const defaultStatus = ProductStatus.Draft
enum ProductStatus {
Draft
Published
Archived = "archived"
}
type Product {
...Entity
id string
name string
price float
status ProductStatus
tags string[]
}
type ProductPage {
items Product[]
total int
}
const defaultProduct = {
...baseProduct
name "Sample"
price 9.99
status ProductStatus.Draft
}
const sampleTags = ["featured" "popular" "seasonal"]
```
## Related Specifications
- RPC modeling and request lifecycle are documented in [RPC Annotations](/docs/reference/rpc/).
- Event annotation rules are documented in [Event Annotations](/docs/reference/events/).
- Deprecation annotation rules are documented in [Deprecation Annotations](/docs/reference/deprecations/).
---
Source: https://vdl.varavel.com/docs/reference/formatting-guide/
Markdown: https://vdl.varavel.com/docs/reference/formatting-guide/index.md
Content file: content/docs/reference/formatting-guide.md
# Formatting Guide
> Standard formatting conventions for VDL.
Canonical HTML: https://vdl.varavel.com/docs/reference/formatting-guide/
Markdown: https://vdl.varavel.com/docs/reference/formatting-guide/index.md
This guide defines how to keep VDL files easy to read, stable in diffs, and predictable across teams.
> **Note:** These conventions are enforced by the official formatter. Run `vdl format ./schema.vdl` or use editor format-on-save through the VDL LSP.
## 1. General Principles
- Format for consistency, not personal style.
- Prefer the smallest readable layout.
- Keep related items close; separate unrelated items with one blank line.
- Use UTF-8, LF (`\n`), no trailing whitespace, and a final newline.
## 2. Indentation
- Use **2 spaces** per indentation level.
- Do not use tabs.
```vdl
type Example {
field string
}
```
## 3. Top-Level Elements
Top-level elements are `include`, `type`, `const`, `enum`, comments, and standalone docstrings.
- Group `include` statements at the top with no blank lines inside the group.
- Use one blank line between top-level declarations.
- Avoid multiple consecutive blank lines.
```vdl
include "./common.vdl"
include "./auth.vdl"
const maxPageSize = 100
type Order {
id string
total float
}
enum OrderStatus {
Pending
Paid
}
```
## 4. Blocks and Fields
### 4.1 Block Structure
- Put `{` on the same line as the declaration, with one space before it.
- Put block members on their own lines.
- Align `}` with the declaration start.
```vdl
type User {
id string
name string
}
```
### 4.2 Field Members
- Keep one member per line.
- Prefer no blank lines between plain fields.
- Add one blank line around field docstrings when it improves scanning.
- Place spreads before regular fields when possible.
```vdl
type User {
...AuditMetadata
""" The user's display name. """
name string
""" The user's email address. """
email string
age? int
}
```
### 4.3 Annotation Placement
Annotations carry domain semantics. Keep placement strict and vertical for readability.
- Put annotations directly above the declaration or member they target.
- Use one annotation per line.
- Keep docstring above annotations when both are present.
- Keep a single blank line between logical groups of annotated members.
```vdl
"""
User operations.
"""
@rpc
type Users {
@proc
GetUser {
input {
userId string
}
output {
id string
email string
}
}
@stream
UserEvents {
input {
userId string
}
output {
event string
at datetime
}
}
}
```
## 5. Spacing
| Context | Rule | Example |
| :--------------------- | :-------------------------------- | :------------------------ |
| Field syntax | `[?] ` | `email? string` |
| Assignment | One space around `=` | `const retries = 3` |
| Braces (`{}`) | One space before `{` | `type User {` |
| Arrays (`[]`) | No spaces inside or before suffix | `string[]`, `int[][]` |
| Maps (`map[...]`) | No spaces inside brackets | `map[int]`, `map[User]` |
| Annotation call | No space before `(` | `@meta({ owner "core" })` |
| Object literal entries | Space-separated key/value | `{ host "localhost" }` |
| Array literal entries | Space-separated elements | `["a" "b" "c"]` |
| Enum member assignment | One space around `=` | `Archived = "archived"` |
## 6. Comments
VDL supports two comment styles, and formatter preserves comment content exactly as written.
- **Single-line:** `// ...`
- **Multi-line:** `/* ... */`
- Comments can appear at top level or inside blocks.
```vdl
// Top-level single-line comment
/*
Top-level multi-line comment
*/
type Example {
// Inside block
field string
}
```
## 7. Docstrings
- Place docstrings immediately above the element they document.
- Enclose in triple quotes (`"""`), preserving internal newlines and formatting.
- Prefer concise, purpose-first text.
- Use a blank line after a standalone docstring node.
```vdl
"""
Represents a user in the system.
"""
type User {
""" The unique identifier. """
id string
""" The user's full name. """
name string
}
```
## 8. Naming Conventions
The formatter automatically enforces the following naming conventions:
| Element | Convention | Example |
| :-------------- | :----------- | :--------------------------- |
| Types and Enums | `PascalCase` | `UserProfile`, `OrderStatus` |
| Enum Members | `PascalCase` | `Pending`, `InProgress` |
| Type Members | `camelCase` | `userId`, `createdAt` |
| Constants | `camelCase` | `maxPageSize`, `apiVersion` |
| Annotations | `camelCase` | `rpc`, `proc`, `deprecated` |
### Acronym Handling
Acronyms longer than two letters are treated as regular words:
```vdl
// Correct
type HttpRequest { ... }
type JsonParser { ... }
// Incorrect
type HTTPRequest { ... }
type JSONParser { ... }
```
---
Source: https://vdl.varavel.com/docs/reference/deprecations/
Markdown: https://vdl.varavel.com/docs/reference/deprecations/index.md
Content file: content/docs/reference/deprecations.md
# Deprecation Annotations
> Marking declarations and members as deprecated with @deprecated.
Canonical HTML: https://vdl.varavel.com/docs/reference/deprecations/
Markdown: https://vdl.varavel.com/docs/reference/deprecations/index.md
## Overview
Deprecation in VDL is expressed with the `@deprecated` annotation.
The annotation can be used as a flag or with a message argument to provide migration guidance.
## Syntax
```vdl
@deprecated
type LegacyUser {
id string
}
@deprecated("Use userProfile")
const userSummaryType = "legacy"
```
## Allowed Targets
Deprecation can be attached anywhere annotations are valid in grammar.
- Top-level declarations: `type`, `const`, `enum`
- Type fields
- Named enum members
## Examples
```vdl
@deprecated("Use AccountV2")
type Account {
id string
@deprecated("Use primaryEmail")
email string
}
enum Status {
Active
@deprecated("Use Disabled")
Inactive
}
@deprecated
const legacyTimeoutMs = 12000
```
## Tooling Expectations
Generators and tooling can surface deprecations in generated code, diagnostics, and documentation output.
Deprecation metadata is informational by default and does not remove symbols from schema validity on its own.
---
Source: https://vdl.varavel.com/docs/reference/rpc/
Markdown: https://vdl.varavel.com/docs/reference/rpc/index.md
Content file: content/docs/reference/rpc.md
# RPC Annotations
> Modeling RPC services with annotations and full request lifecycle behavior.
Canonical HTML: https://vdl.varavel.com/docs/reference/rpc/
Markdown: https://vdl.varavel.com/docs/reference/rpc/index.md
## Overview
RPC services in VDL are modeled with annotations on core language declarations.
`@rpc` marks a service container on a `type` declaration. `@proc` and `@stream` mark service operations on type members.
## Schema Modeling
```vdl
@rpc
type Users {
@proc
getUser {
input {
userId string
}
output {
id string
email string
}
}
@stream
userEvents {
input {
userId string
}
output {
event string
at datetime
}
}
}
```
The RPC model uses these structural rules.
- `@rpc` is attached to a `type` declaration.
- `@proc` and `@stream` are attached to members inside the `@rpc` type.
- Operation members are regular fields with inline object types.
- `input` and `output` are regular field names whose types are inline objects.
Because `input` and `output` are inline object fields, they support standard language features, including spreads and docstrings.
## Request Lifecycle
This section defines the end-to-end data flow for both procedure calls and stream subscriptions. It covers URL structure, JSON payloads, connection behavior, and error handling. The behavior is language-agnostic and applies to official generators.
## Procedures (Request-Response)
Procedures follow a request-response model over HTTP and complete within a single HTTP transaction.
### 1. Client Invocation
Application code invokes a generated procedure client function. The generated client builds the HTTP request.
### 2. HTTP Request
The client sends an HTTP `POST` request.
- **Method:** `POST`
- **URL Structure:** `//`
- Example: `https://api.example.com/rpc/Users/getUser`
- **Headers:**
- `Content-Type: application/json`
- `Accept: application/json`
- **Body:** JSON-encoded procedure input.
```json
{
"userId": "user-123"
}
```
### 3. Server Handling
The server processes a procedure request in three stages.
1. **Routing:** The URL path is mapped to an RPC service and procedure handler.
2. **Deserialization and Validation:** The JSON body is decoded and validated.
3. **Handler Execution:** The user-defined handler executes with validated input.
VDL middleware hooks can run custom logic such as authentication, authorization, validation, logging, and metrics.
### 4. Server Response
The handler returns either output data or error data. The server serializes the result into a JSON envelope.
### 5. HTTP Response
The server returns one HTTP response.
- **Status Code:** `200 OK` for successfully processed requests. Application-level success or failure is represented by the response envelope.
- **Headers:** `Content-Type: application/json`
- **Body:**
Success envelope:
```json
{
"ok": true,
"output": {
"id": "user-123",
"email": "john.doe@example.com"
}
}
```
Failure envelope:
```json
{
"ok": false,
"error": {
"message": "User not found.",
"category": "NotFound",
"code": "USER_NOT_FOUND",
"details": {
"userId": "user-123"
}
}
}
```
`error` fields:
- `message` (`string`): human-readable description
- `category` (`string`, optional): error class
- `code` (`string`, optional): machine-readable code
- `details` (`object`, optional): structured context
### 6. Client Handling
The generated client decodes the envelope and returns output or error to application code.
1. **Deserialization:** parse JSON body.
2. **Envelope handling:**
- `ok: true` returns `output`.
- `ok: false` returns `error`.
3. **Resilience:** transport failures can use retry policies such as exponential backoff, depending on client configuration.
## Streams (Server-Sent Events)
Streams use Server-Sent Events (SSE) with a persistent connection so servers can push multiple events over time.
### 1. Client Subscription
Application code subscribes through the generated stream client.
### 2. HTTP Request (Connection)
The client opens the stream with a `POST` request.
- **Method:** `POST`
- **URL Structure:** `//`
- Example: `https://api.example.com/rpc/Chat/NewMessage`
- **Headers:**
- `Accept: text/event-stream`
- `Content-Type: application/json`
- **Body:** JSON-encoded stream input.
```json
{
"chatId": "room-42"
}
```
### 3. Server Handling (Connection)
The server validates input and initializes the SSE channel.
1. **Validation:** invalid input returns a single JSON error response.
2. **Connection setup:**
- Status `200 OK`
- `Content-Type: text/event-stream`
- `Cache-Control: no-cache`
- `Connection: keep-alive`
3. **Handler execution:** stream handler runs with an `emit` function.
Middleware hooks are available for cross-cutting behavior in stream flows.
### 4. Event Emission
Handlers call `emit` to push events. Each emitted event is serialized and written as SSE `data`.
- JSON payloads must be single-line for SSE framing safety.
- Newline-sensitive formatting is handled before transmission.
Success event:
```
data: {"ok":true,"output":{"messageId":"msg-abc","text":"Hello world!"}}
```
Error event:
```
data: {"ok":false,"error":{"message":"You do not have permission to view this chat."}}
```
### 5. Keep-Alive (Ping Events)
Servers send periodic SSE comments to keep connections alive.
- **Format:** `: ping\n\n`
- **Interval:** server-configurable (commonly 30 seconds)
- **Client behavior:** ignored by generated clients and not surfaced to application code
### 6. Client Handling (Event Loop)
The client processes stream events continuously.
1. Parse SSE frames.
2. Ignore comment ping frames.
3. Deserialize JSON payloads.
4. Deliver `output` or `error` envelopes to application code.
### 7. Stream Termination
A stream can end by client cancellation, handler completion, or network interruption.
Generated clients may reconnect automatically with configurable retry behavior, re-sending the original subscription request when applicable.
---
Source: https://vdl.varavel.com/docs/reference/events/
Markdown: https://vdl.varavel.com/docs/reference/events/index.md
Content file: content/docs/reference/events.md
# Event Annotations
> Modeling asynchronous events, routing subjects, and strict payloads with the @event annotation.
Canonical HTML: https://vdl.varavel.com/docs/reference/events/
Markdown: https://vdl.varavel.com/docs/reference/events/index.md
## Overview
Event-driven architectures in VDL are modeled using the `@event` annotation.
The `@event` annotation binds a dynamic routing subject to a strongly-typed data structure (the payload). This establishes a universal, transport-agnostic contract that can be seamlessly implemented across message brokers like NATS, RabbitMQ, Kafka, HTTP Webhooks, etc.
## Syntax
The `@event` annotation takes a string argument representing the routing subject, and it must be attached to an `object type` declaration.
```vdl
@event("auth.user_created.{userId}")
type UserCreatedEvent {
userId string
email string
timestamp datetime
}
```
## Template and Validation Rules
The routing subject is a string literal that can contain dynamic placeholders in `{fieldName}` format.
To ensure events are fully self-contained and agnostic to the underlying network transport, VDL enforces strict schema validation on event placeholders:
1. **Explicit Field Binding:** Every placeholder used in the `@event` subject **must** exactly match a field defined within the annotated `type`.
2. **Type Safety:** If a subject references `{tenantId}`, the `tenantId` field must exist in the type definition so its data type is statically known.
3. **Repeated Placeholders:** The same placeholder can appear multiple times in the subject. Subject builders should accept that field once and reuse the same value in every occurrence.
4. **Primitive, Top-Level Placeholders Only:** Placeholders must reference top-level primitive fields. Nested paths (for example `{user.id}`) and non-primitive fields (such as objects, arrays, or maps) are not allowed.
```vdl
// VALID: 'orderId' exists in the payload
@event("orders.status_changed.{orderId}")
type OrderStatusChanged {
orderId string
status string
}
// INVALID: 'region' is used in the subject but missing from the payload
@event("logistics.{region}.delivery_failed")
type DeliveryFailed {
deliveryId string
}
// VALID: repeated placeholder uses the same field value
@event("audit.{tenantId}.users.{tenantId}.created")
type UserAuditCreated {
tenantId string
}
// INVALID: nested placeholder path is not allowed
@event("accounts.{user.id}.created")
type AccountCreated {
user object {
id string
}
}
```
This strict validation guarantees that when an event is persisted to a database or forwarded via a protocol without native routing headers (like HTTP), the payload retains 100% of the context required to identify it.
## Generated API Shape
Generators transform an `@event` declaration into primary components: the payload data structure and the subject formatting utilities. Depending on the target, generators may also emit a centralized event catalog.
Subject builders always return a `string`. If a placeholder field uses a non-string primitive type, subject builders must convert that value to string when composing the final subject.
### TypeScript Shape
```ts
// 1. The Payload Type
export interface UserCreatedEvent {
userId: string;
email: string;
timestamp: string;
}
// 2. The Subject Formatter
export function buildUserCreatedEventSubject(userId: string): string {
return `auth.user_created.${userId}`;
}
```
### Go Shape
```go
// 1. The Payload Struct
type UserCreatedEvent struct {
UserId string `json:"userId"`
Email string `json:"email"`
Timestamp time.Time `json:"timestamp"`
}
// 2. The Subject Formatter
func BuildUserCreatedEventSubject(userId string) string {
return "auth.user_created." + userId
}
```
### Event Catalogs
Plugins should aggregate `@event` declarations into a centralized catalog (e.g., a map or dictionary) to facilitate runtime event routing. The catalog metadata must expose the literal routing string strictly under the `Subject` key.
```go
// Conceptual Catalog Shape
type EventMetadata struct {
Name string // e.g., "UserCreatedEvent"
Subject string // e.g., "auth.user_created.{userId}"
}
```