Skip to content

Make Command Reference

This page is the exhaustive lookup for commands that create controllers, commands, jobs, schedules, events, models, migrations, and named queues. Each entry records the files and registration points the command changes.

Use the linked feature guide when you need to implement the generated resource. Use this reference when you need exact placement, wiring, removal, or shared-option behavior.

In a multi-app Project, run make commands through the app that owns the resource:

bash
forj admin make:controller users
forj admin make:job reports:export
forj admin make:schedule audit:cleanup

The app prefix chooses the registration point. forj admin make:* creates the generated resource under internal/... and writes the registration and Wire changes into app/admin/...; unprefixed forj make:* creates the resource under internal/... and writes registration changes to the default app under app/....

This keeps app composition in the owning app while shared domain code can still live under internal/....

Choose a Command or Workflow

Shared workflows apply across those generators:

Command Reference

Some make commands are native GoForj commands and some are App commands. During development, use the same forj prefix for both. Native GoForj commands win on name collisions; otherwise GoForj delegates to the active app through the same source-aware path as forj run.

For an additional app, prefix the command with the app name. The generated resource stays under internal/..., while registration changes go to the owning app under app/<name>/....

make:app

Create an additional app:

bash
forj make:app admin

Without selection flags in an interactive terminal, the command opens the app wizard. In non-interactive use, or when scripting an exact selection, pass flags:

bash
forj make:app admin --components web-api,jobs
forj make:app statuspage --components web-api,web-ui --starter-kit vue

Use --without to remove components from the project-derived default selection. Use --help-format framework, --help-format external_cli, or --help-format guided to choose the App command-help style. --skip-wire renders the App files without regenerating Wire; it is intended for generator debugging or a workflow that deliberately runs Wire separately.

The command creates the conventional binary entrypoint under cmd/admin/, app-owned composition under app/admin/, and app-specific Wire graph under app/admin/wire/. Exact files depend on the selected components; a CLI-only app does not receive HTTP, scheduler, or worker files merely because another app has them.

It also records the app's render metadata under top-level apps in .goforj.yml:

yaml
apps:
  admin:
    components: [web_api, jobs]
    starter_kit: none

The interactive wizard enrolls an app with HTTP, jobs, or schedules in forj dev and proposes the combined run command. A CLI-only app remains unenrolled. Review the Dev Run step when the app needs a narrower long-running command.

Flag-driven and non-interactive creation do not enroll the app unless --dev-run is explicit:

bash
forj make:app admin --components web-api,jobs --dev-run run

--dev-run run selects the development supervisor command; it does not add runtime capabilities. A runtime-capable binary already defaults to run when launched without arguments.

Remove only the conventional App files and metadata created by make:app with:

bash
forj make:app admin --remove

Removal is conservative and does not delete unknown app-owned files or migration history. See Apps for the ownership model and forj dev for normal lifecycle configuration.

make:controller

Generate an HTTP controller for the package that owns the route.

bash
forj make:controller reports

The name controls both the package path and the starter route, /reports.

Prefix the command when an additional app owns the route:

bash
forj admin make:controller users

Remove the generated controller and its managed registrations with:

bash
forj make:controller reports --remove

make:command

Generate an App command.

bash
forj make:command reports:sync

Use --name to override the exposed command signature independently from the generated type and file:

bash
forj make:command Sync -d ./internal/billing/reports --name reports:sync
bash
forj make:command reports:sync --remove

make:job

Generate a queue job and select its default queue.

bash
forj make:job billing:sync-reports --queue billing
bash
forj make:job billing:sync-reports --remove

make:queue

Add a named queue resource.

bash
forj make:queue reports --workers 2

Run forj make:queue without a name in an interactive terminal to use the resource wizard.

Use --name production-report-jobs when the backend queue name should differ from the reports resource name. Use --env-file to update a file other than .env.

bash
forj make:queue reports --remove

Pass the same --env-file during removal when creation did not update .env.

make:schedule

Generate a recurring task.

bash
forj make:schedule reports:daily --every 24h
bash
forj make:schedule reports:daily --remove

If --every is omitted, the generated starter interval is 1h.

make:event

Generate an application event type.

