#Route groups
A group shares a path prefix and middlewares between routes. It organizes an API by resource (/projects), by audience (/admin) or by version (/v2).
const const admin: RouteGroup<EmptyObject & {}, unknown> admin = createRouteGroup<"/admin", readonly [unknown]>(prefix: "/admin", middlewares?: readonly [Middleware<unknown>] | undefined): RouteGroup<EmptyObject & {}, unknown>Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createRouteGroup ('/admin', [const requireAdmin: Middleware<unknown> requireAdmin ])
const admin: RouteGroup<EmptyObject & {}, unknown> admin .RouteGroup<EmptyObject & {}, unknown>.defineRoute<"/users", EmptyObject, false, unknown[]>(route: RouteConfig<"/users", EmptyObject, EmptyObject & {}, false, unknown[], unknown>): void defineRoute ({
RouteConfig<"/users", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.path: "/users" path : '/users',
RouteConfig<"/users", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.handler: RouteHandler<EmptyObject, {}, false, unknown> handler : () => [{ id: string id : '1', name: string name : 'Ada' }],
})The route answers GET /admin/users, after requireAdmin.
#Nested groups
A group creates sub-groups. The prefixes add up, and so do the middlewares, from the outermost group to the route:
const const admin: RouteGroup<EmptyObject & {}, unknown> admin = createRouteGroup<"/admin", readonly [unknown]>(prefix: "/admin", middlewares?: readonly [Middleware<unknown>] | undefined): RouteGroup<EmptyObject & {}, unknown>Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createRouteGroup ('/admin', [const requireAdmin: Middleware<unknown> requireAdmin ])
const const billing: RouteGroup<EmptyObject & {} & {}, unknown> billing = const admin: RouteGroup<EmptyObject & {}, unknown> admin .RouteGroup<EmptyObject & {}, unknown>.createGroup<"/billing", readonly [unknown]>(prefix: "/billing", middlewares?: readonly [Middleware<unknown>] | undefined): RouteGroup<EmptyObject & {} & {}, unknown>Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createGroup ('/billing', [const requireBillingAccess: Middleware<unknown> requireBillingAccess ])
const billing: RouteGroup<EmptyObject & {} & {}, unknown> billing .RouteGroup<EmptyObject & {} & {}, unknown>.defineRoute<"/invoices", EmptyObject, false, unknown[]>(route: RouteConfig<"/invoices", EmptyObject, EmptyObject & {} & {}, false, unknown[], unknown>): void defineRoute ({
RouteConfig<P extends string, S extends RouteSchema, TParentParams = EmptyObject, TRawBody extends boolean = false, TProvided extends readonly unknown[] = unknown[], TGroupContext = unknown>.method?: HttpMethod | undefinedDefaults to get.
method : 'post',
RouteConfig<"/invoices", EmptyObject, EmptyObject & {} & {}, false, unknown[], unknown>.path: "/invoices" path : '/invoices',
RouteConfig<"/invoices", EmptyObject, EmptyObject & {} & {}, false, unknown[], unknown>.handler: RouteHandler<EmptyObject, {}, false, unknown> handler : () => ({ created: boolean created : true }),
})POST /admin/billing/invoices runs requireAdmin, then requireBillingAccess, then the route middlewares.
#Parameters in the prefix
A prefix can hold parameters. Every route of the group receives them, typed:
const const projects: RouteGroup<EmptyObject & {
projectId: string;
}, unknown>
projects = createRouteGroup<"/projects/:projectId", unknown[]>(prefix: "/projects/:projectId", middlewares?: readonly Middleware<unknown>[] | undefined): RouteGroup<EmptyObject & {
projectId: string;
}, unknown>
Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createRouteGroup ('/projects/:projectId')
const const tasks: RouteGroup<EmptyObject & {
projectId: string;
} & {}, unknown>
tasks = const projects: RouteGroup<EmptyObject & {
projectId: string;
}, unknown>
projects .RouteGroup<EmptyObject & { projectId: string; }, unknown>.createGroup<"/tasks", unknown[]>(prefix: "/tasks", middlewares?: readonly Middleware<unknown>[] | undefined): RouteGroup<EmptyObject & {
projectId: string;
} & {}, unknown>
Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createGroup ('/tasks')
const tasks: RouteGroup<EmptyObject & {
projectId: string;
} & {}, unknown>
tasks .RouteGroup<EmptyObject & { projectId: string; } & {}, unknown>.defineRoute<"/:taskId", EmptyObject, false, unknown[]>(route: RouteConfig<"/:taskId", EmptyObject, EmptyObject & {
projectId: string;
} & {}, false, unknown[], unknown>): void
defineRoute ({
RouteConfig<"/:taskId", EmptyObject, EmptyObject & { projectId: string; } & {}, false, unknown[], unknown>.path: "/:taskId" path : '/:taskId',
RouteConfig<"/:taskId", EmptyObject, EmptyObject & { projectId: string; } & {}, false, unknown[], unknown>.handler: RouteHandler<EmptyObject, {
projectId: string;
taskId: string;
}, false, unknown>
handler : ({ req: RouteRequest<EmptyObject, {
projectId: string;
taskId: string;
}, false>
req }) => req: RouteRequest<EmptyObject, {
projectId: string;
taskId: string;
}, false>
req .DotpressRequest<{ projectId: string; taskId: string; }, QueryValues, unknown, UploadedFiles>.params: {
projectId: string;
taskId: string;
}
params ,
})Hover req.params: { projectId: string; taskId: string }. A member check written once on the /projects/:projectId group protects every route below it.
#Typed context
A group with a typed middleware passes its guarantee to all its routes, sub-groups included. With requireAuth on a group, ctx.user is defined in every route of the group:
const const account: RouteGroup<EmptyObject & {}, AuthenticatedContext> account = createRouteGroup<"/account", readonly [AuthenticatedContext]>(prefix: "/account", middlewares?: readonly [Middleware<AuthenticatedContext>] | undefined): RouteGroup<EmptyObject & {}, AuthenticatedContext>Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createRouteGroup ('/account', [const requireAuth: Middleware<AuthenticatedContext>Requires an authenticated user (401 UNAUTHORIZED otherwise). In the
handlers of the routes using it, ctx.user and ctx.auth are defined.
requireAuth ])
const account: RouteGroup<EmptyObject & {}, AuthenticatedContext> account .RouteGroup<EmptyObject & {}, AuthenticatedContext>.defineRoute<"/profile", EmptyObject, false, unknown[]>(route: RouteConfig<"/profile", EmptyObject, EmptyObject & {}, false, unknown[], AuthenticatedContext>): void defineRoute ({
RouteConfig<"/profile", EmptyObject, EmptyObject & {}, false, unknown[], AuthenticatedContext>.path: "/profile" path : '/profile',
RouteConfig<"/profile", EmptyObject, EmptyObject & {}, false, unknown[], AuthenticatedContext>.handler: RouteHandler<EmptyObject, {}, false, AuthenticatedContext> handler : ({ user: AuthUserAuthenticated user (authPlugin). Use requireAuth to require it.
user }) => ({ name: string name : user: AuthUserAuthenticated user (authPlugin). Use requireAuth to require it.
user .AuthUser.name: string name }),
})#Groups of a router
createRouteGroup() creates a group on the global router. With an explicit router, use router.createGroup():
export const const projectsRouter: Router projectsRouter = function createRouter(): RouterCreates an isolated route registry.
createRouter ()
const const projects: RouteGroup<EmptyObject & {}, unknown> projects = const projectsRouter: Router projectsRouter .RouteGroup<EmptyObject, unknown>.createGroup<"/projects", unknown[]>(prefix: "/projects", middlewares?: readonly Middleware<unknown>[] | undefined): RouteGroup<EmptyObject & {}, unknown>Creates a nested group, inheriting the prefix and middlewares. Context
properties guaranteed by the group middlewares are typed in the
handlers of its routes.
createGroup ('/projects')
const projects: RouteGroup<EmptyObject & {}, unknown> projects .RouteGroup<EmptyObject & {}, unknown>.defineRoute<"/", EmptyObject, false, unknown[]>(route: RouteConfig<"/", EmptyObject, EmptyObject & {}, false, unknown[], unknown>): void defineRoute ({ RouteConfig<"/", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.path: "/" path : '/', RouteConfig<"/", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.handler: RouteHandler<EmptyObject, {}, false, unknown> handler : () => [] })
const projects: RouteGroup<EmptyObject & {}, unknown> projects .RouteGroup<EmptyObject & {}, unknown>.defineRoute<"/:projectId", EmptyObject, false, unknown[]>(route: RouteConfig<"/:projectId", EmptyObject, EmptyObject & {}, false, unknown[], unknown>): void defineRoute ({ RouteConfig<"/:projectId", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.path: "/:projectId" path : '/:projectId', RouteConfig<"/:projectId", EmptyObject, EmptyObject & {}, false, unknown[], unknown>.handler: RouteHandler<EmptyObject, {
projectId: string;
}, false, unknown>
handler : ({ req: RouteRequest<EmptyObject, {
projectId: string;
}, false>
req }) => req: RouteRequest<EmptyObject, {
projectId: string;
}, false>
req .DotpressRequest<{ projectId: string; }, QueryValues, unknown, UploadedFiles>.params: {
projectId: string;
}
params })A route at path: '/' answers the prefix itself: GET /projects. Prefixes start with /: to share middlewares without a prefix, give them to each route, or use a global middleware.
See the Tasks API example for nested groups with access checks.