Admin CRUD Interface
Website Rebuild with Express
Chapter 10 · Admin CRUD Interface
No free admin, no scaffold generator — the same most-minimal starting point Astro already reached. Everything here is hand-built, including the one place this course's own recursive SQL knowledge pays off a second time.
The Parent Picker
The same searchable flat list every sibling course reached for — every other page's own full_path, filterable client-side, excluding the page being edited from its own candidate-parent list.
Reparenting: Finding Every Descendant in One Query
updateDescendantPaths(), Rails' update_descendant_paths!, Astro's own version). Here, Chapter 6's own recursive CTE finds every affected descendant, at any depth, in a single query — ORDER BY depth ASC means the results already arrive parent-before-child, so a single linear for loop, not a recursive function, is enough to process them in the correct dependency order.
full_path depends on its own parent's already-computed new path, so writing them still takes one UPDATE per row, run in order. This is a real, partial improvement, not a magic bullet: the recursive CTE removed the N+1 read pattern, not the N-write pattern, which is fundamentally unavoidable here regardless of ORM, framework, or query strategy.
Delete Refusal — the Same Ceremony as Laravel and Astro
Chapter 2 verified ON DELETE RESTRICT as the cleanest, most unambiguous database-level constraint in the series. That means the same try/catch ceremony Laravel and Astro both needed — not Rails' own smoother, non-exception path, which was ActiveRecord's own specific API design, not a universal trait.
Six Admin Interfaces, Compared One Final Time
| Django | Laravel | Rails | Astro | Express | |
|---|---|---|---|---|---|
| Free admin / scaffold? | Live admin | None | Scaffold generator | Neither | Neither |
| Descendant discovery | Implicit save() cascade | Level-by-level recursion | Level-by-level recursion | Level-by-level recursion | One recursive CTE, all levels at once |
| Delete-with-children handling | Caught ProtectedError | Caught QueryException | Returns false, no exception | Caught raw exception | Caught raw exception |
Hands-On Exercises
Build the searchable flat parent picker for the admin interface, excluding the page itself from its own candidate-parent list.
📄 View solutionBuild movePage() using the recursive CTE for descendant discovery, and confirm a real multi-level reparent operation correctly cascades full_path updates to every descendant in the right order.
📄 View solutionAttempt to delete a page with children via the DELETE endpoint, confirm the raw database constraint throws, and confirm the try/catch turns it into a real 409 response with a readable message.
📄 View solutionChapter 10 Quick Reference
- No free admin, no scaffold generator — the same most-minimal starting point as Astro
- Recursive CTE descendant discovery — one query finds every affected row, at any depth, in dependency order
- The write step stays sequential — a real, honestly-scoped limitation, not solved by the same query
- Same delete-refusal ceremony as Laravel and Astro — a real
try/catchneeded, unlike Rails' own smoother path - Next chapter: Deployment