LogoRobo.js

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/health
  • src/api/auth/login.ts/api/auth/login
  • src/api/users/[id].ts/api/users/:id
  • src/api/files/[...path].ts/api/files/*
health.ts/api/health
login.ts/api/auth/login
index.ts/api/users
[id].ts/api/users/:id
[...path].ts/api/files/*

Route prefix

Routes are prefixed with /api by default. Change it:

config/plugins/robojs/server.mjs
export default {
	prefix: '/v1' // Routes at /v1/health, /v1/users, etc.
}
config/plugins/robojs/server.mjs
export default {
	prefix: '/v1' // Routes at /v1/health, /v1/users, etc.
}

Or disable entirely:

config/plugins/robojs/server.mjs
export default {
	prefix: false // Routes at /health, /users, etc.
}
config/plugins/robojs/server.mjs
export default {
	prefix: false // Routes at /health, /users, etc.
}

Dynamic segments

  • [param] — Matches a single segment. Accessible via request.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.
src/api/users/[id].ts
import type { RoboRequest } from '@robojs/server'

export function GET(request: RoboRequest) {
	const { id } = request.params
	return { userId: id }
}
src/api/users/[id].js

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.

src/api/users.ts
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 }
}
src/api/users.js

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:

  • OPTIONS requests auto-respond with a 204 and Allow header listing available methods
  • HEAD requests automatically use the GET handler if no HEAD export exists
  • Unsupported methods return 405 Method Not Allowed with an Allow header

Default export (legacy)

You can also export a default function that handles all methods:

src/api/legacy.ts
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' }
}
src/api/legacy.js

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:

src/api/mixed.ts
export function GET() {
	return { data: 'from GET export' }
}

// Handles POST, PUT, DELETE, etc.
export default (request) => {
	return { data: 'from default export', method: request.method }
}
src/api/mixed.js
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 Response or RoboResponse → 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)
src/api/protected.ts
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' }
}
src/api/protected.js
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:

src/api/search.ts
import type { RoboRequest } from '@robojs/server'

export function GET(request: RoboRequest) {
	const { page, limit } = request.query
	return { page, limit }
}
src/api/search.js

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.

Next steps

On this page