Skip to main content

Blog System Tutorial — Overview

Goal: Give you a complete picture of what you will build and how the steps fit together before you write a single line of code.

Files touched: (none — overview only)

Concepts: Domain-Driven Design with Webda, permissions, one domain served over REST, GraphQL and gRPC.

What we'll build​

By the end of this tutorial you will have a blog API exposing the same domain through three transports:

TransportEntry pointDescription
RESThttp://localhost:18080/CRUD routes for every model, plus every @Operation
GraphQLhttp://localhost:18080/graphqlSchema generated from the models: queries, mutations, subscriptions
gRPClocalhost:50051 (h2c)Services generated from the operations into .webda/app.proto

The domain is a classic blog with a social layer:

User ──< Post ──< Comment
╲
╲──< PostTag >── Tag (many-to-many via join table)

User ──< UserFollow >── User (self-referential follower graph)

Every model decides who may do what in its canAct method. Webda denies by default: a model without a permission check is refused on every transport. The blog rules are simple: anyone reads, logged-in users write, and only the author edits their own content.

Final API surface (REST excerpt)​

MethodPathDescription
PUT/users/registerCreate an account and open the session
PUT/users/loginOpen a session
PUT/usersQuery users (WebdaQL in the body)
GET / PUT / PATCH / DELETE/users/{uuid}Single-user CRUD (owner only for writes)
POST / PUT/postsCreate / query posts
GET / PUT / PATCH / DELETE/posts/{slug}Single-post CRUD (custom primary key)
PUT/posts/{slug}/publishModel operation
POST / PUT/comments, /tagsCreate / query comments and tags
POST/postTagsTag a post (join table with a composite key)
GET/versionService operation with a custom route

GraphQL and gRPC expose the same model operations and permissions.

Prerequisites​

  • Node.js ≥ 22.0.0 (node -v)
  • npm (bundled with Node.js) or pnpm
  • curl (for the REST verification steps)
  • jq (optional, for pretty-printing JSON)
  • grpcurl (page 10 only — brew install grpcurl)
  • Docker is not required: data lives in the default in-memory store

The plan​

PageWhat you do
01 — SetupCreate the project with npm create @webda
02 — User modelFirst model, JSDoc validation, canAct, register/login operations
03 — Post modelCustom primary key (slug), BelongTo, author-only permissions
04 — Comment modelTwo BelongTo relations, the author set by the server
05 — Tag + PostTagStatic canAct, join table with a composite primary key
06 — UserFollowSelf-referential composite-key model
07 — Service layer@WebdaModda services, @Bean, @Operation on services
08 — REST tourHow routes, queries, errors and permissions map to HTTP
09 — GraphQLAdd @webda/graphql, run queries and mutations
10 — gRPCAdd @webda/grpc, call with grpcurl
11 — Next stepsAuthentication, persistent stores, deployment, testing

Quick clone — I just want to browse the finished code​

The finished application is sample-apps/blog-system in the monorepo. It runs inside the pnpm workspace:

git clone https://github.com/loopingz/webda.io.git
cd webda.io
pnpm install && pnpm -r run build
cd sample-apps/blog-system
pnpm run build
pnpm run debug # https://localhost:18080 (self-signed certificate), admin UI at /admin/

The sample has a few extras this tutorial leaves out (binary attachments, an admin web UI, an audit log, MCP) and serves HTTPS with autoTls. Every code block in this tutorial comes from sample-apps/blog-system/src/.

What's next​

→ 01 Setup