Skip to main content

Routing

Webda routes derive from two sources: the @Route decorator on service methods, and the auto-generated REST endpoints from model operations via DomainService. Both feed into the Router which maintains an OpenAPI spec.

@Route decorator​

Register a service method as an HTTP endpoint:

import { Service, Route, WebContext, Bean } from "@webda/core";

@Bean
export class StatusService extends Service {
@Route("/status", ["GET"])
async getStatus(ctx: WebContext): Promise<void> {
ctx.write({ status: "ok", uptime: process.uptime() });
}

@Route("/admin/flush", ["POST"])
async flush(ctx: WebContext): Promise<void> {
await this.clearCache();
ctx.statusCode(204);
}
}

@Route signature: @Route(url, methods, openapi?)

ArgumentTypeDescription
urlstringPath pattern. Use {param} for path parameters
methodsstring[]HTTP methods: ["GET"], ["POST", "PUT"], etc.
openapiobjectOpenAPI metadata override (summary, description, tags, ...)

Path parameters​

@Route("/posts/{slug}", ["GET"])
async getPost(ctx: WebContext): Promise<void> {
const slug = ctx.getPathParameter("slug");
const post = await Post.ref(slug).get();
if (!post) {
ctx.statusCode(404);
return;
}
ctx.write(post);
}

Auto-generated REST routes from models​

DomainService (and RESTOperationsTransport) reads the model graph and generates standard CRUD routes automatically:

HTTP methodURLOperation
GET/postsList (with WQL query param)
POST/postsCreate
GET/posts/:slugGet one
PUT/posts/:slugReplace
PATCH/posts/:slugPartial update
DELETE/posts/:slugDelete
POST/posts/:slug/publishCustom @Operation() method
GET/posts/:slug/commentsList related Contains<Comment>
POST/posts/:slug/commentsCreate comment in relation

Route URLs are derived from:

  • The model class name (pluralized as /posts for Post)
  • The [WEBDA_PLURAL] symbol if set
  • The [WEBDA_PRIMARY_KEY] fields (:slug for Post, :uuid for UuidModel)

Query parameters for list endpoints​

The list endpoint (GET /posts) accepts:

  • ?query=<WQL> — filter expression
  • ?limit=<n> — max results
  • ?offset=<token> — continuation token
curl "https://localhost:18080/posts?query=status%20%3D%20'published'&limit=10"

Content negotiation​

The Router supports Accept headers. By default it returns JSON. REST and gRPC transports co-exist on the same application — the Router dispatches based on content type and protocol.

Viewing routes at runtime​

Check the OpenAPI spec to see all routes:

curl -sk https://localhost:18080/openapi.json | jq '.paths | keys'

Or use the WebUI at /admin (if ResourceService is configured).

OpenAPI spec generation​

The Router automatically builds an OpenAPI 3.0 spec from:

  • @Route decorators (with optional openapi metadata)
  • Model CRUD routes (generated from model schemas)
  • @Operation methods (generated from operation schemas)
@Route("/posts", ["POST"], {
summary: "Create a new post",
tags: ["posts"],
requestBody: {
required: true,
content: { "application/json": { schema: { $ref: "#/components/schemas/PostInput" } } }
}
})
async createPost(ctx: WebContext): Promise<void> { ... }

Verify​

# View all routes via OpenAPI
cd sample-apps/blog-system
# (server running at https://localhost:18080)

curl -sk https://localhost:18080/openapi.json | jq '.paths | keys | .[:15]'
[
"/comments",
"/comments/{uuid}",
"/posts",
"/posts/{slug}",
"/posts/{slug}/publish",
"/posts/{slug}/tags",
"/posts/{slug}/tags/{tag}",
"/tags",
"/tags/{slug}",
"/users",
"/users/{uuid}",
"/users/{uuid}/follow",
"/users/{uuid}/unfollow",
"/users/login",
"/users/logout"
]

Note: Requires the blog-system server to be running.

See also​