Skip to content

Generators

Command Creates Registers
dygo g module auth (mo) src/modules/auth/module.go from the root, auth/module.go elsewhere auth.Module in the parent directory’s module Imports, or AppModule in app.module.go
dygo g service auth (s) auth/service.go, service_test.go dygo.Provide(NewService) in the nearest module’s Providers
dygo g controller auth (co) auth/controller.go, controller_test.go dygo.Provide(NewController) in the nearest module’s Controllers
dygo g resource order-items (res) orderitems/: module, service (stored in the database set by DATABASE_URL, memory by default), controller (CRUD), DTOs with Validate, an HTTP test orderitems.Module in the parent module
dygo g entity Product -fields "..." (en) products/products.go: the entity, create and update bodies with Validate, Entity and Apply. Run again on an existing entity, it adds the fields Nothing
dygo g crud Product For an existing entity: module, service, controller (list, create, get, update, delete) and HTTP test products.Module in the parent module
dygo g resource Product -fields "..." (res) Entity and CRUD in one step (g entity then g crud) products.Module in the parent module
dygo g command seed (cmd) seed/command.go (Name, Description, Run), command_test.go dygo.Provide(seed.NewCommand) in the nearest module’s Providers
dygo g test products products/routes_test.go, one subtest per route the app registers for the package Nothing
dygo g auth auth/: register, login, me, password hashing, tokens, guard, tests auth.Module in the root module, plus AUTH_SECRET in .env.example
dygo g security (sec) security/: rules, global guard, roles, authorities, Require, register, login, me, tokens, CORS, tests and securitytest security.Module in the root module, security.CORS() in dygo.WithMiddleware, settings in .env and .env.example, "security": true in dygo.json
dygo g security -oidc the same, for tokens from an identity provider: security/verifier.go instead of users, passwords and login, and GET /auth/me the same; OAUTH_ISSUER_URL, OAUTH_AUDIENCE, OAUTH_ROLES_CLAIM in .env

“Nearest module” is the &dygo.Module{...} literal in the target directory or the closest one above it, inside the Go module. When a directory has several, the one named like the package or "app" wins. The edit inserts one line and adds the import, so your comments and layout stay. Nothing is overwritten; -dry-run prints the plan.

Names: order-items gives package orderitems, path /order-items, entity OrderItem. Types inside a package are not prefixed (orderitems.Service, not OrderItemsService).

$ dygo g entity Product
Your entity Product already exists, so let's add some new fields!
Current fields: id, title, price
New field name (press <return> to stop adding fields):
> releasedAt
Field type (enter ? to see all types) [time]:
>
Can this field be empty (optional) (yes/no) [no]:
> yes
added: releasedAt time (optional)
Add another field? Enter the field name (or press <return> to stop adding fields):
>
UPDATE products/products.go (+ releasedAt)
UPDATE products/controller_test.go (sample values)

Name the entity in the singular or the plural, in any case. Product, Products, product and products all give the products package (/products) and the Product type.

In a terminal, leaving out -fields starts this loop. It asks for the name, the type (guessed from the name: ...At is a time, is... a bool, price a float, and ? lists every type) and whether the field is optional. Pressing Enter on an empty name stops the loop. It works the same for a new entity or resource. For scripts and CI, pass the fields as a flag:

Terminalsh
dygo g resource Product -fields "title:string price:float stock:int notes:text? owner:email?"
dygo g entity Product -fields "sku:string" # later: add a field to the existing entity
dygo g resource Product # in a terminal, without -fields: asks field by field

Types: string, text, email, int, int64, float, bool, time. Add ? for an optional field. Required text fields must not be empty, email fields must be an address, and required time fields must be set. Validation runs in the validation pipe and answers 400 with every problem at once. Field names become Go names (published-at gives PublishedAt) and camelCase JSON names (publishedAt).

Every line that depends on the fields is in one file, products/products.go: the Product struct, CreateProduct with Validate and Entity(id), and UpdateProduct (pointer fields, so a PATCH changes only what was sent) with Validate and Apply. The service calls Entity and Apply, so adding a field never touches the service or controller. dygo g entity on an existing entity inserts the new fields into each of those places, and adds sample values to the CreateProduct{...} bodies in the package’s tests. Everything else in the files stays as you wrote it, and if you removed one of the methods, the CLI says which parts it could not update.

Make the entity first, then the CRUD around it:

$ dygo g entity Product # the field loop above
$ dygo g crud Product # or just dygo g crud, which asks for the entity
CREATE src/modules/products/module.go
CREATE src/modules/products/service.go
CREATE src/modules/products/controller.go
CREATE src/modules/products/controller_test.go
UPDATE app.module.go (Imports += products.Module)
Success! CRUD for Product:
GET /products List (index)
POST /products Create (new)
GET /products/{id} Get (show)
PATCH /products/{id} Update (edit)
DELETE /products/{id} Delete (delete)

dygo g crud takes the entity’s type (Product) or its package (products). It reads the entity file, and the test fills every field with a valid sample value (email fields get an address). It writes:

File What it holds
controller.go List, Create, Get, Update, Delete (JSON, so no HTML views)
the entity file CreateProduct and UpdateProduct, with Validate (400 with every error)
service.go storage on *sql.Repository[Product]; DATABASE_URL in .env picks memory, Postgres or MySQL
controller_test.go the whole create, list, update, delete flow

No CSRF token is needed for a token-authenticated JSON API; protect routes with auth.Required().

dygo g resource Product -fields "..." still does both steps at once.

Terminalsh
dygo g command seed # seed/command.go, registered in the nearest module
dygo run seed --users 10 # builds the app and runs the command inside its context
dygo run # lists the commands with their descriptions

A command is a provider with Name() and Run(ctx, args) (and optionally Description()), so it can inject any service. Package console builds the application context, runs the command, then shuts down, so OnModuleInit and shutdown hooks run as they do for the server. ctx is cancelled on Ctrl+C. New projects’ main.go starts with:

if len(os.Args) > 1 { // go run . <command>
os.Exit(console.Main(ctx, AppModule(cfg, log), os.Args[1:], dygo.WithLogger(log)))
}

In a project made before this existed, dygo g command prints that snippet.

dygo g test products runs the app (as dygo routes does) and writes products/routes_test.go with one subtest per route of the package. GET routes without path parameters are checked for their status. The others are skipped with a TODO and a request ready to fill in (body and real IDs). The test boots the package’s Module with dygotest.