Your First Generated Application
By the end of this guide you will have used Gen-X to generate SeedVault — a seed inventory application with two related models, an enum field, and full CRUD views. The guide takes around 20 minutes.
What We’re Building
SeedVault tracks seed packets organised by plant family. A PlantFamily groups related seeds together — Solanaceae for tomatoes and peppers, Cucurbitaceae for squash and cucumbers. Each Seed record belongs to one plant family and carries a germination status that moves through a fixed lifecycle: stored, soaking, germinating, and planted. The application gives you list views, create and edit forms, and detail pages for both models out of the box.
Before You Begin
You need a working Gen-X environment. If you haven’t set it up yet, start with Installation.
Step 1 — Register SeedVault in ATC
Open the App Tower Control at http://localhost:4555 and create a new application entry.
- Click New App in the top navigation.
- Fill in the registration form:
- Name:
seed-vault - URL:
seed-vault - Port:
8086
- Name:
- Click Submit.
- On the app detail page that loads, note the integer App ID shown next to the application name. You will need this value in the depiction script.
📝 Note: The
projectIdargument in the depiction script must be the integer ID assigned by ATC during registration, not the app name. Find it in the app detail view after clicking Submit.
Step 2 — Write the Depiction Script
Create a new file for the depiction script. By convention, depiction scripts live in src/srv/depicters/ in the gen-x repository.
touch ~/go/src/gen-x/src/srv/depicters/seed_vault.go
The sections below build the script incrementally. The complete, copy-paste-ready version is at the end of this step.
2a — Initialize the Project
The CreateProject call registers the application with the generation pipeline and returns a ProjectConfig that every subsequent call depends on. Replace 42 with the App ID you noted in Step 1.
config := d.CreateProject(
ctx,
42, // projectId from ATC
"seed-vault", // app name
"seed-vault", // folder name
"github.com/your-org/seed-vault", // Go module path
nil,
)
2b — Add the PlantFamily Model
AddModel with crud: true generates the full actor set — list, create, edit, detail, and delete — for the model automatically.
plantFamilyModel := d.AddModel(ctx, config, "PlantFamily", true)
d.AddModelField(ctx, config, &mdl.ModelField{Name: "name", Kind: "string"}, plantFamilyModel)
d.AddModelField(ctx, config, &mdl.ModelField{Name: "description", Kind: "string"}, plantFamilyModel)
2c — Add the Seed Model
The germination_status field uses StringDataEnum to restrict its value to a fixed set of lifecycle states. The generated form will render a select input automatically.
seedModel := d.AddModel(ctx, config, "Seed", true)
d.AddModelField(ctx, config, &mdl.ModelField{Name: "name", Kind: "string"}, seedModel)
d.AddModelField(ctx, config, &mdl.ModelField{Name: "quantity", Kind: "int"}, seedModel)
d.AddModelField(ctx, config, &mdl.ModelField{Name: "germination_status", Kind: "string", Data: d.StringDataEnum([]string{"stored", "soaking", "germinating", "planted"})}, seedModel)
2d — Add a Relation
AddManyChildToOneParentRelation links Seed (the child) to PlantFamily (the parent). One plant family can have many seeds.
d.AddManyChildToOneParentRelation(ctx, config, seedModel, "PlantFamily")
📝 Note:
AddManyChildToOneParentRelationautomatically adds aplant_family_idforeign-key column toSeed. You do not need to declare it as a separateModelField.
2e — Wire the Router and Navigation
AddPublicRouter registers all generated routes with the Gin router. The two AddIntoTopMenu calls add navigation links to the application’s top bar. CopiesOverwrittenFiles applies any handcrafted // |@@| W overrides at the end of the run.
d.AddPublicRouter(ctx)
d.AddIntoTopMenu(ctx, "Plant Families", "/plant-families")
d.AddIntoTopMenu(ctx, "Seeds", "/seeds")
d.CopiesOverwrittenFiles(ctx, config)
Complete Script
Here is the full seed_vault.go depiction file ready to copy into src/srv/depicters/.
package depicters
// |@@| F
import (
"context"
"gen-x/src/mdl"
)
type SeedVaultDepicter struct {
Depicter *Depicter
}
func (this *SeedVaultDepicter) Depict(ctx context.Context) {
config := this.Depicter.CreateProject(
ctx,
42, // projectId from ATC
"seed-vault", // app name
"seed-vault", // folder name
"github.com/your-org/seed-vault", // Go module path
nil,
)
this.PlantFamilyModel(ctx, config)
this.SeedModel(ctx, config)
this.SeedRelations(ctx, config)
this.Depicter.AddPublicRouter(ctx)
this.Depicter.AddIntoTopMenu(ctx, "Plant Families", "/plant-families")
this.Depicter.AddIntoTopMenu(ctx, "Seeds", "/seeds")
this.Depicter.CopiesOverwrittenFiles(ctx, config)
}
func (this *SeedVaultDepicter) PlantFamilyModel(ctx context.Context, config *mdl.ProjectConfig) {
model := this.Depicter.AddModel(ctx, config, "PlantFamily", true)
this.Depicter.AddModelField(ctx, config, &mdl.ModelField{Name: "name", Kind: "string"}, model)
this.Depicter.AddModelField(ctx, config, &mdl.ModelField{Name: "description", Kind: "string"}, model)
}
func (this *SeedVaultDepicter) SeedModel(ctx context.Context, config *mdl.ProjectConfig) {
model := this.Depicter.AddModel(ctx, config, "Seed", true)
this.Depicter.AddModelField(ctx, config, &mdl.ModelField{Name: "name", Kind: "string"}, model)
this.Depicter.AddModelField(ctx, config, &mdl.ModelField{Name: "quantity", Kind: "int"}, model)
this.Depicter.AddModelField(ctx, config, &mdl.ModelField{Name: "germination_status", Kind: "string", Data: this.Depicter.StringDataEnum([]string{"stored", "soaking", "germinating", "planted"})}, model)
}
func (this *SeedVaultDepicter) SeedRelations(ctx context.Context, config *mdl.ProjectConfig) {
model := this.Depicter.GetModel(ctx, "Seed")
this.Depicter.AddManyChildToOneParentRelation(ctx, config, model, "PlantFamily")
}
Step 3 — Run the Generator
⚠️ Warning: Running the Depicter overwrites all
// |@@| Cfiles in the output directory. If~/go/src/seed-vaultalready exists with custom changes not protected by// |@@| W, those changes will be lost.
From the gen-x repository root, run:
ENV=dev go run main.go depict seed-vault
The terminal prints a series of progress dots as files are written, and displays the output path at the end of a clean run — typically /tmp/generated_project_seed-vault, before the project is copied to ~/go/src/seed-vault.
Step 4 — Start SeedVault
cd ~/go/src/seed-vault && bird
Open http://localhost:8086/plant-families in your browser. You will see the plant family list page with a working create form.
Step 5 — Explore the Generated Output
The generator has produced a complete application. Here is what was created for the Seed model:
src/
├── mdl/
│ └── seed.go # Seed entity struct
├── srv/
│ ├── handlers/
│ │ └── seed_handler.go # Create, Update, Delete for Seed
│ ├── fetchers/
│ │ └── seed_fetcher.go # Read queries for Seed
│ ├── hydrators/
│ │ └── seed_hydrator.go # Entity hydration from DB rows
│ ├── molders/
│ │ └── create_seed_form_molder.go # Form → entity for create
│ │ └── update_seed_form_molder.go # Form → entity for update
│ ├── form_validators/
│ │ └── create_seed_form_validator.go
│ │ └── update_seed_form_validator.go
│ ├── presenters/
│ │ └── seed_presenter.go
│ └── serializers/
│ └── seed_serializer.go
├── mae/
│ ├── new_seed_mae.go # Renders the create form
│ ├── create_seed_mae.go # Processes the create submission
│ ├── edit_seed_mae.go # Renders the edit form
│ ├── update_seed_mae.go # Processes the edit submission
│ ├── show_seed_mae.go # Detail view for a single Seed
│ ├── list_seeds_mae.go # List view with pagination
│ └── delete_seed_mae.go # Delete action
Each file carries a // |@@| C annotation at the top, meaning it will be regenerated on the next Depicter run.
What Happens When You Re-Run
One of Gen-X’s defining features is safe, repeatable regeneration. After you run the Depicter a second time:
- Files annotated
// |@@| Care overwritten with freshly generated code. - Files annotated
// |@@| Ware left untouched. - The depiction script itself carries
// |@@| Wby convention, so it is never clobbered by the generator.
This means you can extend the depiction script at any time — add a new field, a new model, or a new relation — and re-run to regenerate the entire application. Your custom business logic in // |@@| W files survives every regeneration cycle intact.
For a full explanation of how the annotation system works, see Modifying an App and File Header Reference.
Beyond This Walkthrough
Two natural directions from here:
- Extend SeedVault — Add more fields, validations, and relations to the depiction script and re-run. See the Depicter API Reference for all available methods.
- Understand the pipeline — Architecture Overview explains how the Depicter, Appliers, Handlers, and Configurators work together to produce the output.
Next Steps
- Configuration — Configure Gen-X and understand the two configuration layers
- Modifying an App — How to safely add custom code to a generated app