Named Resources
A named resource gives application code a stable, typed handle such as uploads, critical, or audit while configuration chooses the backing driver.
That separation is one of GoForj's main configuration strengths: the same service can use an in-process queue locally and Redis in production without changing the queue name or dispatch code.
flowchart LR
service[Application service] --> accessor[Queues().Critical()]
dev[Local config<br/>workerpool] --> driver[Selected queue driver]
prod[Production config<br/>redis] --> driver
driver --> accessor
accessor --> queue[critical queue]
The accessor is compiled from the Project's named resource configuration. The active driver is selected at startup from the drivers already compiled into the App.
Common Named Resources
Resource families with generated accessors include:
- caches
- storage disks
- queues
- event buses
- mailers
- database connections
Other operational objects also have stable names, but they are registered rather than exposed as infrastructure accessors:
- jobs
- schedules
- routes
- metric series
- inspect records
Use stable names because they appear in logs, metrics, inspects, route lists, worker output, and Lighthouse surfaces.
Default and Named Scopes
GoForj Apps usually provide a default resource and optional named resources when the owning component is enabled. A Project without Cache, File Storage, Background Jobs, or Events does not render accessors for that resource family.
Examples:
CACHE_DRIVER=memory
CACHE_SESSIONS_DRIVER=redis
STORAGE_DRIVER=local
STORAGE_PUBLIC_DRIVER=local
STORAGE_UPLOADS_DRIVER=s3
QUEUE_DRIVER=workerpool
QUEUE_CRITICAL_DRIVER=redis
EVENTS_DRIVER=inproc
EVENTS_AUDIT_DRIVER=nats
MAIL_DRIVER=log
MAIL_TRANSACTIONAL_DRIVER=resendDefault scopes use the base prefix. Named scopes use _<NAME>_ between the primitive prefix and setting name.
Generated Accessors
Named resources are exposed through generated accessors.
Examples:
app.Cache()
app.Caches().Sessions()
app.Storage()
app.Storage().Public()
app.Storage().Uploads()
app.Queue()
app.Queues().Critical()
app.Bus()
app.Events().Audit()
app.Mail()
app.Mail().Transactional()Accessors come from configuration. After adding or renaming named resources, run forj build; forj dev does this automatically for apps listed in dev.apps.
Use a Named Resource in a Service
Inject the owning manager once, then choose the named resource where the workflow needs it:
type AlertService struct {
queues *queues.Manager
}
func NewAlertService(queueManager *queues.Manager) *AlertService {
return &AlertService{queues: queueManager}
}
func (s *AlertService) Dispatch(ctx context.Context, payload []byte) error {
critical := s.queues.Critical()
_, err := critical.WithContext(ctx).Dispatch(
queue.NewJob(AlertJobTypeName).Payload(payload),
)
return err
}The service asks for critical; it does not know whether that queue is backed by workerpool, Redis, NATS, SQS, or another supported driver. Add NewAlertService to the App's service provider set and let Wire supply the manager.
Change a Driver Without Changing the Service
Keep the named contract and change only runtime selection:
QUEUE_SUPPORTED_DRIVERS=workerpool,redis
QUEUE_CRITICAL_DRIVER=workerpoolQUEUE_SUPPORTED_DRIVERS=workerpool,redis
QUEUE_CRITICAL_DRIVER=redis
QUEUE_ADDR=redis:6379Because both drivers are already in QUEUE_SUPPORTED_DRIVERS, this switch needs a restart, not regeneration. Adding a new supported driver or a new named accessor requires forj build.
Fail-Fast Invariants
Named accessors represent generated invariants.
If an accessor is present, its generated resource is required. Missing runtime driver settings use local fallbacks only when the fallback driver is built into the App. New Projects write explicit active selections, and a selected MySQL-only or Postgres-only database must receive its environment configuration.
If the generated code and runtime environment disagree in a way that cannot be satisfied, failing fast is better than silently returning nil or pretending a resource exists.
This makes deployment mistakes visible early.
Operational Naming
Good names are stable, bounded, and operator-readable.
Examples:
sessionsuploadscriticalauditemailsreportsemails:sendreports:dailyGET /api/v1/users/:id
Avoid names derived from user input, request payloads, email addresses, IDs, or unbounded values.
Use Naming Conventions when choosing names for commands, jobs, schedules, events, routes, and named resources.
Metrics and Labels
Use named resources as metric labels when labels are needed.
Prefer:
- route name or route pattern
- job name
- queue name
- schedule name
- disk name
- cache name
Avoid raw paths, raw SQL, user IDs, emails, or arbitrary payload values.
Common Mistakes
Common mistakes
- Do not add named resources by hand in generated accessor files.
- Do not treat missing named accessors as optional runtime state.
- Do not use cache names or queue names as arbitrary user input.
- Do not change business code when only a named resource driver changes.
- Do not use different names for the same resource across docs and examples.
Next Steps
- Code Generation explains regeneration.
- Drivers and Adapters explains backend selection.
- Naming Conventions defines stable resource names.
- Libraries contains package-level resource behavior.