Skip to content

File-based Routing

Reference: Deno File-based Routing Tutorial

File-based routing is Deserve's core concept, where the file system structure becomes the API structure automatically, following the same pattern as Next.js but for Deno APIs.

How It Works

Deserve scans the routes directory and creates endpoints from the file structure, and every supported extension (.ts, .js, .tsx, .jsx, .mjs, .cjs) works the same way:

routes/
├── index.ts               → GET /
├── about.mjs              → GET /about
├── users.js               → GET /users
├── users/[id].ts          → GET /users/:id
└── users/[id]/
    └── posts/
        └── [postId].jsx   → GET /users/:id/posts/:postId

Core Rules

1. File Names Become Routes

  • index.ts, index.js, index.mjs/ (root)
  • about.ts, about.js, about.mjs/about
  • users.ts, users.js, users.cjs/users

All supported extensions (.ts, .js, .tsx, .jsx, .mjs, .cjs) work identically. A filename can only have one dot separating the name from the extension, so about.ts loads but about.config.ts does not.

2. Folders Create Nested Routes

  • users/[id].ts/users/:id
  • users/[id]/posts.ts/users/:id/posts

3. Dynamic Parameters Use [param] Syntax

  • [id].ts:id parameter
  • [userId].ts:userId parameter
  • [postId].ts:postId parameter

Dynamic segments are matched by Route Patterns and read with ctx.get.param() from Request Handling.

4. HTTP Methods Are Exported Functions

typescript
import type { 
Context
} from '@neabyte/deserve'
// Each export maps to its method export function
GET
(
ctx
:
Context
): Response {
return
ctx
.
send
.
json
({
users
: []
}) } export async function
POST
(
ctx
:
Context
):
Promise
<Response> {
// Read parsed request body const
data
= await
ctx
.
get
.
body
()
return
ctx
.
send
.
json
({
message
: 'User created',
data
}) } // PUT, PATCH, DELETE follow the same shape // export function [method](ctx: Context): Response { ... }

A route file must export at least one HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). A file whose name never forms a loadable pattern, such as one starting with _, is skipped during the scan and emits a route:ignored event. A file with a loadable name but no exported method is a mistake worth catching early, so the scan throws Deno.errors.InvalidData at startup, and an export that is not a function throws TypeError.

5. Case-Sensitive URLs

URLs are case-sensitive following HTTP standards:

  • /Users/John/users/john
  • /API/v1/api/v1

6. Valid Filename Characters

The last segment of a route path (the filename without extension) can contain:

  • a-z, A-Z, 0-9 - Alphanumeric characters
  • _ - Underscore (do not prefix a segment with it, see below)
  • - - Dash
  • ~ - Tilde
  • + - Plus sign
  • [ ] - Brackets for dynamic parameters

Skipped segments: Folders or file names that start with _ or @ are not registered as routes (for example _layout.ts, @middleware.ts, folder _components/). Useful for support files that are not endpoints.

Edited route files reload on the fly without a restart, covered in Hot Reload.

Released under the MIT License.