File-System Routing
One of bxn’s core features is file-system based routing — your directory structure automatically becomes your API structure. No configuration required.
Basic Routing
Section titled “Basic Routing”Routes are automatically mapped based on your file structure in the src/routes directory:
src/routes/├── get.ts → GET /├── authors/│ ├── get.ts → GET /authors│ ├── post.ts → POST /authors│ └── $authorId/│ ├── get.ts → GET /authors/:authorId│ └── delete.ts → DELETE /authors/:authorId└── posts/ ├── get.ts → GET /posts └── $postId/ └── get.ts → GET /posts/:postIdConvention
Section titled “Convention”Directories = Path Segments
Section titled “Directories = Path Segments”Each directory in your routes folder becomes a path segment in your API:
routes/users/→/usersroutes/api/v1/→/api/v1
Filenames = HTTP Methods
Section titled “Filenames = HTTP Methods”The filename determines which HTTP method the route handles:
get.ts→GETrequestpost.ts→POSTrequestput.ts→PUTrequestdelete.ts→DELETErequestpatch.ts→PATCHrequest
All standard HTTP methods are supported.
Dynamic Parameters
Section titled “Dynamic Parameters”Use the $ prefix to create dynamic route parameters:
$authorId/→:authorIdparameter$postId/→:postIdparameter$slug/→:slugparameter
These parameters are accessible in your request handlers via req.params.
Examples
Section titled “Examples”Simple Endpoint
Section titled “Simple Endpoint”import { route, json } from '@buildxn/http';
export default route().handle(() => { return json({ status: 'ok' });});Maps to: GET /health
CRUD Operations
Section titled “CRUD Operations”import { route, json } from '@buildxn/http';
export default route().handle(() => { return json({ users: db.users.getAll() });});import { route, created } from '@buildxn/http';import { Type } from '@sinclair/typebox';
export default route() .body( Type.Object({ name: Type.String(), email: Type.String(), }), ) .handle((req) => { const user = db.users.create(req.body); return created(user, `/users/${user.id}`); });Maps to:
GET /usersPOST /users
Dynamic Routes
Section titled “Dynamic Routes”// src/routes/users/$userId/get.tsimport { route, json, notFound } from '@buildxn/http';import { Type } from '@sinclair/typebox';
export default route() .params(Type.Object({ userId: Type.String() })) .handle((req) => { const user = db.users.get(req.params.userId);
if (!user) { return notFound({ error: 'User not found' }); }
return json(user); });Maps to: GET /users/:userId
Nested Dynamic Routes
Section titled “Nested Dynamic Routes”// src/routes/users/$userId/posts/$postId/get.tsimport { route, json, notFound } from '@buildxn/http';import { Type } from '@sinclair/typebox';
export default route() .params( Type.Object({ userId: Type.String(), postId: Type.String(), }), ) .handle((req) => { const { userId, postId } = req.params; const post = db.posts.getByUserAndId(userId, postId);
if (!post) { return notFound({ error: 'Post not found' }); }
return json(post); });Maps to: GET /users/:userId/posts/:postId
Benefits
Section titled “Benefits”- Intuitive: Your file structure mirrors your API structure
- Discoverable: Easy to navigate and understand the codebase
- Type-Safe: TypeScript knows the exact parameter names from the file structure
- No Config: No routing configuration files needed
- Scalable: Organize routes naturally as your API grows