├── .npmrc ├── .gitattributes ├── eslint.config.js ├── .editorconfig ├── .github ├── dependabot.yml ├── workflows │ └── ci.yml └── stale.yml ├── types ├── index.d.ts └── index.test-d.ts ├── index.js ├── LICENSE ├── package.json ├── README.md ├── .gitignore └── test └── routes.test.js /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-scripts=true 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically convert line endings 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = require('neostandard')({ 4 | ignores: require('neostandard').resolveIgnoresFromGitignore(), 5 | ts: true 6 | }) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | 12 | # [*.md] 13 | # trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: "npm" 10 | directory: "/" 11 | schedule: 12 | interval: "monthly" 13 | open-pull-requests-limit: 10 14 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { RouteOptions, FastifyPluginCallback } from 'fastify' 4 | 5 | declare module 'fastify' { 6 | interface FastifyInstance { 7 | routes: fastifyRoutes.FastifyRoutes; 8 | } 9 | } 10 | 11 | declare namespace fastifyRoutes { 12 | export type FastifyRoutes = Map 13 | 14 | export const fastifyRoutes: FastifyPluginCallback 15 | export { fastifyRoutes as default } 16 | } 17 | 18 | declare function fastifyRoutes (...params: Parameters): ReturnType 19 | export = fastifyRoutes 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | function fastifyRoutes (fastify, _options, next) { 6 | fastify.decorate('routes', new Map()) 7 | 8 | fastify.addHook('onRoute', (routeOptions) => { 9 | const { url } = routeOptions 10 | 11 | let routeListForUrl = fastify.routes.get(url) 12 | if (!routeListForUrl) { 13 | routeListForUrl = [] 14 | fastify.routes.set(url, routeListForUrl) 15 | } 16 | 17 | routeListForUrl.push(routeOptions) 18 | }) 19 | 20 | next() 21 | } 22 | 23 | module.exports = fp(fastifyRoutes, { 24 | fastify: '5.x', 25 | name: '@fastify/routes' 26 | }) 27 | module.exports.default = fastifyRoutes 28 | module.exports.fastifyRoutes = fastifyRoutes 29 | -------------------------------------------------------------------------------- /types/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import fastify, { 2 | FastifyInstance, 3 | FastifyPluginCallback, 4 | RouteOptions, 5 | } from 'fastify' 6 | import { expectAssignable, expectError, expectType } from 'tsd' 7 | 8 | import fastifyRoutes, { FastifyRoutes } from '..' 9 | 10 | const app: FastifyInstance = fastify() 11 | app.register(fastifyRoutes) 12 | 13 | expectType(fastifyRoutes) 14 | 15 | expectError( 16 | app.register(fastifyRoutes, { 17 | unknownOption: 'this should trigger a typescript error', 18 | }) 19 | ) 20 | 21 | // Plugin property available 22 | app.after(() => { 23 | expectType(app.routes) 24 | 25 | expectAssignable(app.routes.get('/rotue')) 26 | }) 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - next 8 | - 'v*' 9 | paths-ignore: 10 | - 'docs/**' 11 | - '*.md' 12 | pull_request: 13 | paths-ignore: 14 | - 'docs/**' 15 | - '*.md' 16 | 17 | # This allows a subsequently queued workflow run to interrupt previous runs 18 | concurrency: 19 | group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" 20 | cancel-in-progress: true 21 | 22 | permissions: 23 | contents: read 24 | 25 | jobs: 26 | test: 27 | permissions: 28 | contents: write 29 | pull-requests: write 30 | uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5 31 | with: 32 | license-check: true 33 | lint: true 34 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 15 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - "discussion" 8 | - "feature request" 9 | - "bug" 10 | - "help wanted" 11 | - "plugin suggestion" 12 | - "good first issue" 13 | # Label to use when marking an issue as stale 14 | staleLabel: stale 15 | # Comment to post when marking an issue as stale. Set to `false` to disable 16 | markComment: > 17 | This issue has been automatically marked as stale because it has not had 18 | recent activity. It will be closed if no further activity occurs. Thank you 19 | for your contributions. 20 | # Comment to post when closing a stale issue. Set to `false` to disable 21 | closeComment: false 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Cemre Mengu 4 | Copyright (c) 2018-present The Fastify team 5 | 6 | The Fastify team members are listed at https://github.com/fastify/fastify#team. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fastify/routes", 3 | "version": "6.0.2", 4 | "description": "A plugin for Fastify that provides a map of routes", 5 | "main": "index.js", 6 | "type": "commonjs", 7 | "types": "types/index.d.ts", 8 | "scripts": { 9 | "lint": "eslint", 10 | "lint:fix": "eslint --fix", 11 | "test": "npm run test:unit && npm run test:typescript", 12 | "test:unit": "c8 --100 node --test", 13 | "test:typescript": "tsd" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+ssh://git@github.com/fastify/fastify-routes.git" 18 | }, 19 | "keywords": [ 20 | "fastify" 21 | ], 22 | "author": "Cemre Mengu ", 23 | "contributors": [ 24 | { 25 | "name": "Matteo Collina", 26 | "email": "hello@matteocollina.com" 27 | }, 28 | { 29 | "name": "James Sumners", 30 | "url": "https://james.sumners.info" 31 | }, 32 | { 33 | "name": "Aras Abbasi", 34 | "email": "aras.abbasi@gmail.com" 35 | }, 36 | { 37 | "name": "Frazer Smith", 38 | "email": "frazer.dev@icloud.com", 39 | "url": "https://github.com/fdawgs" 40 | } 41 | ], 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/fastify/fastify-routes/issues" 45 | }, 46 | "homepage": "https://github.com/fastify/fastify-routes#readme", 47 | "funding": [ 48 | { 49 | "type": "github", 50 | "url": "https://github.com/sponsors/fastify" 51 | }, 52 | { 53 | "type": "opencollective", 54 | "url": "https://opencollective.com/fastify" 55 | } 56 | ], 57 | "devDependencies": { 58 | "@types/node": "^24.0.8", 59 | "c8": "^10.1.2", 60 | "eslint": "^9.17.0", 61 | "fastify": "^5.0.0", 62 | "neostandard": "^0.12.0", 63 | "tsd": "^0.33.0" 64 | }, 65 | "dependencies": { 66 | "fastify-plugin": "^5.0.0" 67 | }, 68 | "publishConfig": { 69 | "access": "public" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @fastify/routes 2 | 3 | [![CI](https://github.com/fastify/fastify-routes/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-routes/actions/workflows/ci.yml) 4 | [![NPM version](https://img.shields.io/npm/v/@fastify/routes.svg?style=flat)](https://www.npmjs.com/package/@fastify/routes) 5 | [![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard) 6 | 7 | This plugin decorates a Fastify instance with `routes`, which is a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) of registered routes. Note that you have to await the registration of this plugin before registering any routes so that @fastify/routes can collect them. 8 | 9 | ## Data Structure 10 | 11 | The `fastify.routes` Map has a key for each path any route has been registered, which points to an array of routes registered on that path. There can be more than one route for a given path if there are multiple routes added with different methods or different constraints. 12 | 13 | ```js 14 | { 15 | '/hello': [ 16 | { 17 | method: 'GET', 18 | url: '/hello', 19 | schema: { ... }, 20 | handler: Function, 21 | prefix: String, 22 | logLevel: String, 23 | bodyLimit: Number, 24 | constraints: undefined, 25 | }, 26 | { 27 | method: 'POST', 28 | url: '/hello', 29 | schema: { ... }, 30 | handler: Function, 31 | prefix: String, 32 | logLevel: String, 33 | bodyLimit: Number, 34 | constraints: { ... }, 35 | } 36 | ] 37 | } 38 | ``` 39 | 40 | ## Example 41 | 42 | ```js 43 | const fastify = require("fastify")(); 44 | 45 | (async () => { 46 | await fastify.register(require("@fastify/routes")); 47 | fastify.get("/hello", {}, (request, reply) => { 48 | reply.send({ hello: "world" }); 49 | }); 50 | 51 | fastify.listen({ port: 3000 }, (err, address) => { 52 | if (err) { 53 | console.error(err); 54 | return; 55 | } 56 | console.log(fastify.routes); 57 | /* will output a Map with entries: 58 | { 59 | '/hello': [ 60 | { 61 | method: 'GET', 62 | url: '/hello', 63 | schema: Object, 64 | handler: , 65 | prefix: , 66 | logLevel: , 67 | bodyLimit: 68 | } 69 | ] 70 | } 71 | */ 72 | }); 73 | })(); 74 | ``` 75 | 76 | ## License 77 | 78 | MIT License 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # Vim swap files 133 | *.swp 134 | 135 | # macOS files 136 | .DS_Store 137 | 138 | # Clinic 139 | .clinic 140 | 141 | # lock files 142 | bun.lockb 143 | package-lock.json 144 | pnpm-lock.yaml 145 | yarn.lock 146 | 147 | # editor files 148 | .vscode 149 | .idea 150 | 151 | #tap files 152 | .tap/ 153 | -------------------------------------------------------------------------------- /test/routes.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('node:test') 4 | const Fastify = require('fastify') 5 | const plugin = require('..') 6 | 7 | function handler (_, reply) { 8 | reply.send({ hello: 'world' }) 9 | } 10 | 11 | const schema = { 12 | response: { 13 | 200: { 14 | type: 'object', 15 | properties: { 16 | hello: { type: 'string' } 17 | } 18 | } 19 | } 20 | } 21 | 22 | const routeA = function (fastify, _opts, next) { 23 | const options = { 24 | schema, 25 | bodyLimit: 1000, 26 | logLevel: 'warn' 27 | } 28 | 29 | fastify.get('/hello/:world', options, handler) 30 | 31 | next() 32 | } 33 | 34 | const routeB = function (fastify, _opts, next) { 35 | fastify.post( 36 | '/hello/:world', 37 | { 38 | bodyLimit: 2000, 39 | logLevel: 'info' 40 | }, 41 | handler 42 | ) 43 | 44 | next() 45 | } 46 | 47 | const routeC = { 48 | method: ['GET', 'HEAD'], 49 | path: '/foo', 50 | handler (_req, res) { 51 | res.send({ success: true }) 52 | } 53 | } 54 | 55 | const constrainedRoute = { 56 | method: ['GET'], 57 | path: '/foo', 58 | constraints: { host: 'fastify.dev' }, 59 | handler (_req, res) { 60 | res.send({ success: true }) 61 | } 62 | } 63 | 64 | test('should correctly map routes', async (t) => { 65 | const fastify = Fastify({ exposeHeadRoutes: false }) 66 | 67 | await fastify.register(plugin) 68 | 69 | fastify.register(routeA, { prefix: '/v1' }) 70 | fastify.register(routeB, { prefix: '/v1' }) 71 | fastify.route(routeC) 72 | fastify.route(constrainedRoute) 73 | 74 | await fastify.ready() 75 | 76 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[0].method, 'GET') 77 | t.assert.strictEqual( 78 | fastify.routes.get('/v1/hello/:world')[0].url, 79 | '/v1/hello/:world' 80 | ) 81 | t.assert.strictEqual( 82 | fastify.routes.get('/v1/hello/:world')[0].logLevel, 83 | 'warn' 84 | ) 85 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[0].prefix, '/v1') 86 | t.assert.strictEqual( 87 | fastify.routes.get('/v1/hello/:world')[0].bodyLimit, 88 | 1000 89 | ) 90 | t.assert.strictEqual( 91 | fastify.routes.get('/v1/hello/:world')[0].handler, 92 | handler 93 | ) 94 | t.assert.deepStrictEqual( 95 | fastify.routes.get('/v1/hello/:world')[0].schema, 96 | schema 97 | ) 98 | 99 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[1].method, 'POST') 100 | t.assert.strictEqual( 101 | fastify.routes.get('/v1/hello/:world')[1].url, 102 | '/v1/hello/:world' 103 | ) 104 | t.assert.strictEqual( 105 | fastify.routes.get('/v1/hello/:world')[1].logLevel, 106 | 'info' 107 | ) 108 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[1].prefix, '/v1') 109 | t.assert.strictEqual( 110 | fastify.routes.get('/v1/hello/:world')[1].bodyLimit, 111 | 2000 112 | ) 113 | t.assert.strictEqual( 114 | fastify.routes.get('/v1/hello/:world')[1].handler, 115 | handler 116 | ) 117 | 118 | t.assert.deepStrictEqual(fastify.routes.get('/foo')[0].method, [ 119 | 'GET', 120 | 'HEAD' 121 | ]) 122 | t.assert.strictEqual(fastify.routes.get('/foo')[0].constraints, undefined) 123 | t.assert.deepStrictEqual(fastify.routes.get('/foo')[1].constraints, { 124 | host: 'fastify.dev' 125 | }) 126 | }) 127 | 128 | test('should allow other later onRoute handlers to change route options', async (t) => { 129 | const fastify = Fastify({ exposeHeadRoutes: false }) 130 | 131 | await fastify.register(plugin) 132 | fastify.addHook('onRoute', (options) => { 133 | options.constraints = { host: 'some-automatic-constraint.com' } 134 | }) 135 | fastify.register(routeA) 136 | 137 | await fastify.ready() 138 | 139 | t.assert.strictEqual( 140 | fastify.routes.get('/hello/:world')[0].url, 141 | '/hello/:world' 142 | ) 143 | t.assert.deepStrictEqual(fastify.routes.get('/hello/:world')[0].constraints, { 144 | host: 'some-automatic-constraint.com' 145 | }) 146 | }) 147 | 148 | test('should correctly map routes with automatic HEAD routes', async (t) => { 149 | const fastify = Fastify() 150 | 151 | await fastify.register(plugin) 152 | 153 | fastify.register(routeA, { prefix: '/v1' }) 154 | fastify.register(routeB, { prefix: '/v1' }) 155 | fastify.route(routeC) 156 | fastify.route(constrainedRoute) 157 | 158 | await fastify.ready() 159 | 160 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[0].method, 'GET') 161 | t.assert.strictEqual( 162 | fastify.routes.get('/v1/hello/:world')[0].url, 163 | '/v1/hello/:world' 164 | ) 165 | t.assert.strictEqual( 166 | fastify.routes.get('/v1/hello/:world')[0].logLevel, 167 | 'warn' 168 | ) 169 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[0].prefix, '/v1') 170 | t.assert.strictEqual( 171 | fastify.routes.get('/v1/hello/:world')[0].bodyLimit, 172 | 1000 173 | ) 174 | t.assert.strictEqual( 175 | fastify.routes.get('/v1/hello/:world')[0].handler, 176 | handler 177 | ) 178 | t.assert.deepStrictEqual( 179 | fastify.routes.get('/v1/hello/:world')[0].schema, 180 | schema 181 | ) 182 | 183 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[1].method, 'HEAD') 184 | t.assert.strictEqual( 185 | fastify.routes.get('/v1/hello/:world')[1].url, 186 | '/v1/hello/:world' 187 | ) 188 | t.assert.strictEqual( 189 | fastify.routes.get('/v1/hello/:world')[1].logLevel, 190 | 'warn' 191 | ) 192 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[1].prefix, '/v1') 193 | t.assert.strictEqual( 194 | fastify.routes.get('/v1/hello/:world')[1].bodyLimit, 195 | 1000 196 | ) 197 | t.assert.strictEqual( 198 | fastify.routes.get('/v1/hello/:world')[1].handler, 199 | handler 200 | ) 201 | t.assert.deepStrictEqual( 202 | fastify.routes.get('/v1/hello/:world')[1].schema, 203 | schema 204 | ) 205 | 206 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[2].method, 'POST') 207 | t.assert.strictEqual( 208 | fastify.routes.get('/v1/hello/:world')[2].url, 209 | '/v1/hello/:world' 210 | ) 211 | t.assert.strictEqual( 212 | fastify.routes.get('/v1/hello/:world')[2].logLevel, 213 | 'info' 214 | ) 215 | t.assert.strictEqual(fastify.routes.get('/v1/hello/:world')[2].prefix, '/v1') 216 | t.assert.strictEqual( 217 | fastify.routes.get('/v1/hello/:world')[2].bodyLimit, 218 | 2000 219 | ) 220 | t.assert.strictEqual( 221 | fastify.routes.get('/v1/hello/:world')[2].handler, 222 | handler 223 | ) 224 | 225 | t.assert.deepStrictEqual(fastify.routes.get('/foo')[0].method, [ 226 | 'GET', 227 | 'HEAD' 228 | ]) 229 | t.assert.strictEqual(fastify.routes.get('/foo')[0].constraints, undefined) 230 | t.assert.deepStrictEqual(fastify.routes.get('/foo')[1].constraints, { 231 | host: 'fastify.dev' 232 | }) 233 | }) 234 | --------------------------------------------------------------------------------