bash
forj make:event billing:invoice-paid
bash
forj make:event billing:invoice-paid --remove

Removal deletes the generated type, but it does not remove application code that refers to it.

make:subscriber

Generate a subscriber for an application event.

bash
forj make:subscriber billing:invoice-paid

Use --bus audit to target a named event bus configured by EVENTS_AUDIT_DRIVER.

bash
forj make:subscriber billing:invoice-paid --remove

Pass the same --bus value used during creation. Removal deletes the generated subscriber, its provider, and its subscription block.

make:model

Generate a model and repository helpers in an explicit package.

bash
forj make:model invoices --package billing

The positional argument is the exact name of an existing table. The generator inspects invoices through the default database connection, so that connection must be available. It does not create or migrate the table, and make:model does not select a named connection. The generated Go type and filename are singularized from the inspected table name; when an exact table is missing, the command may suggest an existing singular or plural variant. Models use --package rather than -d because their placement follows database table ownership.

bash
forj make:model invoices --package billing --remove

If the model already exists, the command updates its schema-derived model definition while preserving the repository section.

By default, an ungrouped model is written under ./internal/models. Use --encrypt column_name or --compress column_name to add the corresponding generated field handling; repeat the option or pass comma-separated names for multiple fields.

make:migration

Generate SQL migration files.

bash
forj make:migration create_invoice_tables

Use --connection for a non-default migration stream. Drivers come from DB_SUPPORTED_DRIVERS, falling back to DB_DRIVER.

bash
forj make:migration create_invoice_tables --remove

Removal deletes timestamped up and down files matching the migration name.

How Package Placement Works

Make commands prefer colocated packages, but command names should stay operationally short.

Use category:action for application command names:

bash
forj make:command reports:sync

This creates internal/reports/sync_cmd.go. Use two segments unless the extra segment is truly part of the operator-facing command. When the command belongs in a deeper package, keep the command name short and use -d to control file placement.

See Naming Conventions for command, job, event, schedule, route, and named resource names.

Bare commands

A bare application command is App-wide:

bash
forj make:command sync

It creates internal/cmd/sync_cmd.go. Use a grouped name when a feature package owns the command:

bash
forj make:command reports:sync

This creates internal/reports/sync_cmd.go and exposes reports:sync. If operator naming and package placement differ, keep the command name short and use -d:

bash
forj make:command reports:sync -d ./internal/ops

The file moves to internal/ops/sync_cmd.go, but the exposed command remains reports:sync.

Go package names

Generated package declarations use compact lowercase Go names. For example:

bash
forj make:controller BillingPortal

This creates a billingportal package, not billing_portal. File names can use underscores, but package names should remain short lowercase identifiers.

Organize by package ownership

Make commands organize code around the package that owns the behavior, not around global controllers, jobs, models, or commands directories.

text
internal/reports/
  controller.go
  sync_cmd.go
  generate_job.go
  daily_schedule.go
  report_generated_event.go
  report_generated_subscriber.go
  report.go
  service.go

Every .go file in that directory declares package reports, so the package name provides the scope. Controller, SyncCmd, GenerateJob, DailySchedule, Report, and Service are different entry points and collaborators inside one ownership boundary.

Read grouped generator names from left to right:

  • forj make:controller reports creates internal/reports/controller.go.
  • forj make:job reports:generate creates internal/reports/generate_job.go.
  • forj make:schedule reports:daily creates internal/reports/daily_schedule.go.

Controllers are package anchors, so the full grouped name becomes the controller package. For jobs and schedules, the leading segments select the package and the final segment names the generated entry point. Start with a flat package such as internal/reports; add nesting only when another package boundary clarifies ownership.

The generated entry points should stay thin:

text
HTTP request      -> reports.Controller                -> reports.Service
CLI command       -> reports.SyncCmd                   -> reports.Service
Queue worker      -> reports.GenerateJob               -> reports.Service
Scheduler process -> reports.DailySchedule             -> reports.Service
Event bus         -> reports.ReportGeneratedSubscriber

They translate input, call package services, and return output. Services own workflows and receive repositories, clients, caches, queues, storage, and events through explicit constructor dependencies. Keeping related entry points together makes imports reveal ownership, keeps Wire constructors close to what they construct, and lets a feature move or shrink as one visible unit.

