Skip to main content

Routing

Overview

Feature routes append via ext.routes inside Extension.register. Use Decidim::RestFull::Routing in monorepo gems (required for new work). Core routes stay in decidim-restfull-core/config/routes.rb.

Start with Recipe and Boot and extension if routes do not appear.

When to use

  • You expose new paths for a feature gem.
  • You add /sync aliases for async mutations.

Example

1. Read-only routes

decidim-restfull-blogs/lib/decidim/rest_full/blogs/engine.rb

ext.routes do
Decidim::RestFull::Routing.read_resources(
self,
:blog_components,
controller: "components/blog_components",
only: [:index, :show]
)
end

Pass self (the router inside the block) as the first argument. Runtime gates use organization Toggle via ModuleAvailability (HTTP 501), not route constraints.

2. Async CRUD + sync aliases

Decidim::RestFull::Routing.async_resources(
self,
:blogs,
controller: "blogs/blogs",
only: [:index, :show, :create, :update, :destroy]
)

async_resources adds collection POST …/sync and member PUT/DELETE …/sync for create/update/destroy in only:.

Extra member routes (draft proposal publish):

Decidim::RestFull::Routing.async_resources(
self,
:draft_proposals,
controller: "draft_proposals/draft_proposals",
only: [:index, :show, :create, :update, :destroy],
member: { post: { publish: :publish, "publish/sync": :publish_sync } }
)

3. Escape hatch — raw resources

Use when the Routing DSL cannot express a route (forms questionnaire_responses member update_forbidden):

resources :questionnaire_responses, only: [:show, :destroy],
controller: "/decidim/api/rest_full/forms/questionnaire_responses" do
member do
put "/", action: :update_forbidden
delete "sync", action: :destroy_sync
end
end

Always use an absolute controller path: /decidim/api/rest_full/<feature>/<controller>.

4. Register api_job and wire the controller

Engine:

ext.api_job "draft_proposals#create", ->(ctx, p) {
Proposals::DraftProposalsOperations.new(ctx, p).create!
}

Controller: enqueue_rest_full_api_job!("draft_proposals#create") with the identical string. See Async.

5. Register RSwag spec globs

ext.rswag_specs(
File.join(Widgets::ENGINE_ROOT, "spec/requests/decidim/api/rest_full/widgets/**/*_spec.rb")
)

Rules

RuleDetail
Routing DSLRequired for new monorepo gems; raw resources only for escape hatches.
No duplicate blocksSame route block registered twice raises DuplicateRouteBlockError.
Runtime gateOrganization Toggle + ModuleAvailability (501 when off). Do not use boot Configuration.enable_*_api.
Controller pathRouting builds /decidim/api/rest_full/… from the controller: segment.
CasePath
Routing DSLdecidim-restfull-core/spec/lib/decidim/rest_full/routing_spec.rb
Sync routesdecidim-restfull-proposals/spec/requests/.../draft_proposals_controller_create_spec.rb
Route bootdecidim-restfull-core/spec/requests/.../routes_boot_spec.rb

See also