Skip to content

fix(api): menu REST API with camelCase responses#1052

Open
Rimander wants to merge 4 commits into
emdash-cms:mainfrom
Rimander:fix/menus-api-response-shape-and-delete-route
Open

fix(api): menu REST API with camelCase responses#1052
Rimander wants to merge 4 commits into
emdash-cms:mainfrom
Rimander:fix/menus-api-response-shape-and-delete-route

Conversation

@Rimander
Copy link
Copy Markdown
Contributor

@Rimander Rimander commented May 15, 2026

What does this PR do?

Addresses the menu API problems reported in #932.

What detected while tracing the flow:

  • POST /menus/:name/items with custom_url (snake_case) returned 201 with custom_url: null. The Zod schema only knows the documented camelCase keys (customUrl, sortOrder, …), and Zod's default .object() silently strips unknown keys.
  • DELETE /menus/:name/items/:id returned the SSR 404 HTML page because menus were the only resource using a query-string id (/items?id=); every other resource (content, taxonomies, redirects, sections, widget-areas) uses path-style [id].ts.
  • Menu responses were the only ones returning snake_case (custom_url, menu_id, …) — every other resource (content, media, taxonomies, comments, redirects) goes through a *Repository.rowToX() mapper that produces camelCase. Menus had no repository at all; all SQL lived in handlers/menus.ts.

What changed:

  • New MenuRepository next to the other repositories. All SQL and snake_case ↔ camelCase mapping lives there. handlers/menus.ts is now a thin orchestrator.
  • Menu and menu-item API responses are now camelCase (createdAt, menuId, parentId, sortOrder, customUrl, referenceCollection, …).
  • createMenuItemBody / updateMenuItemBody are .strict() — unknown keys (custom_url, url, sort_order) now fail with 400 VALIDATION_ERROR instead of being silently dropped. type is validated against the canonical enum (custom | page | post | taxonomy | collection).
  • Added PUT and DELETE /_emdash/api/menus/:name/items/:id (path-style).
  • OpenAPI document and admin UI types/components updated to match. Database schema is not touched.

Heads-up for theme authors: if your theme renders a custom menu block on the frontend by reading the REST API directly (rather than going through getMenu() from the runtime), the response keys have changed from snake_case to camelCase. getMenu() already returned camelCase — themes using it are unaffected.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.7

Screenshots / test output

Test Files  208 passed (208)
     Tests  3291 passed (3291)

Repository test: tests/integration/database/menu-repository.test.ts (12 cases — row mapping, item counts, translation cloning, atomic setItems, scoped delete/reorder, listTranslations).

Handler/schema/route test: tests/integration/api/menus-handlers.test.ts (9 cases — .strict() rejects snake_case, enum rejects type: "link", customUrl persists and is returned in camelCase, path-style route module exists with PUT/DELETE).

Rimander added 2 commits May 15, 2026 15:05
…schema validation, and repository-pattern implementation.
…a transaction-scoped existence check and throwing MenuGoneError
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 15, 2026

🦋 Changeset detected

Latest commit: 11cfdc7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
emdash Minor
@emdash-cms/admin Minor
@emdash-cms/cloudflare Minor
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

Scope check

This PR changes 2,345 lines across 14 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 15, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1052

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1052

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1052

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1052

emdash

npm i https://pkg.pr.new/emdash@1052

create-emdash

npm i https://pkg.pr.new/create-emdash@1052

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1052

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1052

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1052

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1052

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1052

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1052

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1052

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1052

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1052

commit: 11cfdc7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant