ArkType `toJsonSchema` returning not open-api compatible schema.

Hi, I am trying to get my arktype schemas working with fastify (was previously using zod but editor performance was tragic) so I started with setting OpenAPI. Zod tools use https://www.npmjs.com/package/zod-to-json-schema for this but since ArkType has a method built in I decided to use it the output however is different and zod's one actually uses enums/unions in a way that openapi can handle.
import { type } from "arktype";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const mySchemaZod = z
.object({
myString: z.string().min(5),
myUnion: z.union([z.number(), z.boolean()]),
})
.describe("My neat object schema");

const jsonSchemaZod = zodToJsonSchema(mySchemaZod, "mySchema");

console.log("ZOD ", jsonSchemaZod);

const mySchemaArk = type({
myString: "string >= 5",
myUnion: "number | boolean"
})

const jsonSchemaArk = mySchemaArk.toJsonSchema();

console.log("ARKTYPE ", jsonSchemaArk);
import { type } from "arktype";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const mySchemaZod = z
.object({
myString: z.string().min(5),
myUnion: z.union([z.number(), z.boolean()]),
})
.describe("My neat object schema");

const jsonSchemaZod = zodToJsonSchema(mySchemaZod, "mySchema");

console.log("ZOD ", jsonSchemaZod);

const mySchemaArk = type({
myString: "string >= 5",
myUnion: "number | boolean"
})

const jsonSchemaArk = mySchemaArk.toJsonSchema();

