Routing
File-based routing, dynamic parameters, and HTTP method handling.
Routes are defined by the file structure inside /src/api. Each file becomes an endpoint, and the file path determines the URL.
File structure
Paths map directly from files to URLs:
src/api/health.ts→/api/healthsrc/api/auth/login.ts→/api/auth/loginsrc/api/users/[id].ts→/api/users/:idsrc/api/files/[...path].ts→/api/files/*
Route prefix
Routes are prefixed with /api by default. Change it:
export default {
prefix: '/v1' // Routes at /v1/health, /v1/users, etc.
}export default {
prefix: '/v1' // Routes at /v1/health, /v1/users, etc.
}Or disable entirely:
export default {
prefix: false // Routes at /health, /users, etc.
}export default {
prefix: false // Routes at /health, /users, etc.
}Dynamic segments
[param]— Matches a single segment. Accessible viarequest.params.param.[...slug]— Catch-all. Matches one or more segments.[[...slug]]— Optional catch-all. Matches zero or more segments.index.ts— Index route. Maps to the directory path itself.
import type { RoboRequest } from '@robojs/server'
export function GET(request: RoboRequest) {
const { id } = request.params
return { userId: id }
}
export function GET(request) {
const { id } = request.params
return { userId: id }
}Named HTTP method exports
Export functions named after HTTP methods. This is the recommended approach.
import type { RoboRequest } from '@robojs/server'
export function GET() {
return [{ id: '1', name: 'Alice' }]
}
export async function POST(request: RoboRequest) {
const body = await request.json()
return { id: '2', ...body }
}
export function DELETE(request: RoboRequest) {
return { deleted: request.params.id }
}
export function GET() {
return [{ id: '1', name: 'Alice' }]
}
export async function POST(request) {
const body = await request.json()
return { id: '2', ...body }
}
export function DELETE(request) {
return { deleted: request.params.id }
}Supported methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD.
Automatic behaviors:
OPTIONSrequests auto-respond with a 204 andAllowheader listing available methodsHEADrequests automatically use theGEThandler if noHEADexport exists- Unsupported methods return
405 Method Not Allowedwith anAllowheader
Default export (legacy)
You can also export a default function that handles all methods:
import type { RoboRequest, RoboReply } from '@robojs/server'
export default (request: RoboRequest, reply: RoboReply) => {
if (request.method !== 'GET') {
reply.code(405).send('Method not allowed')
return
}
return { data: 'hello' }
}
export default (request, reply) => {
if (request.method !== 'GET') {
reply.code(405).send('Method not allowed')
return
}
return { data: 'hello' }
}Mixed usage
Combine both — named exports take priority, default is the fallback:
export function GET() {
return { data: 'from GET export' }
}
// Handles POST, PUT, DELETE, etc.
export default (request) => {
return { data: 'from default export', method: request.method }
}export function GET() {
return { data: 'from GET export' }
}
// Handles POST, PUT, DELETE, etc.
export default (request) => {
return { data: 'from default export', method: request.method }
}Return values
- Return an object → JSON response with 200
- Return a string → text response with 200
- Return a
ResponseorRoboResponse→ sent as-is - Throw an
Error→ 500 JSON response with error message - Throw a
RoboResponse→ sent as the response (useful for custom error status codes)
import { RoboResponse } from '@robojs/server'
import type { RoboRequest } from '@robojs/server'
export function GET(request: RoboRequest) {
if (!request.query.key) {
throw RoboResponse.json({ error: 'API key required' }, { status: 401 })
}
return { data: 'authenticated' }
}import { RoboResponse } from '@robojs/server'
export function GET(request) {
if (!request.query.key) {
throw RoboResponse.json({ error: 'API key required' }, { status: 401 })
}
return { data: 'authenticated' }
}Query parameters
Access via request.query:
import type { RoboRequest } from '@robojs/server'
export function GET(request: RoboRequest) {
const { page, limit } = request.query
return { page, limit }
}
export function GET(request) {
const { page, limit } = request.query
return { page, limit }
}request.query returns only the first value per key. For multi-value parameters, use new URL(request.url).searchParams directly.
