Skip to main content

Boot and extension

Overview

For: gem maintainers wiring Decidim::RestFull::Extension.register — read this after Recipe when routes or scopes do not appear.

Boot sequence

sequenceDiagram
participant Gem as FeatureEngine
participant Ext as Extension
participant Registry as RouteRegistry
participant Core as CoreEngine
participant Reloader as RoutesReloader
participant DK as Doorkeeper

Gem->>Ext: Extension.register (initializer before draw_routes)
Ext->>Registry: draw_api_routes (route blocks)
Ext->>Ext: oauth_scopes → doorkeeper_optional_scopes
Core->>Core: rest_full.draw_routes → Routes.mount! (RouteSet#append)
Reloader->>Reloader: clear! + load routes.rb + finalize!
Note over Reloader,Registry: append blocks run inside finalize! (Devise configures Warden once)
Core->>DK: DoorkeeperConfig.merge_optional_scopes! on late Extension.register
Note over Gem,DK: Late Extension.register → append_pending! + merge! if app initialized

Initializer anchors

Register your engine initializer before core mounts routes and merges scopes:

initializer "rest_full.widgets.extension", before: "rest_full.draw_routes" do
Decidim::RestFull::Extension.register(:widgets) do |ext|
ext.oauth_scopes :widgets
ext.permissions(:widgets, "widgets.read", group: :widgets)
ext.routes do
Decidim::RestFull::Routing.read_resources(self, :widgets, controller: "widgets/widgets", only: [:index, :show])
end
ext.rswag_specs File.join(Widgets::ENGINE_ROOT, "spec/requests/decidim/api/rest_full/widgets/**/*_spec.rb")
ext.open_api_definitions File.join(Widgets::ENGINE_ROOT, "lib/decidim/rest_full/widgets/test_definitions.rb")
end
end
AnchorPurpose
before: "rest_full.draw_routes"Route blocks collected before Routes.mount! queues the append
before: "rest_full.scopes"Belt-and-suspenders for OAuth scope collection

Extension.register

Public DSL: decidim-restfull-core/lib/decidim/rest_full/extension.rb.

MethodRegisters
oauth_scopesDoorkeeper optional scopes (merged at after_initialize)
permissionsSystem-admin API permission checkboxes
routesRoute block → RouteRegistry
api_jobAsync mutation handler
rswag_specsOpenAPI request spec globs
open_api_definitionsSchema barrel for OpenAPI
webhooksActiveSupport::Notifications patterns

When Extension.register runs after routes are already drawn, Routes.append_pending! adds new blocks without a full redraw.

Registering the same route block twice raises Decidim::RestFull::Core::DuplicateRouteBlockError (fail-fast; no duplicate Rails routes).

OAuth scopes

Core optional scopes live on DoorkeeperConfig::CORE_OPTIONAL_SCOPES.

Gems add new scopes only:

ext.oauth_scopes :widgets   # skip if already in core list
ext.permissions(:widgets, "widgets.read", group: :widgets)

Merge runs at boot in the rest_full.scopes initializer. Late Extension.register calls DoorkeeperConfig.merge_optional_scopes! when Rails.application.initialized?.

See Scopes and permissions.

Host app extensions

Host apps may register from config/initializers/ inside Rails.application.config.after_initialize:

Rails.application.config.after_initialize do
require "decidim/rest_full"

Decidim::RestFull::Extension.register(:my_feature) do |ext|
ext.routes { get "my_resource/:id", to: "/decidim/my_app/my_resources#show" }
end
end

Extension.register appends routes and re-merges scopes when the app is already initialized. See Host app extensions.

warning

Host controllers must not live under Decidim::Api::RestFull::* (Zeitwerk conflict with decidim-api). Use an app-specific namespace.

RouteRegistry

RouteRegistry collects route blocks from feature gems and draws them under /api/rest_full/v<major.minor>/ on Decidim::Core::Engine.routes.

Boot entry: Decidim::RestFull::Routes.mount! from initializer rest_full.draw_routes — same pattern as Decidim Admin/System (RouteSet#append). Rails' RoutesReloader runs a single finalize!; Devise configures Warden there. We never call finalize! or patch Warden.

Late Extension.register uses Routes.append_pending! (draw without finalize). Isolated specs may call RouteRegistry.apply! on a throwaway RouteSet.

CasePath
Route registrydecidim-restfull-core/spec/lib/decidim/rest_full/core/route_registry_spec.rb
Routing DSLdecidim-restfull-core/spec/lib/decidim/rest_full/routing_spec.rb
Routes bootdecidim-restfull-core/spec/requests/decidim/api/rest_full/routes_boot_spec.rb

See also