Generators

Forge includes code generators that create boilerplate files following the standard project layout. They are the fastest way to bootstrap a new resource.

forge generate scaffold

Creates a complete resource: migration, model, controller, four views, route entry, and a test file.

forge generate scaffold <Name> [field:type ...]

Example:

forge generate scaffold Post title:string body:text published:boolean user:references

Files created:

db/migrate/001_create_posts.sql
app/models/post.jda
app/controllers/post_controller.jda
app/views/posts/index.html.jda
app/views/posts/show.html.jda
app/views/posts/new.html.jda
app/views/posts/edit.html.jda
config/routes.jda               (appended: resources "posts")
test/test_posts.jda

After generating, run migrations and rebuild:

forge db:migrate
forge build
forge server

forge generate model

Creates the migration and model file only, without views or a controller.

forge generate model <Name> [field:type ...]
forge generate model Comment body:text post:references

forge generate controller

Creates a controller with the seven standard actions (index, new, create, show, edit, update, delete).

forge generate controller <Name>
forge generate controller Comment

forge generate migration

Creates a migration file only. The migration name determines the SQL generated:

Name patternSQL generated
create_<table>CREATE TABLE
add_<cols>_to_<table>ALTER TABLE ... ADD COLUMN
remove_<cols>_from_<table>ALTER TABLE ... DROP COLUMN
anything elseempty skeleton for you to fill in
# Create a new table
forge generate migration create_comments body:text post:references

# Add a column to an existing table
forge generate migration add_slug_to_posts slug:string

# Remove a column
forge generate migration remove_published_from_posts published:boolean

Migration files land in db/migrate/ with a three-digit prefix that increments automatically: 001_create_posts.sql, 002_add_slug_to_posts.sql, and so on.

Field types

TypePostgreSQL column
stringVARCHAR(255)
textTEXT
integerBIGINT
booleanBOOLEAN NOT NULL DEFAULT false
timestampTIMESTAMP
floatFLOAT
jsonJSONB
referencesBIGINT NOT NULL REFERENCES <table>(id) ON DELETE CASCADE

Every table generated by create_* migrations gets id, created_at, updated_at, and deleted_at columns automatically. Forge uses soft-deletes (deleted_at IS NULL filter) by default.

Running migrations

# Apply all pending migrations
forge db:migrate

# Apply against a specific environment
forge db:migrate --environment production

# Show migration status
forge db:status

# Roll back the last migration
forge db:rollback

# Roll back the last 3 migrations
forge db:rollback --step 3

# Roll back to a specific version
forge db:rollback --version 002

Each migration file has a -- migrate:up section (applied by db:migrate) and a -- migrate:down section (applied by db:rollback).

-- Migration: 001_create_posts

-- migrate:up
CREATE TABLE IF NOT EXISTS posts (
    id          BIGSERIAL PRIMARY KEY,
    title       VARCHAR(255),
    body        TEXT,
    published   BOOLEAN NOT NULL DEFAULT false,
    user_id     BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at  TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMP NOT NULL DEFAULT NOW(),
    deleted_at  TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON posts (user_id);

-- migrate:down
DROP TABLE IF EXISTS posts;

Generated model file

The model file (app/models/post.jda) wires up validations, associations, and callbacks. CRUD functions (post_create, post_find, post_all, post_update, post_destroy) are generated automatically from the migration at compile time and do not live in this file.

// app/models/post.jda
fn post_model_init() {
    forge_model("posts")

    forge_assoc_belongs_to("user", "users", "user_id")

    // forge_callback(FORGE_CB_BEFORE_SAVE,  fn_addr(post_before_save))
    // forge_callback(FORGE_CB_AFTER_CREATE, fn_addr(post_after_create))

    forge_field("title, body", FORGE_V_PRESENCE)
    forge_field_length("title", 1, 255)
}

See models.md for the full query builder, validations, callbacks, and associations reference.

Generated controller

The controller (app/controllers/post_controller.jda) contains stub implementations for all seven actions. Fill in the create and update actions with your field params:

fn create(ctx: i64) {
    let title = ctx_param(ctx, "title")
    let body  = ctx_param(ctx, "body")
    post_create(title, body)
    ctx_flash_set(ctx, "notice", "Created.")
    ctx_redirect(ctx, posts_path)
}

Generated views

Views live in app/views/<table>/. The scaffold creates four files:

FilePurpose
index.html.jdaList all records
show.html.jdaDisplay one record
new.html.jdaForm to create a record
edit.html.jdaForm to update a record

Add your field inputs to the new and edit forms:

<form method="POST" action="<%== posts_path %>">
  <input type="hidden" name="_csrf" value="<%= forge_csrf_token(ctx) %>">
  <input type="text"   name="title" placeholder="Title">
  <textarea name="body"></textarea>
  <button>Create Post</button>
  <a href="<%== posts_path %>">Cancel</a>
</form>

See views.md for the full template reference.

Generated route entry

config/routes.jda receives a resources "posts" line, which expands to the seven standard REST routes at compile time:

MethodPathAction
GET/postsindex
GET/posts/newnew
POST/postscreate
GET/posts/:idshow
GET/posts/:id/editedit
PUT/PATCH/posts/:idupdate
DELETE/posts/:iddelete

See routing.md for nested resources, custom routes, and middleware.