Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
3 changes: 3 additions & 0 deletions docs/Guides/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ This table of contents is in alphabetical order.
written with a fluent API and used in Fastify.
+ [Getting Started](./Getting-Started.md): Introduction tutorial for Fastify.
This is where beginners should start.
+ [Migration Guide (declaration merging to registration-scoped typing)](./Migration-Guide-Declaration-Merging.md):
Details how to migrate TypeScript decorator usage to the Fastify 6
registration-scoped inference model for both applications and plugins.
+ [Migration Guide (v4)](./Migration-Guide-V4.md): Details how to migrate to
Fastify v4 from earlier versions.
+ [Migration Guide (v3)](./Migration-Guide-V3.md): Details how to migrate to
Expand Down
185 changes: 185 additions & 0 deletions docs/Guides/Migration-Guide-Declaration-Merging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<h1 align="center">Fastify</h1>

# Migration Guide: from declaration merging to registration-scoped typing

This guide explains how to migrate TypeScript code from global/ambient
`declare module 'fastify'` assumptions to Fastify's registration-scoped typing
model for decorators.

It is written for:

- **application maintainers** using Fastify in production services;
- **plugin maintainers** publishing Fastify plugins.

> This registration-scoped decorator typing approach is part of the Fastify 6
> TypeScript model.

## What changed

In the declaration-merging model, decorators often appeared globally in types,
even outside the scope where they were registered.

In the registration-scoped model:

- `decorate()`, `decorateRequest()`, and `decorateReply()` widen the current
instance type;
- `register()` carries plugin effects through typed registration flow;
- decorator visibility follows encapsulation and registration boundaries.

Runtime behavior is unchanged. The change is in type transport and type
visibility.

---

## Migration checklist (quick)

1. Track where decorators are actually registered.
2. Keep and use the registered instance value in typed code.
3. Stop relying on type-only side-effect imports to expose decorators globally.
4. Refactor tests to use local typed helpers instead of global ambient test
augmentations.
5. Validate with full lint/build/typecheck/tests and explicit success output.

---

## Application migration

## 1) Keep the registered instance in scope

### Before

```ts
const app = fastify()
app.register(fastifyStatic, { root: '/public' })

app.get('/', (request, reply) => {
reply.sendFile('index.html')
})
```

### After

```ts
const app = fastify().register(fastifyStatic, { root: '/public' })

app.get('/', (request, reply) => {
reply.sendFile('index.html')
})
```

The runtime object is the same, but the typed registered value carries decorator
availability.

## 2) Make registration order explicit in typed code

If route/hook definitions are attached before type-visible registration,
TypeScript should reject decorator usage. Move route definitions into the proper
scope or use the typed registered value.

## 3) Respect encapsulation boundaries

If a decorator is registered in a child plugin, do not assume it exists on
parent/sibling branches.

## 4) Avoid type-only plugin imports for decorator visibility

Do not use `import '@fastify/some-plugin'` as a typing shortcut for unrelated
instances. Register plugins on the instance path where decorators are used.

## 5) Handle dynamic decorator names explicitly

For namespaced/renamed decorators, inference may not be fully automatic.
Introduce local helper types or explicit aliases where names are configured at
runtime.

---

## Plugin migration

## 1) Type plugin effects explicitly

Plugin types should encode what is added to:

- instance decorators,
- request decorators,
- reply decorators.

Avoid relying only on ambient merging for public typing.

## 2) Keep runtime logic stable, migrate type transport

Most plugin ports are primarily typing refactors. Preserve runtime behavior,
move typing to registration-scoped contracts.

## 3) Make default paths fully inferred

For plugins with configurable decorator names, keep default names strongly
typed; provide helper types for advanced renamed paths.

## 4) Derive override signatures from base contracts

When subclassing strategy-like abstractions, derive parameter types from the
base signature, e.g. `Parameters<Base['method']>[0]`, to avoid variance and
augmentation mismatches.

## 5) Refactor tests away from ambient assumptions

If tests assumed globally available decorators, migrate to local test helpers
that narrow request/reply types at usage points.

This is usually the longest part of migration work.

---

## Test migration patterns that work

## Pattern A: local request/reply adapters in tests

Use helper functions in test utilities that narrow to the expected decorated
shape where needed, rather than globally augmenting all test types.

## Pattern B: runtime guard + typed return helper

In tests where setup is indirect, use small runtime checks (`if (!('flash' in
reply)) throw`) and return a typed helper object. This keeps type assumptions
local and explicit.

## Pattern C: avoid final-state crutches

Temporary migration aids can unblock early work, but final validation should not
require:

- test-only ambient compat bridges;
- `skipLibCheck` as a migration bypass.

---

## Common failures and fixes

| Error | Cause | Fix |
|---|---|---|
| `Property 'x' does not exist on request/reply` | Decorator used outside registered type scope | Use typed registered instance or move code into correct encapsulation scope |
| Override method is not assignable | Override signature does not match base contract | Derive signature from base method parameters |
| Works at runtime, fails in TS | Registration order not reflected in typing flow | Reorder definitions or retain typed registration value |
| Tests only pass with compat `.d.ts` | Hidden global assumptions in tests | Replace with local typed test helpers |

