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:referencesFiles 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.jdaAfter generating, run migrations and rebuild:
forge db:migrate
forge build
forge serverforge 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:referencesforge generate controller
Creates a controller with the seven standard actions (index, new, create, show, edit, update, delete).
forge generate controller <Name>forge generate controller Commentforge generate migration
Creates a migration file only. The migration name determines the SQL generated:
| Name pattern | SQL generated |
|---|---|
create_<table> | CREATE TABLE |
add_<cols>_to_<table> | ALTER TABLE ... ADD COLUMN |
remove_<cols>_from_<table> | ALTER TABLE ... DROP COLUMN |
| anything else | empty 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:booleanMigration 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
| Type | PostgreSQL column |
|---|---|
string | VARCHAR(255) |
text | TEXT |
integer | BIGINT |
boolean | BOOLEAN NOT NULL DEFAULT false |
timestamp | TIMESTAMP |
float | FLOAT |
json | JSONB |
references | BIGINT 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 002Each 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:
| File | Purpose |
|---|---|
index.html.jda | List all records |
show.html.jda | Display one record |
new.html.jda | Form to create a record |
edit.html.jda | Form 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:
| Method | Path | Action |
|---|---|---|
| GET | /posts | index |
| GET | /posts/new | new |
| POST | /posts | create |
| GET | /posts/:id | show |
| GET | /posts/:id/edit | edit |
| PUT/PATCH | /posts/:id | update |
| DELETE | /posts/:id | delete |
See routing.md for nested resources, custom routes, and middleware.