Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save JonFranchi/910252d44fe8cc9689443e97c855581f to your computer and use it in GitHub Desktop.
Save JonFranchi/910252d44fe8cc9689443e97c855581f to your computer and use it in GitHub Desktop.
Koa Dynamic Routing Options
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
console.log('🚀 Koa Dynamic Routing Demo\n');
// =============================================================================
// SOLUTION 1: Static Route (your starting point)
// =============================================================================
router.get('/api/v1/thing/stuff', (ctx) => {
ctx.body = {
solution: 1,
type: 'static',
message: 'Static route: /api/v1/thing/stuff',
matched: 'thing/stuff'
};
});
// =============================================================================
// SOLUTION 2: Basic Dynamic Parameters (thing and stuff are dynamic)
// =============================================================================
router.get('/api/v1/:category/:item', (ctx) => {
const { category, item } = ctx.params;
ctx.body = {
solution: 2,
type: 'basic-dynamic',
message: `Dynamic route with 2 parameters`,
params: { category, item },
path: `${category}/${item}`
};
});
// =============================================================================
// SOLUTION 3: Custom Middleware for Unlimited Segments
// =============================================================================
const multiSegmentMatcher = (ctx, next) => {
const path = ctx.path;
const apiV2Match = path.match(/^\/api\/v2\/(.+)$/);
if (apiV2Match) {
const segments = apiV2Match[1].split('/').filter(s => s.length > 0);
ctx.multiSegment = {
solution: 3,
type: 'unlimited-segments',
message: 'Custom middleware handling unlimited segments',
segments,
segmentCount: segments.length,
interpretation: {
category: segments[0] || null,
item: segments[1] || null,
additional: segments.slice(2)
}
};
}
return next();
};
router.get(/^\/api\/v2\/(.+)$/, multiSegmentMatcher, (ctx) => {
ctx.body = ctx.multiSegment || { error: 'No match found' };
});
// =============================================================================
// SOLUTION 4: Pure Regex Route Matching
// =============================================================================
router.get(/^\/api\/v3\/([^\/]+)\/([^\/]+)(?:\/(.*))?$/, (ctx) => {
const category = ctx.params[0];
const item = ctx.params[1];
const additional = ctx.params[2];
ctx.body = {
solution: 4,
type: 'regex-matching',
message: 'Pure regex route with optional additional segments',
params: { category, item },
additional: additional ? additional.split('/').filter(s => s.length > 0) : [],
fullPath: `${category}/${item}${additional ? '/' + additional : ''}`
};
});
// =============================================================================
// SOLUTION 5: Layered Approach (multiple routes, most specific first)
// =============================================================================
router.get('/api/v4/:category/:item/:extra1/:extra2', (ctx) => {
const { category, item, extra1, extra2 } = ctx.params;
ctx.body = {
solution: 5,
type: 'layered-4-segments',
message: 'Matched 4-segment pattern',
segments: [category, item, extra1, extra2]
};
});
router.get('/api/v4/:category/:item/:extra1', (ctx) => {
const { category, item, extra1 } = ctx.params;
ctx.body = {
solution: 5,
type: 'layered-3-segments',
message: 'Matched 3-segment pattern',
segments: [category, item, extra1]
};
});
router.get('/api/v4/:category/:item', (ctx) => {
const { category, item } = ctx.params;
ctx.body = {
solution: 5,
type: 'layered-2-segments',
message: 'Matched 2-segment pattern (fallback)',
segments: [category, item]
};
});
// =============================================================================
// SOLUTION 6: Simple Catch-All Test
// =============================================================================
router.get('/api/v5/:param1/(.*)', (ctx) => {
const param1 = ctx.params.param1;
const param2 = ctx.params[0]; // The catch-all captures everything after param1
console.log(ctx.params);
ctx.body = {
solution: 6,
type: 'simple-catch-all',
message: 'Simple catch-all with standard Koa routing',
param1,
param2,
params: ctx.params
};
});
// =============================================================================
// Demo Routes & Documentation
// =============================================================================
router.get('/', (ctx) => {
ctx.body = {
title: 'Koa Dynamic Routing Demo - Multiple Solutions',
solutions: [
{
id: 1,
name: 'Static Route',
example: '/api/v1/thing/stuff',
description: 'Your original fixed route'
},
{
id: 2,
name: 'Basic Dynamic',
example: '/api/v1/books/fiction',
description: 'Making thing/stuff dynamic'
},
{
id: 3,
name: 'Unlimited Segments (Middleware)',
example: '/api/v2/books/fiction/mystery/agatha-christie',
description: 'Custom middleware for any number of segments'
},
{
id: 4,
name: 'Regex Matching',
example: '/api/v3/books/fiction/mystery/author',
description: 'Pure regex with optional additional segments'
},
{
id: 5,
name: 'Layered Routes',
examples: [
'/api/v4/books/fiction',
'/api/v4/books/fiction/mystery',
'/api/v4/books/fiction/mystery/author'
],
description: 'Multiple routes, most specific matches first'
}
]
};
});
app.use(router.routes());
app.use(router.allowedMethods());
const PORT = 3000;
app.listen(PORT, () => {
console.log(`✅ Server running on http://localhost:${PORT}`);
console.log('\n📋 Test these URLs:');
console.log('• http://localhost:3000/ (documentation)');
console.log('• http://localhost:3000/api/v1/thing/stuff (static original)');
console.log('• http://localhost:3000/api/v1/books/fiction (basic dynamic)');
console.log('• http://localhost:3000/api/v2/books/fiction/mystery/author (unlimited)');
console.log('• http://localhost:3000/api/v3/books/fiction/mystery/author (regex)');
console.log('• http://localhost:3000/api/v4/books/fiction (layered 2-seg)');
console.log('• http://localhost:3000/api/v4/books/fiction/mystery (layered 3-seg)');
console.log('• http://localhost:3000/api/v4/books/fiction/mystery/author (layered 4-seg)');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment