microsoft / microsoft/typespec
[http-server-js] Add support for body validation
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
### Clear and concise description of the problem
### Context
Typespec's JS emitter is a great approach to API servers! The type safety it brings is 👌
What I really miss is a validation mechanism. Currently the `body` available in the endpoint handler is properly typed, but is not actually validated against the schema. It gives a false sense of safety.
I've tried implementing my workaround(see below) in a policy, so it's executed before each endpoint, but this would mean to reimplement the routing logic (maybe related to #6933?).
### Feature request
I'd love for this to be natively available.
If it's not desired (because of dependecy to a library for instance), a great option would be to add an optional `validate` function on the options of the router.
Something like this :
```ts
const router = createDemoServiceRouter(widgetService, {
validate: (ctx, body) => {
// ctx would need to contain at least the path, ideally also the related openAPI schema ?
}
}
```
### Workaround
Here is my current workaround : I load the openApiDocument, inject the schema in AJV, and validate the body in each endpoint where I need it.
```ts
import { Ajv2020, ValidationError, ValidateFunction } from 'ajv/dist/2020.js';
import type { JTDDataType } from 'ajv/dist/core.js';
import addFormats from 'ajv-formats';
import { BadRequestError } from '../errors.js';
export const createSchemaValidator = >(
schemas: SchemasTypes,
) => {
const ajv = new Ajv2020();
addFormats.default(ajv);
for (const [name, schema] of Object.entries(schemas)) {
ajv.addSchema(schema, name);
}
const validate = >(
type: T,
...[data, dataCtx]: Parameters
): JTDDataType => {
const schema = ajv.getSchema>(type);
if (!schema) {
throw new Error(`Schema ${type} not found`);
}
if (!schema(data, dataCtx)) {
const error = new ValidationError(schema.errors ?? []);
if (error.errors.length === 1) {
const err = error.errors[0];
const formattedError = err.message;
const formattedMessage = err.instancePath
? `Validation failed (${err.instancePath})`
: 'Validation failed';
throw new BadRequestError(`${formattedMessage}: ${formattedError}`);
}
throw new BadRequestError(`Validation failed`, error.errors);
}
return data;
};
return validate;
};
```
Usage :
```ts
import { openApiDocument } from "./http/openapi3";
const schemas = openApiDocument.components.schemas;
const validate = createSchemaValidator(schemas);
// ...
// in endpoint handler
const { id, weight } = validate("Widget", body);
// typesafe, and validated against the open API schema.
```
### Checklist
- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Read the [docs](https://typespec.io/docs/).
- [x] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
Contributor guide
Assessment
This issue has not been assessed yet.