DSL Basics
The craftgo DSL is a small file format that describes your API. From it, craftgo generates Go types, validators, HTTP handlers, route registration, and an OpenAPI 3.1 spec.
At a glance
A .craftgo file has three things:
- A
packageline (mandatory) - Optional
importlines for cross-package references - Declarations:
type,enum,scalar,error,service,middleware
Every declaration produces specific generated code. The DSL is the single source of truth: change a field once, every generated artifact updates.
package design
type CreateUserReq {
name string @length(1, 80)
email string @format(email)
}
@prefix("/v1")
service UserService {
post CreateUser /users {
request CreateUserReq
response User
}
}This page covers the syntax. For per-decorator detail see Decorators. For runtime behavior see Runtime.
File layout
A craftgo project keeps DSL files under a design/ folder. Each subfolder is one logical package.
design/
├── users/
│ ├── service.craftgo
│ └── errors.craftgo
└── orders/
└── service.craftgoFiles in the same subfolder share one package and see each other's declarations directly. Across subfolders, use import.
Six declaration kinds
Every craftgo file declares from this set:
| Keyword | Purpose |
|---|---|
type | Request / response struct |
enum | Closed value set |
scalar | Named primitive with bundled validators |
error | Typed error with HTTP status |
service | Group of HTTP methods |
middleware | Named middleware slot |
package design
import "shared"
type User { id string name string }
enum Status { Active Inactive }
scalar Email string @format(email)
error NotFound UserNotFound
service UserService { ... }
middleware AuthTypes
Describe the shape of request and response bodies.
type CreateUserReq {
name string
email string
age int?
tags string[]
meta map<string, string>
}Field syntax: name TypeRef [@decorator(...) ...]. Type references are primitives, arrays (T[]), maps (map<K, V>), or other declared types. Append ? to mark optional.
| DSL form | Go output |
|---|---|
string | string |
int / int64 | matching Go integers |
float64 | float64 |
bool | bool |
bytes | []byte |
T? | *T |
T[] | []T |
map<K, V> | map[K]V |
Custom | Custom (your type) |
See Types and Scalars for advanced shapes (generics, mixins).
Decorators
Decorators attach metadata. They start with @ and may take arguments.
type CreateUserReq {
name string @length(1, 80)
email string @format(email)
age int? @gte(0) @lte(150)
}50 decorators total, grouped by purpose: validators, bindings, metadata, service-level. Full reference at Decorators.
Services
A service is a group of HTTP methods sharing a path prefix and middleware chain.
@prefix("/v1")
@tags(users)
service UserService {
@doc("Fetch a user.")
get GetUser /users/{id} {
request GetUserReq
response User
}
@doc("Create a user.")
@status(201)
post CreateUser /users {
request CreateUserReq
response User
}
}Method form: <verb> <Name> <path> { request <Type> response <Type> }.
Verbs: get, post, put, patch, delete, head, options. trace and connect are not supported.
Request and response types are single named structs only. Generic instantiations such as response Page<Order> work; bare-array forms (response Order[]) and optional markers (response User?) do not — wrap the shape in a struct (type Items { items Order[] }) and reference that struct instead.
Path parameters use {name} and bind to fields with @path:
type GetUserReq {
id string @path
}A path that declares {name} segments requires a request struct whose fields cover every segment; otherwise the route would parse the URL but the handler would never see the value, so the semantic phase rejects it with path/param-missing. The exception is @passthrough handlers, which receive the raw *http.Request and pull params themselves. Trailing slashes are kept verbatim (/users/ is distinct from /users); avoid them unless the API explicitly distinguishes the two.
Extending a service across files
Real services grow. To split methods across files (or add admin endpoints in a separate file from the public ones), use extend service:
// design/users/service.craftgo - the primary block
package design
@prefix("/users")
@middlewares(AuthRequired)
service UserService {
get GetUser /{id} {
request GetUserReq
response User
}
}// design/users/admin.craftgo - additional methods, same service
package design
extend service UserService {
@middlewares(AdminOnly)
delete PurgeUser /{id}/purge {
request GetUserReq
response shared.OkResp
}
}After codegen, both methods live under the same service, sharing the /users prefix and the AuthRequired middleware. PurgeUser additionally runs AdminOnly.
extend service carries its own decorators
The extend block itself can declare method-level-applicable decorators that propagate to every method in the block. The canonical use case is the 50/50 split: half the methods need auth, half don't.
service Users {
// Public endpoints - no service-level decorators
get Healthz /healthz { response HealthResp }
post Signup /signup { request SignupReq response User }
post Login /login { request LoginReq response Session }
}
@middlewares(AuthRequired)
@security(Bearer)
extend service Users {
get List /users { response UserList } // inherits AuthRequired + Bearer
get Get /users/{id} { request GetUserReq response User } // inherits
post Create /users { request CreateUserReq response User } // inherits
}The extend block's @middlewares / @security decorators apply to every method inside as if they were written directly on the method. Each method can still add its own decorators on top - those append.
Rules (enforced at gen time with a diagnostic, not silently):
- The primary
serviceblock declares whole-service decorators (@prefix,@groupbelong here). extend serviceblocks may carry method-level-applicable decorators only (@middlewares,@security,@tags,@deprecated,@doc). Service-only decorators like@prefixon an extend raiseservice/extend-decorator-not-method.- The extended service must already be declared somewhere in the same package (same design subfolder); a cross-package extend raises
service/extend-orphan. - Multiple
extendblocks for the same service are allowed (one per file is the typical pattern). Each block contributes its own decorators only to its own methods.
The extended methods inherit every service-level decorator from the primary AND every decorator on the extend block. Method-level decorators of the same kind (@middlewares, @security, @tags) append; use @ignoreMiddleware / @ignoreSecurity / @ignoreTags to drop the inherited chain for one specific method.
See Decorators - Service-level decorators and inheritance for the full combine semantics and combinations cheatsheet.
When to use it:
- Split a large service across files for navigability (admin vs public, read vs write)
- Group methods by feature area (
profile.craftgo,billing.craftgo,notifications.craftgo) - Separate frequently changed endpoints from stable ones
If your service has 5 methods, keep them in one file. extend shines around 10+ methods or when methods cluster by audience.
Enums
Closed value sets:
enum Status {
Active
Inactive
Pending
}Three forms: bare identifiers (Go string constants), = 1 (integer), = "active" (custom string). See Enums.
Scalars
Named primitives with built-in validators:
scalar Email string @format(email) @maxLength(254)
scalar Cents int @gte(0) @multipleOf(2)
type Order {
email Email
total Cents
}Every field of type Email automatically runs @format(email) and @maxLength(254). See Types and Scalars.
Errors
Typed errors with HTTP status mapping:
error NotFound UserNotFound
error Conflict EmailTaken {
email string
}See Errors.
Middleware
Declared at file level, attached to services or methods via @middlewares:
middleware AuthRequired
middleware RateLimit
@middlewares(AuthRequired, RateLimit)
service UserService { ... }See Middleware.
Imports
Files reference declarations across folders with import:
package design
import "shared"
type User {
contact shared.Contact
}The codegen wires the matching Go imports automatically.
Comments
// line comments. Comments above a declaration become its doc string and surface in OpenAPI:
// User is the public user entity.
// Email is the canonical login id.
type User { ... }// only - no /* */.
Next
- Decorators - the full decorator catalog
- Validators - validation runtime semantics
- Types and Scalars - generics, mixins, advanced types
- AI Reference - one-page consolidated reference (paste this into LLM prompts)