Repository: fastify/fastify (JavaScript)

Issue:
Strange behavior with route reusage
<!--
Before you submit an issue we recommend you visit [Fastify Help](https://github.com/fastify/help) and ask any questions you have or mention any problems you've had getting started with Fastify.

**Please read this entire template before posting any issue. If you ignore these instructions
and post an issue here that does not follow the instructions, your issue might be closed,
locked, and assigned the `missing discussion` label.** 
-->

## 🐛 Bug Report

By reusing the same route inside more than one plugins, I've noticed that the second time the route is registered, its URL is somehow prefixed by the previous plugin. I'm not sure if this behavior is intentional or a bug.

## To Reproduce

```js
const fastify = require('fastify')({ logger: true })

const route = {
  method: 'GET',
  url: '/',
  async handler(request, reply) {
    return { hello: 'world' }
  },
}

async function v1Plugin(fastify) {
  fastify.route(route)
}

async function v2Plugin(fastify) {
  fastify.route(route)
}

fastify.register(v1Plugin, { prefix: '/api/v1' })
fastify.register(v2Plugin, { prefix: '/api/v2' })

const start = async () => {
  try {
    await fastify.listen(3000)
    console.log(fastify.printRoutes())
    fastify.log.info(`server listening on ${fastify.server.address().port}`)
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}

start()
```

## Expected behavior

I was expecting this output:
```
└── /
    └── api/v
        ├── 1 (GET)
        │   └── / (GET)
        └── 2 (GET)
            └── / (GET)
```

I got this instead:
```
└── /
    └── api/v
        ├── 1 (GET)
        │   └── / (GET)
        └── 2/api/v1/ (GET)
```

And I can "fix" that by cloning the `route` object and redefining the `url` property:
```js
fastify.route({ ...route, url: '/' })
```

## Your Environment

- *node version*: 12
- *fastify version*: ^3.4.1
- *os*: Linux



Discussion:

Patch:
diff --git a/lib/route.js b/lib/route.js
index 40779b903ca..03746bb9ae1 100644
--- a/lib/route.js
+++ b/lib/route.js
@@ -116,7 +116,10 @@ function buildRouting (options) {
   }
 
   // Route management
-  function route (opts) {
+  function route (options) {
+    // Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
+    const opts = { ...options }
+
     throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')
 
     if (Array.isArray(opts.method)) {


Test patch:
diff --git a/test/route.test.js b/test/route.test.js
index befddc5d81c..9625dd28707 100644
--- a/test/route.test.js
+++ b/test/route.test.js
@@ -190,6 +190,33 @@ test('invalid schema - route', t => {
   })
 })
 
+test('same route definition object on multiple prefixes', async t => {
+  t.plan(2)
+
+  const routeObject = {
+    handler: () => {},
+    method: 'GET',
+    url: '/simple'
+  }
+
+  const fastify = Fastify()
+
+  fastify.register(async function (f) {
+    f.addHook('onRoute', (routeOptions) => {
+      t.is(routeOptions.url, '/v1/simple')
+    })
+    f.route(routeObject)
+  }, { prefix: '/v1' })
+  fastify.register(async function (f) {
+    f.addHook('onRoute', (routeOptions) => {
+      t.is(routeOptions.url, '/v2/simple')
+    })
+    f.route(routeObject)
+  }, { prefix: '/v2' })
+
+  await fastify.ready()
+})
+
 test('path can be specified in place of uri', t => {
   t.plan(3)
   const fastify = Fastify()