Glue
Glue is a compact Interface Definition Language (IDL) and toolchain for modeling data structures and API interfaces once, then generating useful artifacts for multiple ecosystems. Use Glue when you need a clear source of truth for models, enums, type aliases, API endpoints, OpenAPI, JSON Schema, Protobuf, or language-native types.
Canonical References
Prefer current public docs over memorized syntax when exact behavior matters:
- LLM-ready docs: https://gluelang.dev/llms.txt
- Documentation site: https://gluelang.dev/docs
- Quickstart: https://gluelang.dev/docs/quickstart
- Language reference: https://gluelang.dev/docs/language-reference
- CLI reference: https://gluelang.dev/docs/cli-reference
- Configuration: https://gluelang.dev/docs/configuration
- Code generation docs: https://gluelang.dev/docs/codegen-typescript, https://gluelang.dev/docs/codegen-python, https://gluelang.dev/docs/codegen-rust, https://gluelang.dev/docs/codegen-go, https://gluelang.dev/docs/codegen-openapi, https://gluelang.dev/docs/codegen-jsonschema, https://gluelang.dev/docs/codegen-protobuf
- Repository: https://github.com/guywaldman/glue
- Repository docs folder: https://github.com/guywaldman/glue/tree/main/docs
- Source docs files: https://github.com/guywaldman/glue/blob/main/docs/04-language-reference.mdx, https://github.com/guywaldman/glue/blob/main/docs/02-cli-reference.mdx, https://github.com/guywaldman/glue/blob/main/docs/03-configuration.mdx
- Examples: https://github.com/guywaldman/glue/tree/main/examples
- Config schema source: https://github.com/guywaldman/glue/blob/main/glue/assets/config_schema.glue
Agent Workflow
- Clarify the domain boundary: identify the nouns, stable identifiers, enum vocabularies, request/response shapes, and API operations.
- Write the smallest useful
*.gluefile first. Add config only when output paths, generator options, or multiple targets matter. - Prefer explicit, reusable named models over deeply inlined anonymous structures. Reuse type aliases for IDs, timestamps, and repeated primitives.
- Validate with
glue check <file>when the CLI is available. - Generate with
glue gen <target> -i <file> -o <output>, or useglue gen/glue gen --config .gluerc.yamlfor config-driven generation. You may also useglue gen <target> -i <file> -o -to print to stdout when the output is small or for debugging. - Only when you absolutely must dive deeper, inspect the AST with
glue ast <file>. - When a user asks for a target-specific artifact, consult the target docs or
https://gluelang.dev/llms.txtbefore relying on memory. - If the source uses imports, keep imports at the top of the file and resolve relative paths from the importing file.
Core Syntax
- Comments:
// line comment; doc comments:/// documentation. - Primitive types:
string,int,uint,i8,i16,i32,i64,u8,u16,u32,u64,float,bool,any. - Arrays:
Type[]. - Tuples:
(TypeA, TypeB). - Maps:
Record<KeyType, ValueType>. - Optional fields:
field?: Type. - Defaults:
field: Type = value. - Union types:
string | string[]. - Type aliases:
type UserId = string. - Codegen emits public type aliases where suitable: TypeScript
export type, Rust tuple newtypes, Gotype Name = ..., and PythonTypeAlias. Aliases starting with_are private and inlined. - Constants:
const MAX_PAGE_SIZE = 100; optional annotations useconst MAX_PAGE_SIZE: int = 100. Constants can be top-level or declared inside models. - Model-scoped constants: public constants can be referenced as
Model.CONSTANTorOuter.Inner.CONSTANT; leading_constants cannot be referenced from outside their owning model. - Constant expressions support int
+/*, string+, parentheses, bool literals, and references to other constants, including qualified model constants. - Models:
model User { id: UserId }. - Model, anonymous model, endpoint, service, and RPC block members may be separated with commas for compact one-line shapes, e.g.
model User { id: string, email?: string }. - Enums:
enum Status: "pending" | "paid" | "cancelled". - Imports must appear before declarations:
import * from "common.glue"import * as common from "common.glue"import { User, Address as PostalAddress } from "domain.glue"
- Field metadata can use
@field(alias="external_name", example="value", proto_tag=1).aliasandproto_tagcan use constants, e.g.@field(alias=Aliases.USER_ID_ALIAS, proto_tag=Tags.USER_ID). - Endpoints use
endpoint "METHOD /path/{param}" Name { ... }with optionalbody,headers, andresponses. - Protobuf services use
service Name { rpc Method { body: Request returns: Response } }.
Supported generation targets are typescript, python, rust, go, openapi, jsonschema, and protobuf.
Modeling Guidelines
- Model domain concepts, not one target language. Avoid naming or structuring fields around a single framework unless the user explicitly asks.
- Use stable aliases for IDs and repeated scalar concepts:
type UserId = string,type IsoTimestamp = string,type MoneyCents = int. - Use enums for closed vocabularies. Keep enum values meaningful wire values, usually lowercase strings.
- Make optionality intentional. Use
?for data that may be absent; use defaults for values the producer can safely omit. - Avoid global namespace pollution and leverage nested models, enums, aliases, and constants to keep related concepts together. For example, if
Orderis the only model that usesOrderStatus, nestingOrderStatuswithinOrderkeeps the domain tidy and makes it clear that the enum is specific to orders. - Keep endpoint payloads named. Prefer
CreateOrderRequestandOrderResponseover anonymous ad hoc endpoint-only models. - Use
Record<string, T>for keyed objects and dictionaries; use arrays for ordered lists. - Keep imports simple and acyclic. Split files by domain only when the resulting boundaries are obvious.
- Document public models and important fields with
///. Good doc comments improve generated schemas and help downstream agents. Internal comments are supported with//and are stripped from generated output, so they are ideal for implementation notes and reminders that should not be exposed downstream. - Treat Glue as the source of truth. When editing generated code, consider whether the change belongs in the
.gluemodel instead. - Validate early. A small
glue checkpass is cheaper than debugging generated TypeScript, Python, or OpenAPI later.
Protobuf Guidance
Glue Protobuf generation emits proto3 files from models, enums, and services.
- Plain model fields do not need annotations; Protobuf field tags default to source order starting at
1. - Enum constants are generated in
CONSTANT_CASEfrom Glue string values. - Use
@field(proto_tag=<number>)when the schema needs stable wire compatibility across field reordering. If one field in a model hasproto_tag, every field in that model must have one. - Optional scalar and message fields generate
optionalProtobuf fields. Optional repeated and map fields are accepted, but Protobuf has no presence for those shapes, so they emit as regularrepeatedandmapfields. Record<K, V>emitsmap<K, V>for supported Protobuf key/value types. Anonymous structs emit generated messages named from the owning field path.- Unions emit
oneofonly when every member is valid inside a Protobufoneof; repeated fields, maps, optional members, and explicitproto_tagunions are rejected with an error. - Services use
serviceandrpcdeclarations withbodyandreturns; both should be message types. HTTPendpointdeclarations are ignored by the Protobuf generator.
model GetUserRequest {
@field(proto_tag=1)
user_id: int
}
model User {
@field(proto_tag=1)
user_id: int
@field(proto_tag=2)
display_name?: string
@field(proto_tag=3)
tags?: string[]
}
service UserService {
rpc GetUser {
body: GetUserRequest
returns: User
}
}
Current Limitations
Do not invent unsupported syntax. As of the current docs, notable limitations include:
- No generics such as
model Response<T> { data: T }. - No intersection types such as
type A = B & C. - Endpoint path/query parameter typing and auth scheme modeling are limited compared with full OpenAPI.
When these areas matter, keep the Glue model simple and use generated OpenAPI or target-language code as the integration point for additional framework-specific detail.
CLI Patterns
# Validate one file.
glue check api.glue
# Generate one target from one file.
glue gen typescript -i api.glue -o generated/api.ts
glue gen python -i api.glue -o generated/api.py
glue gen openapi -i api.glue -o openapi.yaml
# Generate from stdin or a URL.
cat api.glue | glue gen jsonschema -o schema.json
glue gen go https://raw.githubusercontent.com/guywaldman/glue/refs/heads/main/examples/basic.glue -o generated/models.go
# Use auto-discovered .gluerc / .gluerc.yaml, or pass one explicitly.
glue gen
glue gen --config .gluerc.yaml
# Override config inline.
glue gen typescript -i api.glue -o generated/api.ts --set "watermark=none"
Config Pattern
Prefer .gluerc.yaml when a project generates multiple targets:
# yaml-language-server: $schema=https://raw.githubusercontent.com/guywaldman/glue/main/glue/assets/config_schema.json
global:
output_base_dir: generated
diagnostics:
suppress_warnings:
- constant_case
config:
watermark: short
lint_suppressions: true
gen:
- mode: typescript
files: "schemas/*.glue"
output: "ts/{file_name}.{file_ext}"
config_overrides:
typescript:
zod: true
- mode: python
files:
- "schemas/*.glue"
- "shared/*.glue"
output: "py/{file_name}.{file_ext}"
config_overrides:
python:
data_model_library: pydantic
- mode: openapi
files: "schemas/api.glue"
output: "openapi/{file_name}.{file_ext}"
Documented Configuration Example
Use this pattern when a project has one or more .glue files and wants repeatable generation for several consumers. The config below assumes source files live under schemas/ and writes all generated files under generated/.
# .gluerc.yaml
#
# The schema line gives editors autocomplete and validation for Glue config.
# It is optional, but highly recommended for humans and agents.
# yaml-language-server: $schema=https://raw.githubusercontent.com/guywaldman/glue/main/glue/assets/config_schema.json
global:
# Every gen entry writes relative to this directory unless its output is absolute.
output_base_dir: generated
# Defaults applied to every generation entry before per-entry overrides.
config:
# Watermark modes: full, short, none.
watermark: short
# Useful for generated code that intentionally has names or imports that
# linters might otherwise complain about.
lint_suppressions: true
# TypeScript defaults. Individual entries can override these.
typescript:
zod: false
# Python can generate pydantic, dataclasses, attrs, or msgspec models.
python:
data_model_library: pydantic
base_model: pydantic.BaseModel
# Rust can opt into extra derives by generated shape.
rust:
extra_derives:
structs: ["PartialEq"]
enums: ["Ord", "PartialOrd"]
unions: ["PartialEq"]
type_aliases: ["Ord", "PartialOrd"]
# Go packages usually need a stable package name.
go:
package_name: commerce
# Protobuf package name for generated .proto files.
protobuf:
package_name: commerce.v1
gen:
# Generate TypeScript runtime schemas for every Glue file.
- mode: typescript
files:
- "schemas/*.glue"
output: "typescript/{file_name}.{file_ext}"
config_overrides:
typescript:
zod: true
# Generate Python models from the same source files, but choose dataclasses
# for this target instead of the global pydantic default.
- mode: python
files:
- "schemas/*.glue"
output: "python/{file_name}.{file_ext}"
config_overrides:
python:
data_model_library: dataclasses
# Generate a single OpenAPI document from the API-facing Glue file.
- mode: openapi
files:
- "schemas/commerce_api.glue"
output: "openapi/commerce.{file_ext}"
# Generate Protobuf messages for model-only schemas.
- mode: protobuf
files:
- "schemas/models.glue"
output: "protobuf/{file_name}.{file_ext}"
Run it with:
# Auto-discover .gluerc.yaml from the current/input directory.
glue gen
# Or choose a config explicitly.
glue gen --config .gluerc.yaml
# Inline overrides are useful in CI or one-off local generation.
glue gen --config .gluerc.yaml --set "watermark=none"
Comprehensive Example
The following single-file example demonstrates the most important Glue features in a compact domain. It is intentionally heavily documented so agents can copy patterns from it.
// commerce.glue
//
// Glue files are meant to be the source of truth for data contracts.
// This example models a small commerce API that could generate TypeScript
// types, Python models, Rust structs, Go structs, OpenAPI, JSON Schema, or
// Protobuf from the same IDL.
/// Reusable scalar aliases keep domain intent visible in every model.
/// They currently alias primitive types, but the names carry semantic meaning.
type UserId = string
type OrderId = string
type ProductId = string
type IsoTimestamp = string
type MoneyCents = int
/// String enums are ideal for closed vocabularies and wire-level states.
/// Use values that should appear in JSON/OpenAPI payloads.
enum OrderStatus: "draft" | "submitted" | "paid" | "fulfilled" | "cancelled"
enum CurrencyCode: "USD" | "EUR" | "GBP"
/// A user account in the commerce system.
model User {
/// Stable unique identifier, reused across endpoints and related models.
id: UserId
/// Field metadata can provide generator hints such as aliases and examples.
/// Here the generated wire/schema name can be `email`.
@field(alias="email", example="ada@example.com")
email_address: string
/// Optional fields use `?`; this may be absent from incoming payloads.
display_name?: string
/// Defaults describe safe producer defaults and can be reflected downstream.
active: bool = true
/// Arrays use the `Type[]` suffix.
roles: string[]
/// Nested models are useful for concepts owned by exactly one parent type.
profile: Profile
model Profile {
created_at: IsoTimestamp
marketing_opt_in: bool = false
}
}
/// A product that can be ordered.
model Product {
id: ProductId
sku: string
name: string
active: bool = true
/// Records model dictionaries/maps. Prefer string keys for JSON-like maps.
attributes?: Record<string, string>
}
/// Money is modeled explicitly so amount and currency do not drift apart.
model Money {
amount_cents: MoneyCents
currency: CurrencyCode = "USD"
}
/// A single line item within an order.
model OrderItem {
product_id: ProductId
quantity: int = 1
unit_price: Money
}
/// The aggregate order model reused by multiple API responses.
model Order {
id: OrderId
user_id: UserId
status: OrderStatus = "draft"
items: OrderItem[]
total: Money
created_at: IsoTimestamp
submitted_at?: IsoTimestamp
}
/// Request payload for creating an order.
/// Named request/response models are easier to reuse and generate cleanly.
model CreateOrderRequest {
user_id: UserId
items: OrderItem[]
}
/// A standard paginated response shape.
model OrderListResponse {
orders: Order[]
next_cursor?: string
}
/// A standard API error shape. Use it consistently in 4XX/5XX responses.
model ApiError {
code: string
message: string
details?: Record<string, any>
}
/// List orders.
/// Endpoint paths can include placeholders such as `{user_id}`.
/// Rich query/path parameter modeling is intentionally limited today, so keep
/// path semantics clear in the endpoint name and docs.
endpoint "GET /users/{user_id}/orders" ListOrders {
headers: {
/// Optional request ID for tracing across services.
"X-Request-ID"?: string
}
responses: {
200: OrderListResponse
4XX: ApiError
5XX: ApiError
}
}
/// Create a new order from a JSON request body.
endpoint "POST /orders" CreateOrder {
body: CreateOrderRequest
responses: {
201: Order
4XX: ApiError
5XX: ApiError
}
}
/// Submit an existing order. Multiple explicit media types can be used when
/// an endpoint supports more than one request representation.
endpoint "POST /orders/{order_id}/submit" SubmitOrder {
body: {
"application/json": Order
}
responses: {
200: Order
4XX: ApiError
5XX: ApiError
}
}
Review Checklist
Before handing a Glue file back to a user, check:
- Imports are at the top and paths are correct.
- Every referenced type is declared or imported.
- Optional fields use
?; defaults use=. - Enums are pipe-separated quoted string values.
- Endpoint responses include success and error cases.
- No unsupported generics, intersections, or invented decorators/config keys appear.
glue checkpasses when the CLI is available.- Generated output paths are explicit when the user asked for code generation.