-
Notifications
You must be signed in to change notification settings - Fork 339
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 :
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.
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 = <SchemasTypes extends Record<string, object>>(
schemas: SchemasTypes,
) => {
const ajv = new Ajv2020();
addFormats.default(ajv);
for (const [name, schema] of Object.entries(schemas)) {
ajv.addSchema(schema, name);
}
const validate = <T extends Extract<keyof SchemasTypes, string>>(
type: T,
...[data, dataCtx]: Parameters<ValidateFunction>
): JTDDataType<SchemasTypes[T]> => {
const schema = ajv.getSchema<JTDDataType<SchemasTypes[T]>>(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 :
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
- Follow our Code of Conduct
- Read the docs.
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.