Shared Options

Removing Generated Resources

Removal uses the same name, package, connection, bus, output, and env-file flags as creation. Pass the same options so the generator resolves the same file and registration entries.

Use --dry-run to preview file and wiring cleanup:

bash
forj make:controller reports --remove --dry-run

--remove resolves the conventional generated path and removes the file at that path; it does not distinguish an untouched generated file from one you later edited. It also removes the matching generated registration entries. Migration removal deletes matching timestamped up/down files by migration name.

Commit or preserve application changes before removal, and inspect --dry-run output carefully. Removal does not search for tests, manually added references, or business code elsewhere, so the following build is what exposes remaining dependencies.

After removing a wired resource, rebuild the graph:

bash
forj build

The build exposes application code that still refers to the removed type, route, command, repository, job, schedule, or subscriber.

Opening Generated Files

Source-generating make commands can open their primary generated file after a successful run:

bash
forj make:controller reports -o
forj make:job billing:sync-reports --open

This applies to controllers, commands, events, subscribers, jobs, schedules, models, and migrations. A migration opens its first generated up migration. make:queue only updates configuration, so it has no source file to open.

Use --no-open to suppress opening for one run. Apps can set:

dotenv
FORJ_MAKE_OPEN=auto
FORJ_EDITOR=

FORJ_MAKE_OPEN accepts:

ValueBehavior
autoOpen only in an interactive terminal when CI is not active.
alwaysTry to open after every successful generator run.
neverOpen only when the command explicitly uses --open or -o.

Automatic opening stays quiet when it cannot resolve an editor. An explicit --open, or FORJ_MAKE_OPEN=always, prints a warning instead.

Set FORJ_EDITOR to pin the command when automatic detection is not what you want:

dotenv
FORJ_EDITOR="code --reuse-window --goto {location}"
FORJ_EDITOR="goland --line {line} {file}"

The command supports {file} for the absolute generated path, {line} for the line number, and {location} for both as path:line. Without FORJ_EDITOR, GoForj checks terminal editor hints, then running GUI editors, then commands on PATH. Its editor preference is GoLand, Cursor, VS Code, Zed, then IntelliJ IDEA, while preferring an already-running editor over launching a different one.

Output Overrides

Ungrouped resources use these source-owned defaults:

CommandDefault output
make:command./internal/cmd
make:job./internal/jobs
make:schedule./internal/schedules
make:event./internal/events
make:subscriber./internal/events
make:model./internal/models

Use -d when the default grouped package path is not the package you want:

bash
forj make:command reports:sync -d ./internal/billing/reports
forj make:job billing:sync-reports -d ./internal/ops
forj make:schedule reports:daily -d ./internal/billing/reports
forj make:event billing:invoice-paid -d ./internal/billing/events
forj make:subscriber billing:invoice-paid -d ./internal/billing/events

The override controls the file location and package name. The grouped command name can still express the command, job, or event identity.

make:model uses --package instead of -d because models and repositories are generated around database table ownership.

Ownership and Verification

What Belongs to You

Generated files are starting points. Your App still owns:

  • business logic and service methods
  • constructor parameters for application services
  • route behavior, validation, and response shape
  • command input parsing and console output
  • job payloads and handler behavior
  • schedule intervals and handler behavior
  • event payloads and subscribers
  • migration SQL
  • model relationships and repository options

Keep dependencies explicit. If a generated controller, command, or job needs an application service, add that service constructor to the right provider set and let Wire pass it in.

Verify

After running a make command, verify the graph and the exposed runtime surface:

bash
forj build
forj route:list

Use route:list for controllers. For commands, run the generated command signature through forj <command>. Use forj run <command> only when you want to force App command execution explicitly.

Common Mistakes

Common mistakes

  • Do not create one package per generated file.
  • Do not collect every controller, job, command, and service in one global package.
  • Do not make operator-facing command names longer merely to mirror package depth.
  • Do not use snake case package names.
  • Do not put business workflows directly in generated entry points.
  • Do not hand-edit generated wiring before using the make command path.

Next Steps