Skip to content

Golden Path β€” Routes (routes/*) ​

Route modules are the HTTP adapter around your service: they parse the request, call one or a few service methods, and return the result. Keeping them small matters because the same service logic is what you want to call from elsewhere (CLI tasks, other routes, background work) without copying request/response handling.

Think of routes as a thin wrapper β€” validation and transport at the edge, decisions and persistence in services.

The same Fastify + raclette config rules apply whether you use one route file per endpoint (recommended here) or register several handlers from a single module (for example the cli-connector plugin’s src/backend/routes.ts in the _PLUGINS repo β€” a flatter layout that still wires method, schema, and config the same way).

Fastify route options ​

Each registration passes a route options object to the plugin-scoped Fastify instance (fastify.get, fastify.post, fastify.patch, fastify.put, fastify.delete, …). At minimum you will set:

  • method / url / handler β€” HTTP surface and implementation.
  • schema β€” request/response validation and serialization (see the Fastify guide on Validation and Serialization). Golden Path examples use TypeBox via fastify.registerSchema; see Schema.
  • config β€” raclette-specific metadata (below). This is Fastify’s route config (context config), extended by raclette for store and realtime integration.

How URLs and names become $data operations ​

The first URL path segment after your plugin’s route prefix is treated as the entity key in generated-config.ts (unless you remap it with entity mapping). Further non-parameter segments are folded into the operation name together with the HTTP verb.

Raclette does not infer config.type or broadcast behavior from the function or file name β€” you must set config.type and config.broadcastChannels explicitly when you want the frontend pipeline to classify reads/writes and subscribe to the right channels.

For the exact verb β†’ operation prefix mapping (get, create, update, delete) and how path segments are concatenated, see Frontend entity mapping β€” Operation names.

Raclette route config (store + channels) ​

Raclette augments Fastify’s FastifyContextConfig with optional fields used when generating frontend/generated-config.ts (see @raclettejs/core/services/backend/src/types/custom-fastify.ts in the monorepo for the declaration).

  • config.type β€” store action classification for the frontend (dataRead, dataCreate, dataUpdate, dataDelete, …).
  • config.broadcastChannels β€” string names emitted on success; the generator turns these into the channels array in generated-config.ts.

Legacy config.broadcast is not part of the Golden Path; prefer broadcastChannels only.

config.type (store action) ​

config.type tells the frontend pipeline which store action classification applies (for example dataRead, dataCreate, dataUpdate, dataDelete, dataHardDelete, dataMove).

typescript
config: {
  type: "dataUpdate",
}

Setting this correctly lets workbench and $data update lists and detail views without each plugin re-implementing store merges for standard CRUD.

config.broadcastChannels ​

Declare which channels this route participates in. You can list one or many channels; keep names aligned with events your service emits when mutating data.

typescript
config: {
  // One or many channels, depending on your use case
  broadcastChannels: ["exampleUpdated", "dataUpdated"],
}

Default platform events (dataUpdated, …) ​

Include global channel names when you want cross-plugin or portal-wide listeners to react β€” for example dataCreated, dataUpdated, and dataDeleted (and the broadcastData* variants used in some core routes) so the UI stack knows what kind of mutation happened. Those names are part of the shared event map in the backend (eventBusEventMap).

The generated config FAQ documents how channels appear on each operation in generated-config.ts and how they map to $socket usage.

Route registration index ​

Use a dedicated routes/index.ts that imports small route modules and registers them on the same PluginFastifyInstance:

typescript
import type { PluginFastifyInstance } from "@raclettejs/core"
import { registerGetAllExampleRoute } from "./route.example.getAll"
import { registerGetExampleRoute } from "./route.example.get"
import { registerPostExampleRoute } from "./route.example.post"
// …

export const registerRoutes = async (fastify: PluginFastifyInstance) => {
  await registerGetAllExampleRoute(fastify)
  await registerGetExampleRoute(fastify)
  await registerPostExampleRoute(fastify)
}

Full CRUD walkthroughs live in the cookbook (for example routes index and single route).

See also ​