Repository: fastify/fastify
File: lib/route.js (JavaScript)

Code before:
    return findRoute(options) !== null
  }

  function findRoute (options) {
    return router.find(
      options.method,
      options.url || '',
      options.constraints
    )
  }

  function validateRouteParams (options) {
    const { method, url, constraints } = options
    const route = findRoute({ method, url: url || '', constraints })
    if (route === null) {
      return false
    }

    const schemaParams = new Set(Object.keys(route.store.schema.params))
    const params = new Set(Object.keys(route.params))
    if (schemaParams.size !== params.size) {
      return false
    }

    return [...schemaParams].every(param => params.has(param))
  }

  /**
   * Route management
   * @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
   */
  function route ({ options, isFastify }) {
    // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
    const opts = { ...options }

    const { exposeHeadRoute } = opts
    const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
    const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes

    const isGetRoute = opts.method === 'GET' ||
      (Array.isArray(opts.method) && opts.method.includes('GET'))
    const isHeadRoute = opts.method === 'HEAD' ||
      (Array.isArray(opts.method) && opts.method.includes('HEAD'))

    // we need to clone a set of initial options for HEAD route
    const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null

    throwIfAlreadyStarted('Cannot add route!')

    const path = opts.url || opts.path || ''


Review comment (suggestion):
Overall, I'm +1 on exposing `findRoute` but not in sync with having this validation within core. The scope for the feature seems really specific, that's why the suggestion of having it within a community plugin.

Code after:
    return findRoute(options) !== null
  }

  function findRoute (options) {
    return router.find(
      options.method,
      options.url || '',
      options.constraints
    )
  }

  /**
   * Route management
   * @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
   */
  function route ({ options, isFastify }) {
    // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
    const opts = { ...options }

    const { exposeHeadRoute } = opts
    const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
    const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes

    const isGetRoute = opts.method === 'GET' ||
      (Array.isArray(opts.method) && opts.method.includes('GET'))
    const isHeadRoute = opts.method === 'HEAD' ||
      (Array.isArray(opts.method) && opts.method.includes('HEAD'))

    // we need to clone a set of initial options for HEAD route
    const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null

    throwIfAlreadyStarted('Cannot add route!')

    const path = opts.url || opts.path || ''

    if (Array.isArray(opts.method)) {
      // eslint-disable-next-line no-var
      for (var i = 0; i < opts.method.length; ++i) {
        opts.method[i] = normalizeAndValidateMethod(opts.method[i])
        validateSchemaBodyOption(opts.method[i], path, opts.schema)
      }
    } else {
      opts.method = normalizeAndValidateMethod(opts.method)
      validateSchemaBodyOption(opts.method, path, opts.schema)
    }

    if (!opts.handler) {
      throw new FST_ERR_ROUTE_MISSING_HANDLER(opts.method, path)
    }

    if (opts.errorHandler !== undefined && typeof opts.errorHandler !== 'function') {