Routing: Astro's Own Catch-All, Now Database-Backed
Website Rebuild with Astro
Chapter 3 · Routing: Astro's Own Catch-All, Now Database-Backed
astro1's own Chapter 3 built [...path].astro against Content Collections, and Chapter 10 showed getStaticPaths() disappearing entirely in server mode. This chapter is where both findings actually pay off together — the catch-all route, hooked to a real database query, with no pre-declared path list anywhere.
The Catch-All Route, Database-Backed
No getStaticPaths() anywhere in this file — Chapter 1's own server-mode configuration means every possible path value resolves fresh, per request, straight from the pages table.
astro1's own catch-all stayed backed by Content Collections, its final shape looked structurally different from every sibling rebuild's own routing. Here, with a real database query behind it, Astro's routing finally looks the same shape as all four completed siblings: one catch-all route, one lookup by path, resolved per request. The differences that remain are in the ORM and syntax, not in the underlying architecture — a genuine convergence, not a coincidence.
A Real Gotcha: Your Own Catch-All Shadows the Conventional 404 Page
src/pages/404.astro file is served automatically for any unmatched route. But [...path].astro matches every possible URL, including genuinely nonexistent ones — so it always wins the routing match first, and 404.astro is never reached through normal routing at all. A missing page simply falls through this file's own if (!page) branch instead — that has to be handled explicitly, not left to Astro's own file-based convention.
Handling Not-Found Correctly
Astro.rewrite('/404') renders the real content of src/pages/404.astro for this request, while Astro.response.status = 404 ensures the response actually carries a genuine 404 status code — both are needed together; rewrite alone would render the right content with a misleading 200 status.
Routing, Now Genuinely Converged
| Next.js | Django | Laravel | Rails | Astro | |
|---|---|---|---|---|---|
| Mechanism | [...path] folder | <path:full_path> | {path?} + regex | *path glob | [...path].astro |
| Resolved by | A database lookup | A database lookup | A database lookup | A database lookup | A database lookup |
Hands-On Exercises
Build [...path].astro with a real Drizzle query resolving Astro.params.path against the pages table, and confirm a real stored page renders correctly at its own full path.
📄 View solutionVisit a genuinely nonexistent path with no explicit not-found handling in place yet, and observe exactly what happens — confirming src/pages/404.astro is never actually reached.
📄 View solutionAdd the explicit Astro.response.status = 404 plus Astro.rewrite('/404') handling, and confirm the response now carries both a real 404 status code and the correct 404 page content together.
📄 View solutionChapter 3 Quick Reference
[...path].astro— nogetStaticPaths()needed; resolves fresh, per request- Genuine convergence — Astro's routing finally matches the same shape as all four sibling rebuilds
- The 404 gotcha — a global catch-all shadows
src/pages/404.astro; it's never reached via normal routing Astro.response.status = 404+Astro.rewrite('/404')— the correct combination for a real 404, both status and content- Next chapter: Views & Rendering Trusted Content