console.log("ARKTYPE ", jsonSchemaArk);
18 Replies
stanisław
stanisławOP5d ago
ZOD {
$ref: "#/definitions/mySchema",
definitions: {
mySchema: {
type: "object",
properties: {
myString: {
type: "string",
minLength: 5,
},
myUnion: {
type: [ "number", "boolean" ],
},
},
required: [ "myString", "myUnion" ],
additionalProperties: false,
description: "My neat object schema",
},
},
$schema: "http://json-schema.org/draft-07/schema#",
}
ARKTYPE {
type: "object",
properties: {
myString: {
type: "string",
minLength: 5,
},
myUnion: {
anyOf: [
{
type: "number",
}, {
type: "boolean",
}
],
},
},
required: [ "myString", "myUnion" ],
}
ZOD {
$ref: "#/definitions/mySchema",
definitions: {
mySchema: {
type: "object",
properties: {
myString: {
type: "string",
minLength: 5,
},
myUnion: {
type: [ "number", "boolean" ],
},
},
required: [ "myString", "myUnion" ],
additionalProperties: false,
description: "My neat object schema",
},
},
$schema: "http://json-schema.org/draft-07/schema#",
}
ARKTYPE {
type: "object",
properties: {
myString: {
type: "string",
minLength: 5,
},
myUnion: {
anyOf: [
{
type: "number",
}, {
type: "boolean",
}
],
},
},
required: [ "myString", "myUnion" ],
}
This is the generated JSON for both. You can see that zod has
myUnion: {
type: [ "number", "boolean" ],
}
myUnion: {
type: [ "number", "boolean" ],
}
and ArkType has anyOf
ssalbdivad
ssalbdivad5d ago
I guess this is naive but what is the difference?
stanisław
stanisławOP5d ago
The difference is the interpretation of such a schema by open api generators
openapi: 3.0.3
info:
title: Docs
version: 1.0.0
components:
schemas: {}
paths:
/v{v}/route:
get:
parameters:
- schema:
anyOf:
- enum:
- '0'
- enum:
- '1'
in: path
name: v
required: true
responses:
'200':
description: Default Response
openapi: 3.0.3
info:
title: Docs
version: 1.0.0
components:
schemas: {}
paths:
/v{v}/route:
get:
parameters:
- schema:
anyOf:
- enum:
- '0'
- enum:
- '1'
in: path
name: v
required: true
responses:
'200':
description: Default Response
ssalbdivad
ssalbdivad5d ago
I actually didn't know this syntax existed, but it's actually closer to ArkType's internal Json representation
No description
stanisław
stanisławOP5d ago
export const S_params = type({
v: "'0' | '1'"
})
export const S_params = type({
v: "'0' | '1'"
})
that's this params https://editor.swagger.io/ But when u actually paste it to open api
stanisław
stanisławOP5d ago
No description
stanisław
stanisławOP5d ago
then u can see that value is not an enum it's just 0 where with zod its correct 0 or 1
ssalbdivad
ssalbdivad5d ago
I am planning some JSON schema work in 2.1.0. If you want to create an issue I can simplify these cases where possible My only goal in releasing to toJsonSchema() was to generate valid JSON schema. I didn't look at OpenAPI or anything similar, which I guess accepts a subset of JSON schema? Happy to see where I can align with that
stanisław
stanisławOP5d ago
https://github.com/fastify/fastify-swagger I am using this internally (not that this issue might not be related to arktype I just noticed the difference when I was migrating and it's a pretty big deal)
GitHub
GitHub - fastify/fastify-swagger: Swagger documentation generator f...
Swagger documentation generator for Fastify. Contribute to fastify/fastify-swagger development by creating an account on GitHub.
stanisław
stanisławOP5d ago
await server.register(import("@fastify/swagger"), {
openapi: {
info: {
title: "Docs",
version: "1.0.0",
}
},
transform: (item) => {
const { response, headers, querystring, body, params, ...rest } = item.schema

if (response) {
for (const status in response as Record<string, unknown>) {
// @ts-expect-error -- That's ok it should be an object with status codes as keys
const responseSchema = response[status] as unknown


// @ts-expect-error -- That's ok it should be an object with status codes as keys
// eslint-disable-next-line
response[status] = responseSchema instanceof Type ? responseSchema.toJsonSchema() : arkSchema
}
}

return {
...item,
schema: {
...rest,
response,
headers: headers instanceof Type ? headers.toJsonSchema() : headers,
querystring: querystring instanceof Type ? querystring.toJsonSchema() : querystring,
body: body instanceof Type ? body.toJsonSchema() : body,
params: params instanceof Type ? params.toJsonSchema() : params,
}
}
}
})
await server.register(import("@fastify/swagger"), {
openapi: {
info: {
title: "Docs",
version: "1.0.0",
}
},
transform: (item) => {
const { response, headers, querystring, body, params, ...rest } = item.schema

if (response) {
for (const status in response as Record<string, unknown>) {
// @ts-expect-error -- That's ok it should be an object with status codes as keys
const responseSchema = response[status] as unknown


// @ts-expect-error -- That's ok it should be an object with status codes as keys
// eslint-disable-next-line
response[status] = responseSchema instanceof Type ? responseSchema.toJsonSchema() : arkSchema
}
}

return {
...item,
schema: {
...rest,
response,
headers: headers instanceof Type ? headers.toJsonSchema() : headers,
querystring: querystring instanceof Type ? querystring.toJsonSchema() : querystring,
body: body instanceof Type ? body.toJsonSchema() : body,
params: params instanceof Type ? params.toJsonSchema() : params,
}
}
}
})
ssalbdivad
ssalbdivad5d ago
That makes sense because I haven't looked at OpenAPI directly at all
stanisław
stanisławOP5d ago
and there is a transform
ssalbdivad
ssalbdivad5d ago
But I know it would be valuable to a lot of users so next release seems like the right time if you want to create an issue! Just be sure to include the cases that currently are generated in a non-compatible format and could be generated in a comptable one
stanisław
stanisławOP5d ago
sure thanks for help It would be useful to look at this zod to json schema as it looks battle tested and make the outputs similar so the tools like open api work properly
ssalbdivad
ssalbdivad4d ago
I mean I don't think the issue is JSON schema AFAIK the issue is OpenAPI compatability (or maybe something to do with Fastify not 100%). So I'd probably just want to look at whatever that spec was and make sure what we're generating conforms to it @stanisław Are you able to create a GH issue for this summarizing the cases you expect to work? Otherwise I will try to create one but would be useful to get it from someone using this tooling
stanisław
stanisławOP4d ago
I will do it @ArkDavid if not today then on the weekend
stanisław
stanisławOP4d ago
GitHub
Make toJsonSchema method compatible with OpenAPI spec / @fastify/sw...
Request a feature Make output of toJsonSchema method on enums/unions compatible with OpenAPI spec and make the output look the same to other popular tools like zod-to-json-schema for zod. 🤷 Motivat...
ssalbdivad
ssalbdivad4d ago
Thank you!

Did you find this page helpful?