Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/fastify/fastify-type-provider-json-schema-to-ts

A Type Provider for json-schema-to-ts
https://github.com/fastify/fastify-type-provider-json-schema-to-ts

fastify fastify-library

Last synced: 6 days ago
JSON representation

A Type Provider for json-schema-to-ts

Awesome Lists containing this project

README

        

# @fastify/type-provider-json-schema-to-ts

[![CI](https://github.com/fastify/fastify-type-provider-json-schema-to-ts/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-type-provider-json-schema-to-ts/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/@fastify/type-provider-json-schema-to-ts.svg?style=flat)](https://www.npmjs.com/package/@fastify/type-provider-json-schema-to-ts)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)

A Type Provider for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts)

## Install

```bash
npm i @fastify/type-provider-json-schema-to-ts
```

## TypeScript requirements

It is required to use `[email protected]` or above with
[`strict`](https://www.typescriptlang.org/tsconfig#strict)
mode enabled and
[`noStrictGenericChecks`](https://www.typescriptlang.org/tsconfig#noStrictGenericChecks)
disabled. You may take the following configuration (`tsconfig.json`) as an example:

```json
{
"compilerOptions": {
"strict": true,
"noStrictGenericChecks": false
}
}
```

## Plugin definition

> **Note**
> When using plugin types, `withTypeProvider` is not required to register the plugin.

```ts
const plugin: FastifyPluginAsyncJsonSchemaToTs = async function (
fastify,
_opts
) {
fastify.get(
"/",
{
schema: {
body: {
type: "object",
properties: {
x: { type: "string" },
y: { type: "number" },
z: { type: "boolean" },
},
required: ["x", "y", "z"],
} as const,
},
},
(req) => {
// The `x`, `y`, and `z` types are automatically inferred
const { x, y, z } = req.body;
}
);
};
```

## Setting FromSchema for the validator and serializer

You can set the `FromSchema` settings for things like [`references`](https://github.com/ThomasAribart/json-schema-to-ts#references) and [`deserialization`](https://github.com/ThomasAribart/json-schema-to-ts#deserialization) for the validation and serialization schema by setting `ValidatorSchemaOptions` and `SerializerSchemaOptions` type parameters.
You can use the `deserialize` option in `SerializerSchemaOptions` to allow Date objects in place of date-time strings or other special serialization rules handled by [fast-json-stringify](https://github.com/fastify/fast-json-stringify?tab=readme-ov-file#specific-use-cases).

```ts
const userSchema = {
type: "object",
additionalProperties: false,
properties: {
givenName: { type: "string" },
familyName: { type: "string" },
},
required: ["givenName", "familyName"],
} as const satisfies JSONSchema;

const sharedSchema = {
$id: "shared-schema",
definitions: {
user: userSchema,
},
} as const satisfies JSONSchema;

const userProfileSchema = {
$id: "userProfile",
type: "object",
additionalProperties: false,
properties: {
user: {
$ref: "shared-schema#/definitions/user",
},
joinedAt: { type: "string", format: "date-time" },
},
required: ["user", "joinedAt"],
} as const satisfies JSONSchema

type UserProfile = FromSchema;

// Use JsonSchemaToTsProvider with shared schema references
const fastify = Fastify().withTypeProvider<
JsonSchemaToTsProvider<{
ValidatorSchemaOptions: { references: [typeof sharedSchema] }
SerializerSchemaOptions: {
references: [typeof userProfileSchema]
deserialize: [{ pattern: { type: "string"; format: "date-time" }; output: Date }]
}
}>
>()

fastify.get(
"/profile",
{
schema: {
body: {
type: "object",
properties: {
user: {
$ref: "shared-schema#/definitions/user",
},
},
required: ['user'],
},
response: {
200: { $ref: "userProfile#" },
},
} as const,
},
(req, reply) => {
// `givenName` and `familyName` are correctly typed as strings
const { givenName, familyName } = req.body.user;

// Construct a compatible response type
const profile: UserProfile = {
user: { givenName: "John", familyName: "Doe" },
joinedAt: new Date(), // Returning a Date object
};

// A type error is surfaced if profile doesn't match the serialization schema
reply.send(profile)
}
)
```

## Using References in a Plugin Definition

When defining a plugin, shared schema references and deserialization options can also be used with `FastifyPluginAsyncJsonSchemaToTs` and `FastifyPluginCallbackJsonSchemaToTs`.

### Example

```ts
const schemaPerson = {
$id: "schema:person",
type: "object",
additionalProperties: false,
properties: {
givenName: { type: "string" },
familyName: { type: "string" },
joinedAt: { type: "string", format: "date-time" },
},
required: ["givenName", "familyName"],
} as const satisfies JSONSchema;

const plugin: FastifyPluginAsyncJsonSchemaToTs<{
ValidatorSchemaOptions: { references: [typeof schemaPerson] }
SerializerSchemaOptions: {
references: [typeof schemaPerson]
deserialize: [{ pattern: { type: "string"; format: "date-time" }; output: Date }]
};
}> = async function (fastify, _opts) {
fastify.addSchema(schemaPerson)

fastify.get(
"/profile",
{
schema: {
body: {
type: "object",
properties: {
user: {
$ref: "schema:person",
},
},
required: ['user'],
},
response: {
200: { $ref: "schema:person" },
},
}, // as const satisfies JSONSchema is not required thanks to FastifyPluginAsyncJsonSchemaToTs
},
(req, reply) => {
// `givenName`, `familyName`, and `joinedAt` are correctly typed as strings and validated for format.
const { givenName, familyName, joinedAt } = req.body.user;

// Send a serialized response
reply.send({
givenName: "John",
familyName: "Doe",
// Date objects form DB queries can be returned directly and transformed to string by fast-json-stringify
joinedAt: new Date(),
})
}
)
}

const callbackPlugin: FastifyPluginCallbackJsonSchemaToTs<{
ValidatorSchemaOptions: { references: [typeof schemaPerson] }
SerializerSchemaOptions: {
references: [typeof schemaPerson]
deserialize: [{ pattern: { type: "string"; format: "date-time" }; output: Date }]
};
}> = (fastify, options, done) => {
// Type check for custom options
expectType(options.optionA)

// Schema is already added above
// fastify.addSchema(schemaPerson);

fastify.get(
"/callback-profile",
{
schema: {
body: {
type: "object",
properties: {
user: { $ref: "schema:person" },
},
required: ["user"],
},
response: {
200: { $ref: "schema:person" },
},
},
},
(req, reply) => {
const { givenName, familyName, joinedAt } = req.body.user

reply.send({
givenName,
familyName,
joinedAt: new Date(),
});
}
);

done()
};
```

## License

Licensed under [MIT](./LICENSE).