-
Notifications
You must be signed in to change notification settings - Fork 179
feat(event-handler): add validation support for REST router #4736
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
base: main
Are you sure you want to change the base?
Conversation
|
|
Given that this is just middleware under the hood, I think we can simplify the API here. We already allow to users to pass middleware into their routes, I don't see a reason to make the type signature of the HTTP verb methods more complicated when we can do this instead: import {validation} from '@aws-lambda-powertools/event-handler/experimental-rest/middleware';
app.post('/users', [validation({ req: { body: z.object({ name: z.string() }) } })], async () => {
return { id: '123', name: 'John' };
}); |
|
This was implemented this way because of the RFC. It's true that it's just a middleware, but I'd like us to at least consider the option of having a special treatment for this one. I'm suggesting this because of two aspects:
Arguably the first one might be a skill issue on my side, if there's a way to mitigate it, happy to remove the concern. For the second, it's primarily preference so again, I just wanted to have a discussion, not trying to force any other direction. Also @sdangol, I'd consider moving all the AIDLC files into a public gist and linking that one in the PR body, the sheer amount of markdown generated is a bit unwieldy when it comes to looking at the diff. |
| // Validate request | ||
| if (reqSchemas) { | ||
| if (reqSchemas.body) { | ||
| let bodyData: unknown = reqCtx.event.body; |
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.
We should avoid using the raw event in middleware because they are specific to the particualr lambda integration. This means that the middleware may only work with say API Gateway or function URLs etc. We should use the req and res objects where bossible. Bearing in mind that if we wish to use the body or request or a response, we need to clone it.
| path: Path, | ||
| middlewareOrHandler?: Middleware[] | RouteHandler, | ||
| handler?: RouteHandler | ||
| handlerOrOptions?: RouteHandler | Omit<RestRouteOptions, 'method' | 'path'>, |
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.
As mentioned in #4736 (comment) we won't need to change anything in all these HTTP methods if we treat validation as just another middleware.
| const contentType = response.headers.get('content-type'); | ||
|
|
||
| let bodyData: unknown; | ||
| if (contentType?.includes('application/json')) { |
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 can be simplified:
const bodyData = contentType?.includes('application/json')
? await clonedResponse.json() : await clonedResponse.text();;| schema: StandardSchemaV1, | ||
| data: unknown, | ||
| component: 'body' | 'headers' | 'path' | 'query', | ||
| isRequest: boolean |
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.
Magic booleans in APIs are a code smell imo.
| /** | ||
| * Configuration for request validation | ||
| */ | ||
| type RequestValidationConfig<T = unknown> = { |
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.
Seems odd to me that these are all optional. Is there a way to express through the type system that at least one of these should be present?
|
I'll answer your points in reverse order @dreamorosi.
I wouldn't recommend embedding a large validation, espeically OpenAPI, in the handler signature either way. I would have a separate place for the validation definitions, something like this: // validators.ts
import {validation} from '@aws-lambda-powertools/event-handler/experimental-rest/middleware';
export userValidation = validiation(/** very large validation object **/);
// index.ts
import {userValidation} from './validators.js';
app.post('/users', [userValidation], async () => {
return { id: '123', name: 'John' };
});
While I would be disappointed to see the simplicity of treating everything as middleware go, this is a very compelling argument. Trying to infer types from things that may or may not be in an array sounds like a nightmare, especially when the elements in that array are just functions. My main objection to adding this extra argument is the compelxity it creates because we now have these HTTP methods that can take, 2, 3 or 4 arguments depending on the use case. This is also compounded when you throw decorators into the mix. I think maybe the way forward then is to have a options object so that we can reduce the combinations of arguments. This way the function will only ever have 2 or 3 arguments. app.post('/users', async () => {
return { id: '123', name: 'John' };
}, {middleware: [ /** ... */], validation: {/** ... */} });I don't love the way the middleware is after the handler here but having the optional argument last simplifies things more imo. Obviously this is a decision that needs to be made before GA as it is a breaking change. |



Summary
Note
This PR is an experimental PR generated using the AI-DLC workflow with the model Claude Sonnet 4.5
Changes
This PR adds first-class support for data validation in the Event Handler REST router using the Standard Schema specification.
Issue number: closes #4516
What's Changed
createValidationMiddlewarefunction that validates request and response data using Standard Schema-compliant libraries (Zod, Valibot, ArkType)validationoption to route configuration for declarative validationRequestValidationError(422) andResponseValidationError(500)Implementation Details
New Files:
packages/event-handler/src/rest/middleware/validation.ts- Validation middleware implementationpackages/event-handler/src/rest/errors.ts- AddedRequestValidationErrorandResponseValidationErrorclassespackages/event-handler/tests/unit/rest/middleware/validation.test.ts- Comprehensive test suite (16 tests)packages/event-handler/tests/unit/rest/validation-errors.test.ts- Error class tests (16 tests)Modified Files:
packages/event-handler/src/rest/Router.ts- Added validation middleware integrationpackages/event-handler/src/types/rest.ts- Added validation types and optionsexamples/snippets/event-handler/rest/validation_*.ts- Example code snippetsdocs/features/event-handler/rest.md- Documentation for validation featureAPI Usage
Testing
Breaking Changes
None - this is a new feature addition.
Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.