microsoft / microsoft/typespec

[http-server-js] Add support for body validation

Open
#9,889 3 comments 0 reactions 0 assignees View on GitHub
emitter:service:js feature triaged:core
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.