-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[kbn/server-route-repository] Add support for defining response validations via Zod #205486
base: main
Are you sure you want to change the base?
Conversation
…seValidation` zod schema to generate OAS docs.
Some comments before I jump into the code: From the example, I don’t understand how this inference connects with the response validation? Do we try to infer 4xx/5xx in the client? If so, I’m not sure if that is a good idea since the Core HTTP client will throw for those codes so we will lose anything that we infer anyway. |
} & Omit<VersionedRouteResponseValidation[number], 'body'>; | ||
} | ||
|
||
export type TRouteResponse = TRouteResponseStatusCodes & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what this type achieves, can you explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a result of incrementally trying to get the union of all configured response body's. Without this TypeScript would short circuit to only the top response type, forcing the handler to always return the top response type.
Now that I go through it again, I've simplified the type into:
export type TRouteResponse = {
[statusCode: number]: {
body: z.ZodSchema<ServerRouteHandlerReturnType>;
} & Omit<VersionedRouteResponseValidation[number], 'body'>;
} & Omit<VersionedRouteResponseValidation, number>;
export type ExtractResponseStatusBodyTypes<T extends TRouteResponse> = z.infer<
T[Extract<keyof T, number>]['body']
>;
}); | ||
} | ||
|
||
return { success: false as const, error: 'Example error' }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there is no code changes done in register routes, this will result in a HTTP 200 OK, so I don't think the 400 response schema will be used, what we should do here is return a 400 using the response factory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes using 400 here was incorrect. It was more like different shapes of 2xx responses. I've updated the test.
assertType<{ | ||
success: false; | ||
error: string; | ||
}>(res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This likely passes because indeed the type can be inferred to this but I don't believe the response validation for the 400 is running and since we're not actually returning a 400, this ends up in then
while a real 400 should end up in a rejection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it shouldn't be 400. I've updated the test to use 2xx as currently error responses aren't unwrapped as with .ok
response.
Done. I too was feeling it needs better naming.
Using 400 in the example was misleading. It was more like being able to define multiple success response types e.g. for 200, 201 and other 2xx. I've updated the example.
That's right. Trying to type the errors requires to change types in the core and is likely to invalidate types of many consumer plugins. For now, there's no typing for error responses and it is assumed the handlers will throw any 4xx or 5xx (e.g.
No, the typings currently do not cater for error responses or thrown payload. Doing se needs some work and the types in |
@awahab07 Can you just confirm for me, what happens if a handler does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we first try to get a better understanding of the impact of zod on TS responsiveness? And ideally we can make sure that the Zod types are only checked against the return type of the handler, and not stored in the inferred type in the Repository. type, as this will cause TypeScript to re-parse the zod schemas every time there is a change in any file that uses the Repository type
@miltonhultgren If explicit error type is asserted for IKibanaResponse: return kibanaResponseFactory.notFound({
body: { message: 'Not found' },
}) as IKibanaResponse<{ message: string }>; then the handler requires the type |
I ran TS with tracing on and compared the runtime performance (by invoking type checking and auto completion in VSCode), against an example route. For each case, the route imported its params from io-ts and zod defined schema with four levels of nesting including a mix of object and array types. Here's the summary of executed commands by TS with their average times:
|
@dgieselaar I used a similar process as above to try to determine that. I imported route's params and response types from a separate zod file |
@awahab07 thank you! Two things:
|
@dgieselaar you can see the zod schema used in PR. io-ts schemas are defined alongside. For re-evaluation issue, and given this test here, do you have a suggestion to how to confirm that? Are we interested in evaluating zod explicit types vs. zod inferred types performance? |
💚 Build Succeeded
Metrics [docs]Public APIs missing comments
Public APIs missing exports
History
|
Closes #198680
Summary
The PR adds
responses
route config property toCreateServerRouteFactory
so that consumers can define response schemas while configuring routes. The schema is then used to generate OAS docs with response types as well as making route handler return payload type safe.Testing
Consult #187562 to generate OAS for a particular route during dev.
Example route configuration:
Type inference in client when using
createRepositoryClient
Generates OAS: