Generators
Generate code
Section titled “Generate code”| 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).
Entities and fields
Section titled “Entities and fields”$ 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:
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 entitydygo g resource Product # in a terminal, without -fields: asks field by fieldTypes: 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.
CRUD for an entity
Section titled “CRUD for an entity”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.goCREATE src/modules/products/service.goCREATE src/modules/products/controller.goCREATE src/modules/products/controller_test.goUPDATE 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.
Console commands
Section titled “Console commands”dygo g command seed # seed/command.go, registered in the nearest moduledygo run seed --users 10 # builds the app and runs the command inside its contextdygo run # lists the commands with their descriptionsA 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.
Route tests
Section titled “Route tests”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.