Open DemianD opened 2 years ago
cc @fastify/typescript
Had the same issue, but when providing an additional onRequest
handler instead of security
.
Using the alternative syntax using route()
for route declaration instead of get() / post() / whateverVerb()
seems to make the issue disappear.
@sinclairzx81 could you take a look?
Hi @mcollina ! Just had a quick look at the original issue code, this seems to be working ok...
General Recommendation:
typescript@4.9.5
)compilerOptions: { strict: true }
is configured in tsconfig.json
Happy to look a bit deeper if there's a way to repro the inference issue, but this appears to be working alright :)
Hope this helps! S
Thanks!
Hi @mcollina ! Just had a quick look at the original issue code, this seems to be working ok...
General Recommendation:
- Ensure latest version of Fastify (noting this issue dates back to Sep last year so there may have been some type updates since)
- Ensure latest version of the TS compiler (currently
typescript@4.9.5
)- Ensure
compilerOptions: { strict: true }
is configured intsconfig.json
Happy to look a bit deeper if there's a way to repro the inference issue, but this appears to be working alright :)
Hope this helps! S
Well, seems like my first assumption was not correct, my issue popped up in all (fastify.post
| fastify.get
) && fastify.route
scenarii after all.
The issue I had, which I do not think is related to OP's issue, was that the function which I wanted to override onRequest
with had explicitly defined parameters as generic FastifyRequest
/ FastifyReply
, instead of my fastify's instance super-seeded generics (FastifyRequest<..., TypeBoxTypeProvider>
/ FastifyReply<..., TypeBoxTypeProvider>
)
...by enforcing these basic types, functionality breaks and my request components all resolves as unknown
, which is kinda understandable.
Thank you guys @mcollina @sinclairzx81 for your time :)
I ran into the same issues with multiple items in the security array making body unknown
however it's using FastifyPluginAsyncTypebox
could that be a another source for this issue. I have tested with the latest version of everything but still get the same issues. I created a small repo where the issue is reproduced.
https://github.com/andreaspalsson/typebox-schema-security
Also ran it through GitHub Actions to make sure it wasn't something local and the types fails there as well
https://github.com/andreaspalsson/typebox-schema-security/actions/runs/4232737696/jobs/7352833222
Versions:
"@fastify/swagger": "^8.3.1",
"@fastify/type-provider-typebox": "^2.4.0",
"@sinclair/typebox": "^0.25.23",
"fastify": "^4.13.0",
"typescript": "^4.9.5"
@andreaspalsson Hey, thanks for the repro! Had another look at this and can reproduce the issue on the TSP. The issue seems to be related to the definitions inside @fastify/swagger
. See TypeScript link below and uncomment the following line to reproduce.
// import '@fastify/swagger' // uncomment to introduce error
The swagger package performs a declaration merge on the FastifySchema
to include a set of additional properties specific to swagger. One of which being the security
property. This is defined as follows (code can be found here)
interface FastifySchema {
hide?: boolean;
deprecated?: boolean;
tags?: string[];
description?: string;
summary?: string;
consumes?: string[];
produces?: string[];
externalDocs?: OpenAPIV2.ExternalDocumentationObject | OpenAPIV3.ExternalDocumentationObject;
security?: Array<{ [securityLabel: string]: string[] }>; // <-- issue here !
/**
* OpenAPI operation unique identifier
*/
operationId?: string;
}
TypeScript can have some difficulties dealing with arbitrary length array / property key definitions when mapping types, but its curious why it's causing issues here as this property is not mapped. My best guess is due to the declaration merge, this is causing the security
property (and type) to be propagated to the route (via req.routeSchema.security
) and as the property type is non-mappable, it's causing TS to inference to break down (even though there's no mapping on it, which is strange)
In situations like this though, it's common to turn to readonly
and as const
assertions, this to provide TS enough assurances as to the shape of the security
array.
The following updates the security
property to be readonly
. This will need to be updated in the @fastify/swagger
declarations here).
// ------------------------------------------------------------------------------
// Fix: @fastify/swagger
//
// Here we mandate that the security array is readonly, and the caller must
// pass with a 'as const' assertion. This enables TS to propagate the type as
// fixed readonly data structure. Similar techniques can be seen in libraries
// like json-schema-to-ts which mandate 'as const' to infer object property
// keys encoded in JSON Schema.
// ------------------------------------------------------------------------------
declare module "fastify" {
// added: readonly is required for propagation of dictionary types.
export type SecurityArray = readonly {
[securityLabel: string]: readonly string[]
}[]
interface FastifySchema {
hide?: boolean;
deprecated?: boolean;
tags?: string[];
description?: string;
summary?: string;
consumes?: string[];
produces?: string[];
externalDocs?: OpenAPIV2.ExternalDocumentationObject | OpenAPIV3.ExternalDocumentationObject;
security?: SecurityArray; // Fix
/**
* OpenAPI operation unique identifier
*/
operationId?: string;
}
}
Here's an example of things working in the TSP with the as const
applied to the security array per route.
server.post(
"/double-security",
{
schema: {
body: Type.Object({
foo: Type.Number(),
bar: Type.String(),
}),
security: [{ apiKey: ['foo'] }, { authHeader: ['bar'] }] as const // mandate const readonly assertion
},
},
(request, reply) => {
const { foo, bar } = request.body; // ok
return { foo, bar };
}
);
};
While in TS 4.0 the as const
assertion is required, In TS 5.0, it should be possible to automatically apply the as const
with the upcoming TS 5.0 const generics language feature. Setting up readonly now should get things up for a later TS 5.0 enhancement once it lands.
Hope this helps S
CC @mcollina @Amphaal
@andreaspalsson @Amphaal Oh, one other thing. In the interim, you should be able to get around the inference issues with an as any
assertion on the security array (which isn't ideal, but works)
security: [{ apiKey: [] }, { authHeader: [] }] as any // replace with "as const" on fix
Cheers
@sinclairzx81 Thanks for a great explanation of the problem and a workaround. We only have a few endpoints that have multiple security items so this interim solution will work fine for us. Thanks for the quick help.
Is this a problem i @fastify/swagger
then? Could we fix it there?
@mcollina Yeah, it might be worth opening an issue on @fastify/swagger
and linking back to this one. The suggested fix would require an update in the swagger declarations (so would need a review from the Fastify TS team), also there's actually a few ways to approach a solution, so would be good to discuss those within the context of the swagger project.
Should we move this issue?
Prerequisites
Fastify version
4.5.3
Plugin version
2.3.0
Node.js version
18.7.0
Operating system
macOS
Operating system version (i.e. 20.04, 11.3, 10)
11.5.2
Description
We have a problem that our types are not automatically inferred when we add multiple items to the
security
property of the JSON schema that you can define on the route.You can see this in the example below:
Steps to Reproduce
Expected Behavior
We'd like that our types are inferred when adding multiple items to the security array.