SpaceNode
SpaceNode uses a Trie (radix tree) based router. Routes are defined in module.js and automatically prefixed with the module's prefix.
// module.js
routes: [
['GET', '/users/:id', 'getUser'],
['GET', '/users/:id/posts/:pid', 'getPost'],
]
// controller
export function getUser({ params, send }) {
console.log(params.id) // "123"
}
export function getPost({ params, send }) {
console.log(params.id) // "123"
console.log(params.pid) // "456"
}
// GET /products?q=laptop&minPrice=500
export function list({ query, send }) {
console.log(query.q) // "laptop"
console.log(query.minPrice) // "500"
}
routes: [
['GET', '/*', 'catchAll'],
]
export function catchAll({ params, send }) {
console.log(params['*']) // "everything/after/prefix"
}
All standard methods are supported: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD.
If a route path exists but the method doesn't match, SpaceNode returns 405 Method Not Allowed with an Allow header listing valid methods.
SpaceNode handles HEAD requests automatically — you don't need to define separate HEAD routes. When a HEAD request is received, SpaceNode routes it to the matching GET handler, runs the full pipeline (pipes + handler), but suppresses the response body. Status code and headers are preserved.
// Define only GET — HEAD is handled automatically:
routes: [
['GET', '/users', 'list'],
]
// HEAD /users → runs list handler, returns headers + status, no body
// GET /users → runs list handler, returns headers + status + body
The trie router matches in priority order:
/users/search wins over /users/:id/users/:id/users/*The trie router finds routes in O(path_length) time, not O(n_routes). This means routing performance doesn't degrade as you add more routes — unlike Express's linear regex matching.