Getting Started
This page walks through creating a craftgo project, defining one endpoint, and running it. About 5 minutes.
Prerequisites
- Go 1.24 or later
- A terminal
Install the CLI
go install github.com/craftgodotdev/craftgo/cmd/craftgo@latestVerify:
craftgo --helpCreate a project
mkdir hello && cd hello
go mod init example.com/hello
go get github.com/craftgodotdev/craftgoScaffold the design folder
craftgo init designThis creates design/craftgo.design.yaml with default settings. Now write a .craftgo file:
design/users/service.craftgo:
package design
type CreateUserReq {
name string @length(1, 80)
email string @format(email)
}
type User {
id string
name string
email string
}
@prefix("/v1")
service UserService {
post CreateUser /users {
request CreateUserReq
response User
}
}Folder layout so far:
hello/
├── design/
│ ├── craftgo.design.yaml
│ └── users/
│ └── service.craftgo
├── go.mod
└── go.sumGenerate
craftgo gen designThe CLI walks up from the given path looking for craftgo.design.yaml, reads go.mod for the Go module path, then writes:
hello/
├── design/... (your DSL, unchanged)
├── internal/
│ ├── types/design/ generated structs + Validate()
│ │ ├── types.go
│ │ ├── validate.go
│ │ └── errors.go
│ ├── transport/user-service/ generated HTTP handlers
│ │ ├── create-user.go
│ │ └── errors.go
│ ├── service/user-service/ gen-once business logic stubs
│ │ └── create-user.go
│ ├── routes/ generated routing
│ │ ├── routes.go
│ │ └── user-service/routes.go
│ └── middleware/ gen-once middleware stubs
├── svccontext/svccontext.go gen-once dependency container
├── config/ gen-once runtime config
│ ├── config.go
│ ├── config.yaml
│ └── example.config.yaml
├── docs/openapi.yaml generated OpenAPI 3.1 spec
├── main.go gen-once entry point
└── go.modImplement business logic
Open internal/service/user-service/create-user.go:
func (l *CreateUserService) CreateUser(req *types.CreateUserReq) (*types.User, error) {
return &types.User{
ID: "u1",
Name: req.Name,
Email: req.Email,
}, nil
}This is the only file you edit. Everything in internal/types/, internal/transport/, internal/routes/ is regenerated.
Run
go run .listening on :8080 (api)In another terminal:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name":"alice","email":"alice@example.com"}'{ "id": "u1", "name": "alice", "email": "alice@example.com" }(/api comes from openapi.basePath: /api in craftgo.design.yaml; /v1 from @prefix("/v1") in the DSL.)
Try a bad payload:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name":"","email":"not-an-email"}'name: length out of range [1, 80]Validate() is fail-fast — it returns the first violation (here name), so fixing one surfaces the next. The validators ran without you writing any code.
What just happened
- The DSL described one endpoint, two types, three validators.
craftgo genproduced typed Go structs, an HTTP handler, a logic stub, route registration, and an OpenAPI spec.- You filled the logic stub.
- The handler decoded JSON, ran
req.Validate(), called your function, encoded the response.
No reflection. No struct tags. No middleware boilerplate. The handler is a plain http.HandlerFunc registered on *http.ServeMux.
Designing with an LLM?
If you draft .craftgo files with Claude, ChatGPT, Cursor, or any other LLM, paste this URL into your prompt:
https://craftgodotdev.github.io/craftgo/llmsIt is a single-page consolidated reference (every keyword, decorator, CLI flag, and generated layout) designed for AI ingestion. Your assistant will know the full spec and stop inventing non-existent decorators.
Next steps
- Read DSL Basics to learn the syntax in depth.
- Browse Decorators to see every decorator with arguments and sites.
- Install the VS Code extension (search "craftgo" in the Extensions panel) — or set up the LSP for another editor — for completion, hover, and live diagnostics.
- See Configuration to relocate generated files or add custom config.