---

## Validation gate

A migration is considered complete when all are true:

- registration-scoped typing is used for decorator visibility;
- docs are updated for typing behavior changes;
- no global compat test augmentation is required;
- no `skipLibCheck` migration bypass is required;
- lint/build/typecheck/tests pass with explicit success indicators.

---

## Related docs

- [TypeScript reference](../Reference/TypeScript.md)
- [Plugins guide](./Plugins-Guide.md)
- [Write Plugin](./Write-Plugin.md)
- [Encapsulation reference](../Reference/Encapsulation.md)
10 changes: 6 additions & 4 deletions docs/Reference/Hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ fastify.addHook('preHandler', async (request, reply) => {
If you want to respond with a stream, you should avoid using an `async` function
for the hook. If you must use an `async` function, your code will need to follow
the pattern in
[test/hooks-async.js](https://github.com/fastify/fastify/blob/94ea67ef2d8dce8a955d510cd9081aabd036fa85/test/hooks-async.js#L269-L275).
[test/hooks-async.test.js](https://github.com/fastify/fastify/blob/main/test/hooks-async.test.js).

```js
fastify.addHook('onRequest', (request, reply, done) => {
Expand Down Expand Up @@ -866,9 +866,11 @@ would be a dangerous and destructive operation. So be careful and
make sure your property is entirely new, also using this approach
only for very specific and small cases like this example.

Regarding TypeScript in this example, you'd need to update the
`FastifyRequest` core interface to include your new property typing
(for more about it, see [TypeScript](./TypeScript.md) page), like:
Regarding TypeScript in this example, in Fastify 6 you should prefer a
registration-scoped/plugin-based typing approach. For small local cases, a
module augmentation can still be used to type your custom property
(for more about migration options, see [TypeScript](./TypeScript.md) and the
[declaration-merging migration guide](../Guides/Migration-Guide-Declaration-Merging.md)):

```ts
interface AuthenticatedUser { /* ... */ }
Expand Down
6 changes: 3 additions & 3 deletions docs/Reference/Logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ app.addHook('preHandler', function (req, reply, done) {
> ℹ️ Note:
> Ensure serializers never throw errors, as this can cause the Node
> process to exit. See the
> [Pino documentation](https://getpino.io/#/docs/api?id=opt-serializers) for more
> information.
> [Pino documentation](https://github.com/pinojs/pino/blob/main/docs/api.md#serializers-object)
> for more information.

*Any logger other than Pino will ignore this option.*

Expand Down Expand Up @@ -265,4 +265,4 @@ const fastify = Fastify({
})
```

See https://getpino.io/#/docs/redaction for more details.
See https://github.com/pinojs/pino/blob/main/docs/redaction.md for more details.
8 changes: 4 additions & 4 deletions docs/Reference/Server.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ method, otherwise attempting to set it will throw an exception.

Defines the maximum number of requests a socket can handle before closing keep
alive connection. See [`server.maxRequestsPerSocket`
property](https://nodejs.org/dist/latest/docs/api/http.html#servermaxrequestspersocket)
property](https://nodejs.org/api/http.html#servermaxrequestspersocket)
to understand the effect of this option. This option only applies when HTTP/1.1
is in use. Also, when `serverFactory` option is specified, this option is
ignored.
Expand All @@ -213,7 +213,7 @@ ignored.

Defines the maximum number of milliseconds for receiving the entire request from
the client. See [`server.requestTimeout`
property](https://nodejs.org/dist/latest/docs/api/http.html#servertimeout)
property](https://nodejs.org/api/http.html#serverrequesttimeout)
to understand the effect of this option.

When `serverFactory` option is specified, this option is ignored.
Expand Down Expand Up @@ -335,7 +335,7 @@ This property is used to configure the internal logger instance.
The possible values this property may have are:

+ Default: `false`. The logger is disabled. All logging methods will point to a
null logger [abstract-logging](https://www.npmjs.com/package/abstract-logging) instance.
null logger [abstract-logging](https://github.com/jsumners/abstract-logging) instance.

+ `object`: a standard Pino [options
object](https://github.com/pinojs/pino/blob/c77d8ec5ce/docs/API.md#constructor).
Expand Down Expand Up @@ -1243,7 +1243,7 @@ addresses](https://nodejs.org/api/net.html#serverlistenport-host-backlog-callbac

Be careful when deciding to listen on all interfaces; it comes with inherent
[security
risks](https://web.archive.org/web/20170711105010/https://snyk.io/blog/mongodb-hack-and-secure-defaults/).
risks](https://snyk.io/blog/mongodb-hack-and-secure-defaults/).

The default is to listen on `port: 0` (which picks the first available open
port) and `host: 'localhost'`:
Expand Down
Loading
Loading