Skip to content

Commit

Permalink
feat: route parameters (#134)
Browse files Browse the repository at this point in the history
* route prefixing feature

* route prefix tests

* adding test for parameterized route with no root action

* adding cjs test for route parameters

* adding route parameter documentation

* moving routeParamPattern out of options scope
  • Loading branch information
polymathca authored Apr 6, 2021
1 parent 56dd9e5 commit ac6e74f
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 1 deletion.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,31 @@ Autoload can be customised using the following options:
})
```
- `routeParams` (optional) - Folders prefixed with `_` will be turned into route parameters.
```js
/*
├── routes
│ └── users
│ ├── _id
│ │ └── actions.js
│ └── index.js
└── app.js
*/

fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
routeParams: true // routes/users/_id/actions.js will be loaded with prefix /users/:id
})

// curl http://localhost:3000/users/index
// { userIndex: [ { id: 7, username: 'example' } ] }

// curl http://localhost:3000/users/7/details
// { user: { id: 7, username: 'example' } }

```
## Plugin Configuration
Each plugin can be individually configured using the following module properties:
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const isJestEnviroment = process.env.JEST_WORKER_ID !== undefined
const typescriptSupport = isTsNode || isJestEnviroment

const moduleSupport = semver.satisfies(process.version, '>= 14 || >= 12.17.0 < 13.0.0')
const routeParamPattern = /\/_/i

const defaults = {
scriptPattern: /((^.?|\.[^d]|[^.]d|[^.][^d])\.ts|\.js|\.cjs|\.mjs)$/i,
Expand All @@ -33,14 +34,15 @@ const fastifyAutoload = async function autoload (fastify, options) {
return loadPlugin(file, type, prefix, opts)
.then((plugin) => {
if (plugin) {
// create route parameters from prefixed folders
if (options.routeParams) plugin.options.prefix = plugin.options.prefix ? plugin.options.prefix.replace(routeParamPattern, '/:') : plugin.options.prefix
pluginsMeta[plugin.name] = plugin
}
})
.catch((err) => {
throw enrichError(err)
})
}))

await Promise.all(hookArray.map((h) => {
if (hooksMeta[h.file]) return null // hook plugin already loaded, skip this instance
return loadHook(h, opts)
Expand Down
41 changes: 41 additions & 0 deletions test/commonjs/route-parameters-basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'

const t = require('tap')
const Fastify = require('fastify')

t.plan(10)

const app = Fastify()

app.register(require('./route-parameters/basic'))

app.ready(function (err) {
t.error(err)

app.inject({
url: '/users'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { users: [{ id: 7, username: 'example' }] })
})

app.inject({
url: '/users/7'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { user: { id: 7, username: 'example' } })
})

app.inject({
url: '/users/_id'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { user: { id: '_id', username: 'example' } })
})
})
40 changes: 40 additions & 0 deletions test/commonjs/route-parameters-disabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict'

const t = require('tap')
const Fastify = require('fastify')

t.plan(9)

const app = Fastify()

app.register(require('./route-parameters/disabled'))

app.ready(function (err) {
t.error(err)

app.inject({
url: '/users'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { users: [{ id: 7, username: 'example' }] })
})

app.inject({
url: '/users/7'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 404)
})

app.inject({
url: '/users/_id'
}, function (err, res) {
t.error(err)

t.equal(res.statusCode, 200)
t.deepEqual(JSON.parse(res.payload), { user: { id: 'null', username: 'example' } })
})
})
13 changes: 13 additions & 0 deletions test/commonjs/route-parameters/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

const path = require('path')
const autoLoad = require('../../../')

module.exports = function (fastify, opts, next) {
fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
routeParams: true
})

next()
}
13 changes: 13 additions & 0 deletions test/commonjs/route-parameters/disabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

const path = require('path')
const autoLoad = require('../../../')

module.exports = function (fastify, opts, next) {
fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
routeParams: false
})

next()
}
5 changes: 5 additions & 0 deletions test/commonjs/route-parameters/routes/users/_id/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = async function (app, opts, next) {
app.get('/', async function (req, reply) {
reply.status(200).send({ user: { id: req.params.id || 'null', username: 'example' } }) // explicit null reply to preserve response body
})
}
5 changes: 5 additions & 0 deletions test/commonjs/route-parameters/routes/users/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = async function (app, opts, next) {
app.get('/', async function (req, reply) {
reply.status(200).send({ users: [{ id: 7, username: 'example' }] })
})
}
81 changes: 81 additions & 0 deletions test/module/route-parameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import t from 'tap'
import fastify from 'fastify'
import routeParametersBasic from './route-parameters/basic.mjs'
import routeParametersDisabled from './route-parameters/disabled.mjs'

const { test } = t

test('routeParams: Default behaviour', async () => {
const app = fastify()
let res

app.register(routeParametersBasic)

await app.ready()

res = await app.inject('/')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/' })

res = await app.inject('/pages')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages' })

res = await app.inject('/pages/archived')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/archived' })

res = await app.inject('/pages/test_id')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/:id/', id: 'test_id' })

res = await app.inject('/pages/test_id/edit')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/:id/edit', id: 'test_id' })

res = await app.inject('/users/test_id/details')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/users/:id/details', id: 'test_id' })
})

test('routeParams: off', async () => {
const app = fastify()
let res

app.register(routeParametersDisabled)

await app.ready()

res = await app.inject('/')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/' })

res = await app.inject('/pages')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages' })

res = await app.inject('/pages/archived')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/archived' })

res = await app.inject('/pages/test_id')
t.equal(res.statusCode, 404)

res = await app.inject('/pages/test_id/edit')
t.equal(res.statusCode, 404)

res = await app.inject('/pages/_id')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/:id/' })

res = await app.inject('/pages/_id/edit')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/pages/:id/edit' })

res = await app.inject('/users/test_id/details')
t.equal(res.statusCode, 404)

res = await app.inject('/users/_id/details')
t.equal(res.statusCode, 200)
t.deepEqual(res.json(), { route: '/users/:id/details' })
})
15 changes: 15 additions & 0 deletions test/module/route-parameters/basic.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import path from 'path'
import { fileURLToPath } from 'url'

import autoload from '../../../index.js'

const { dirname } = path
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

export default async function (fastify, opts) {
fastify.register(autoload, {
dir: path.join(__dirname, 'routes'),
routeParams: true
})
}
15 changes: 15 additions & 0 deletions test/module/route-parameters/disabled.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import path from 'path'
import { fileURLToPath } from 'url'

import autoload from '../../../index.js'

const { dirname } = path
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

export default async function (fastify, opts) {
fastify.register(autoload, {
dir: path.join(__dirname, 'routes'),
routeParams: false
})
}
5 changes: 5 additions & 0 deletions test/module/route-parameters/routes/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default async function (app, opts) {
app.get('/', async function (req, reply) {
return { route: '/' }
})
}
9 changes: 9 additions & 0 deletions test/module/route-parameters/routes/pages/_id/actions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default async function (app, opts) {
app.get('/', async function (req, reply) {
return { route: '/pages/:id/', id: req.params.id }
})

app.get('/edit', async function (req, reply) {
return { route: '/pages/:id/edit', id: req.params.id }
})
}
9 changes: 9 additions & 0 deletions test/module/route-parameters/routes/pages/routes.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default async function (app, opts) {
app.get('/', async function (req, reply) {
return { route: '/pages' }
})

app.get('/archived', async function (req, reply) {
return { route: '/pages/archived', id: req.params.id } // should be null
})
}
5 changes: 5 additions & 0 deletions test/module/route-parameters/routes/users/_id/actions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default async function (app, opts) {
app.get('/details', async function (req, reply) {
return { route: '/users/:id/details', id: req.params.id }
})
}

0 comments on commit ac6e74f

Please sign in to comment.