Skip to content

feat(examples): make lumibase the default client in nextjs-blog + quickstart (EN/VI) - #474

Open
khuepm wants to merge 2 commits into
mainfrom
feature/334-lumibase-default-example
Open

feat(examples): make lumibase the default client in nextjs-blog + quickstart (EN/VI)#474
khuepm wants to merge 2 commits into
mainfrom
feature/334-lumibase-default-example

Conversation

@khuepm

@khuepm khuepm commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

Làm cho lumibase trở thành client mặc định của example Next.js và của tutorial quickstart, đồng thời khiến examples/nextjs-blog thật sự chạy được độc lập ngoài monorepo.

Trước PR này example dùng plugin GraphQL với @lumibase/sdk: workspace:* (không cài được từ registry), còn tutorial dạy fetch trước SDK và mô tả một API không tồn tại: createClient(...).items(c).readMany(...).

Closes #334. Bằng chứng nghiệm thu đầy đủ: #334 (comment)

Loại thay đổi

  • feat — Tính năng mới
  • docs — Cập nhật tài liệu

Phase / Feature liên quan

Lane A — A03 (#334), parent #331. Contract consumer theo A02 (#332, PR #468). Bằng chứng chuyển sang #448.


✅ Definition of Done (DoD)

Code Quality

  • Không có TypeScript error — pnpm typecheck 18/18 successful; thêm tsc --noEmit exit 0 ở thư mục tạm ngoài workspace
  • Tất cả tests pass — không sửa code có test; examples/* nằm ngoài pnpm workspace nên không có suite riêng
  • Không có lint error — không đụng package có lint config
  • Self-review: đã đọc lại diff line-by-line

Architecture & Docs

  • architecture.mdn/a, không đổi cấu trúc hệ thống
  • apps/cms/openapi.yamln/a, không thêm/sửa endpoint nào
  • packages/sdk types — n/a, không đổi API; PR này chỉ dùng SDK hiện có
  • Docs người dùng — cập nhật docs/en/tutorials/nextjs-quickstart.md + docs/vi/... (bắt buộc song ngữ), đã parity → 0 problems, verify → 26 claims 0 stale, stamp-pair --verified → cả hai version: 3, hash khớp 8517ebf6d2842ff5, detect 148/148 up-to-date

Testing

  • Unit test — n/a, không có logic mới trong package có test
  • Integration test — n/a, thay vào đó là nghiệm thu end-to-end thật (bên dưới)
  • Property-based test — n/a
  • Test thủ công trên Docker runtime — đã chạy: CMS 1.0.0-rc.1 (serve.ts, docker mode) trên DB riêng lumibase_a03

Runtime Compatibility

  • Cloudflare Workers — n/a với PR này: chỉ đổi code frontend example + docs, không thêm route/service nào phía CMS
  • Node.js / Docker — đã verify trực tiếp trên Node runtime
  • Feature flag — n/a, không dùng API riêng theo runtime

Security

  • Không hardcode secret/credential — token đọc từ env; mọi giá trị trong docs là placeholder
  • Input validation — n/a, không nhận payload mới
  • Multi-tenant isolation — verify negative: sai X-Lumi-Site401, không trả nội dung site khác
  • Token không log/lưu plaintext — verify token không có trong .next/static; .env.example ghi rõ không dùng NEXT_PUBLIC_

Database / Migrations

  • Migration — n/a, không đổi schema

Conventional Commits

  • feat(examples): make lumibase the default client in nextjs-blog
  • docs(tutorials): teach the SDK first in the Next.js quickstart (EN+VI)

Screenshots / Recordings

Không có UI mới. Bằng chứng là output build thật, trích từ next build ở thư mục tạm ngoài repo:

Route (app)                                 Size  First Load JS  Revalidate  Expire
┌ ○ /                                      165 B         106 kB          1m      1y
├ ○ /_not-found                            995 B         103 kB
└ ● /posts/[id]                            165 B         106 kB
    ├ /posts/Cpxfq5vcVlo9ZAlNT04gu
    └ /posts/Y6P-XLshyR45Ssa3uLE6w

Đúng 2 id published được prerender; item draft không xuất hiện.

Rollback Plan

Revert 2 commit (221ab4c4, d417b617). Không có migration, không có thay đổi runtime/API, không đụng lockfile hay workflow — nên rollback không ảnh hưởng production. examples/* nằm ngoài pnpm workspace nên không tác động build của repo.

Notes for Reviewers

Đã nghiệm thu thật (không phải suy luận)

CMS 1.0.0-rc.1 chạy thật trên DB riêng, seed collection posts với 2 item published + 1 draft, API key gắn role → policy read với rule {"status":{"_eq":"published"}}.

  • Cài ngoài workspace: dùng artifact pnpm pack (KHÔNG phải npm pack — chỉ pnpm rewrite workspace:*1.0.0-rc.1, khớp pnpm publish trong release.yml). Copy example ra thư mục tạm → npm install <2 tarball> → không còn workspace:*, không symlink vào monorepo.
  • Draft không lộ: grep toàn bộ output build → 0 kết quả; gọi thẳng id draft → 404.
  • Negative, không có 500 nào: draft by id 404 · ghi 403 · sai site 401 · không token 401.
  • Cache: publish → API thấy ngay, trang vẫn phục vụ bản cache, revalidate ở ~56s đúng với revalidate = 60.
  • types --check: schema khớp → exit 0; thêm field subtitle (drift có chủ đích) → exit 1; xoá → exit 0 trở lại.
  • Compatibility: createLumiClient/legacyRest/LumiError import từ @lumibase/sdk và từ lumibasecùng object (=== true), cùng trả về 2 bài published.

⚠️ Đây là local pack, CHƯA publish lên registry. Không tính là đã ship; registry install cần báo riêng sau khi publish.

🔴 Ba điểm reviewer cần quyết

  1. Bug CMS ngoài phạm vi PR (DoS). Request với X-Lumi-Site không tồn tại trả 401 đúng, nhưng lượt ghi audit log sau đó vi phạm FK lumibase_audit_log_site_id_lumibase_sites_id_fk (23503) và làm chết cả process. Trigger được từ xa bằng 1 header sai. Không thuộc grant A03 nên không sửa ở đây.

  2. Typegen không dùng được API key. GET /api/v1/typegen/schema nằm sau STUDIO_ACCESS_PATH_PREFIXES (apps/cms/src/middleware/studio-access.ts) vốn đòi user principal → API key bị 403 kể cả khi policy đã cấp schema:read. Đã ghi rõ trong README + tutorial EN/VI, nhưng cần xác nhận đây là chủ ý hay là gap.

  3. Sửa claim sai có sẵn trong tutorial: createClient(...).items(c).readMany(...) chưa từng tồn tại trong SDK. Hai locale hash khớp nhau vẫn cùng mô tả một API không có thật — đúng kịch bản mà CLAUDE.md cảnh báo.

Lệch nhỏ so với exact-file grant (cố ý, cần biết)

  • src/app/layout.tsx (mới) — grant không liệt kê, nhưng thiếu root layout thì example không build được độc lập (page.tsx doesn't have a root layout). Không thêm thì tiêu chí "chạy độc lập" không thể đạt.
  • lumibase.config.json thay vì lumibase.config.ts như grant ghi — CLI chỉ đọc .json (CONFIG_FILENAME trong packages/cli/src/config.ts), .ts sẽ bị bỏ qua.
  • src/lumibase-types.d.ts (mới) — output typegen, commit để types --check có cái mà so.
  • Waiver check-parity: allow code-fences (kèm lý do, ở cả hai locale): các code block giống hệt từng byte, chỉ khác comment cuối dòng đã dịch; check-parity strip comment cả dòng nhưng không strip comment cuối dòng. Đã diff kiểm chứng sau khi bỏ comment cuối dòng → không còn khác biệt.

Không đụng tới

Không sửa CMS/MCP/harness, SDK/CLI/scaffolder, root lockfile/manifests/workflows, hay docs getting-started. git status xác nhận pnpm-lock.yaml, package.json, turbo.json sạch.

Một cảnh báo còn lại

next@^15.5.19npm audit báo 2 lỗi (1 high), đều là postcss transitive, chỉ hết khi lên Next 16 (major, ngoài grant). Không tự nâng — để coordinator quyết.

Switch the Next.js blog example from the GraphQL plugin to the REST
surface of the `lumibase` package, installed as a real runtime
dependency instead of `workspace:*`, so the directory can be copied
outside the monorepo and installed from the registry.

- `createLumiClient(...).with(legacyRest())` replaces `.with(graphql())`;
  pages use `items('posts').list()` / `.detail(id)`.
- Content fields now read from `row.data.*` (REST returns `ItemRow`),
  and sorting uses the structural column name `-created_at`.
- 404 from `detail()` maps to `notFound()` via `LumiError.status`.
- Add the missing root layout — without it the example could not build
  standalone at all.
- Add `lumibase.config.json` plus `types` / `types:check` scripts and
  commit the generated `src/lumibase-types.d.ts`.
- Document the read-only credential, the server-only token rule and the
  `@lumibase/sdk` compatibility path in the README.
The tutorial presented raw `fetch` as Option A and the SDK as Option B,
and the SDK sample documented an API that does not exist
(`createClient(...).items(c).readMany(...)`).

- Step 6 is now the `lumibase` package: `createLumiClient` +
  `legacyRest()`, `items('posts').list()` / `.detail(id)`, with the
  `.data` nesting and `-created_at` sorting called out, and `LumiError`
  mapped to `notFound()`. Plain `fetch` moves to an appendix.
- New Step 7 covers `lumibase types` / `--check` in CI, and records that
  typegen needs a staff-user token: the endpoint sits behind the Studio
  access wall, so an API key gets 403 even with `schema:read`.
- Step 4 gains a least-privilege policy/role chain for the API key; the
  `status` field is removed from Step 3 because it is a built-in column.
- Compatibility contracts updated to the real SDK surface; both locales
  re-verified against source and re-stamped (verified_on 1.0.0-rc.1).

The code-fences parity waiver is scoped and explained in both files:
the blocks are byte-identical apart from translated trailing comments,
which check-parity does not strip.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sdk: use lumibase as the default client in standalone examples and Next.js tutorial

1 participant