Una biblioteca Typecript para usar Zod Schemas para crear documentación OpenAPI v3.x
Instalar a través de npm
, yarn
o pnpm
:
npm install zod zod-openapi
# # or
yarn add zod zod-openapi
# # or
pnpm install zod zod-openapi
Esto muta a Zod para agregar un método .openapi()
adicional. Llame a esto en la parte superior de su(s) punto(s) de entrada. Puede lograr esto de dos maneras diferentes, según sus preferencias.
import 'zod-openapi/extend' ;
import { z } from 'zod' ;
z . string ( ) . openapi ( { description : 'hello world!' , example : 'hello world' } ) ;
Esto es útil si tiene una instancia específica de Zod o una instancia de Zod de otra biblioteca a la que le gustaría dirigirse.
import { z } from 'zod' ;
import { extendZodWithOpenApi } from 'zod-openapi' ;
extendZodWithOpenApi ( z ) ;
z . string ( ) . openapi ( { description : 'hello world!' , example : 'hello world' } ) ;
.openapi()
Utilice el método .openapi()
para agregar metadatos a un tipo de Zod específico. El método .openapi()
toma un objeto con las siguientes opciones:
Opción | Descripción |
---|---|
Opciones de API abierta | Esto tomará cualquier opción que pondría en un SchemaObject. |
effectType | Úselo para anular el tipo de creación de un efecto Zod |
header | Úselo para proporcionar metadatos para encabezados de respuesta |
param | Úselo para proporcionar metadatos para los parámetros de solicitud. |
ref | Utilice esto para registrar automáticamente un esquema como componente reutilizable |
refType | Utilícelo para establecer el tipo de creación de un componente al que no se hace referencia en el documento. |
type | Utilice esto para anular el tipo generado. Si se proporciona esto, no se generarán metadatos. |
unionOneOf | Establezca en true para forzar que un único ZodUnion genere oneOf en lugar de anyOf . Consulte CreateDocumentOptions para ver una opción global. |
createDocument
Crea un objeto de documentación OpenAPI
import 'zod-openapi/extend' ;
import { z } from 'zod' ;
import { createDocument } from 'zod-openapi' ;
const jobId = z . string ( ) . openapi ( {
description : 'A unique identifier for a job' ,
example : '12345' ,
ref : 'jobId' ,
} ) ;
const title = z . string ( ) . openapi ( {
description : 'Job title' ,
example : 'My job' ,
} ) ;
const document = createDocument ( {
openapi : '3.1.0' ,
info : {
title : 'My API' ,
version : '1.0.0' ,
} ,
paths : {
'/jobs/{jobId}' : {
put : {
requestParams : { path : z . object ( { jobId } ) } ,
requestBody : {
content : {
'application/json' : { schema : z . object ( { title } ) } ,
} ,
} ,
responses : {
'200' : {
description : '200 OK' ,
content : {
'application/json' : { schema : z . object ( { jobId , title } ) } ,
} ,
} ,
} ,
} ,
} ,
} ,
} ) ;
{
"openapi" : " 3.1.0 " ,
"info" : {
"title" : " My API " ,
"version" : " 1.0.0 "
},
"paths" : {
"/jobs/{jobId}" : {
"put" : {
"parameters" : [
{
"in" : " path " ,
"name" : " jobId " ,
"description" : " A unique identifier for a job " ,
"schema" : {
"$ref" : " #/components/schemas/jobId "
}
}
],
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"type" : " object " ,
"properties" : {
"title" : {
"type" : " string " ,
"description" : " Job title " ,
"example" : " My job "
}
},
"required" : [ " title " ]
}
}
}
},
"responses" : {
"200" : {
"description" : " 200 OK " ,
"content" : {
"application/json" : {
"schema" : {
"type" : " object " ,
"properties" : {
"jobId" : {
"$ref" : " #/components/schemas/jobId "
},
"title" : {
"type" : " string " ,
"description" : " Job title " ,
"example" : " My job "
}
},
"required" : [ " jobId " , " title " ]
}
}
}
}
}
}
}
},
"components" : {
"schemas" : {
"jobId" : {
"type" : " string " ,
"description" : " A unique identifier for a job " ,
"example" : " 12345 "
}
}
}
}
createDocument
toma un argumento CreateDocumentOptions
opcional que se puede usar para modificar cómo se crea el documento.
const document = createDocument ( details , {
defaultDateSchema : { type : 'string' , format : 'date-time' } , // defaults to { type: 'string' }
unionOneOf : true , // defaults to false. Forces all ZodUnions to output oneOf instead of anyOf. An `.openapi()` `unionOneOf` value takes precedence over this one.
} ) ;
createSchema
Crea un objeto de esquema OpenAPI junto con cualquier componente registrado. Los objetos de esquema OpenAPI 3.1.0 son totalmente compatibles con el esquema JSON.
import 'zod-openapi/extend' ;
import { z } from 'zod' ;
import { createSchema } from 'zod-openapi' ;
const jobId = z . string ( ) . openapi ( {
description : 'A unique identifier for a job' ,
example : '12345' ,
ref : 'jobId' ,
} ) ;
const title = z . string ( ) . openapi ( {
description : 'Job title' ,
example : 'My job' ,
} ) ;
const job = z . object ( {
jobId ,
title ,
} ) ;
const { schema , components } = createSchema ( job ) ;
{
"schema" : {
"type" : " object " ,
"properties" : {
"jobId" : {
"$ref" : " #/components/schemas/jobId "
},
"title" : {
"type" : " string " ,
"description" : " Job title " ,
"example" : " My job "
}
},
"required" : [ " jobId " , " title " ]
},
"components" : {
"jobId" : {
"type" : " string " ,
"description" : " A unique identifier for a job " ,
"example" : " 12345 "
}
}
}
createSchema
toma un parámetro CreateSchemaOptions
opcional que también puede tomar las mismas opciones que CreateDocumentOptions junto con las siguientes opciones:
const { schema , components } = createSchema ( job , {
schemaType : 'input' ; // This controls whether this should be rendered as a request (`input`) or response (`output`). Defaults to `output`
openapi: '3.0.0' ; // OpenAPI version to use, defaults to `'3.1.0'`
components: { jobId : z . string ( ) } // Additional components to use and create while rendering the schema
componentRefPath: '#/definitions/' // Defaults to #/components/schemas/
} )
Los parámetros de consulta, ruta, encabezado y cookie se pueden crear usando la clave requestParams
debajo de la clave method
de la siguiente manera:
createDocument ( {
paths : {
'/jobs/{a}' : {
put : {
requestParams : {
path : z . object ( { a : z . string ( ) } ) ,
query : z . object ( { b : z . string ( ) } ) ,
cookie : z . object ( { cookie : z . string ( ) } ) ,
header : z . object ( { 'custom-header' : z . string ( ) } ) ,
} ,
} ,
} ,
} ,
} ) ;
Si desea declarar parámetros de una manera más tradicional, también puede declararlos usando la clave de parámetros. Luego se combinarán todas las definiciones.
createDocument ( {
paths : {
'/jobs/{a}' : {
put : {
parameters : [
z . string ( ) . openapi ( {
param : {
name : 'job-header' ,
in : 'header' ,
} ,
} ) ,
] ,
} ,
} ,
} ,
} ) ;
Donde normalmente declararía el tipo de medio, configure el schema
como su esquema Zod de la siguiente manera.
createDocument ( {
paths : {
'/jobs' : {
get : {
requestBody : {
content : {
'application/json' : { schema : z . object ( { a : z . string ( ) } ) } ,
} ,
} ,
} ,
} ,
} ,
} ) ;
Si desea utilizar la sintaxis OpenAPI para sus esquemas, simplemente agregue un esquema OpenAPI al campo de schema
.
De manera similar al cuerpo de la solicitud, simplemente configure el schema
como su esquema Zod de la siguiente manera. Puede configurar los encabezados de respuesta usando la tecla headers
.
createDocument ( {
paths : {
'/jobs' : {
get : {
responses : {
200 : {
description : '200 OK' ,
content : {
'application/json' : { schema : z . object ( { a : z . string ( ) } ) } ,
} ,
headers : z . object ( {
'header-key' : z . string ( ) ,
} ) ,
} ,
} ,
} ,
} ,
} ,
} ) ;
createDocument ( {
paths : {
'/jobs' : {
get : {
callbacks : {
onData : {
'{$request.query.callbackUrl}/data' : {
post : {
requestBody : {
content : {
'application/json' : { schema : z . object ( { a : z . string ( ) } ) } ,
} ,
} ,
responses : {
200 : {
description : '200 OK' ,
content : {
'application/json' : {
schema : z . object ( { a : z . string ( ) } ) ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
} ) ;
OpenAPI le permite definir componentes reutilizables y esta biblioteca le permite replicarlos de dos maneras distintas.
Si tomamos el ejemplo en createDocument
y en su lugar creamos title
de la siguiente manera
const title = z . string ( ) . openapi ( {
description : 'Job title' ,
example : 'My job' ,
ref : 'jobTitle' , // <- new field
} ) ;
Siempre que se utilice title
en los esquemas del documento, se creará como referencia.
{ "$ref" : " #/components/schemas/jobTitle " }
title
se generará como un esquema dentro de la sección de componentes de la documentación.
{
"components" : {
"schemas" : {
"jobTitle" : {
"type" : " string " ,
"description" : " Job title " ,
"example" : " My job "
}
}
}
}
Esta puede ser una forma extremadamente poderosa de crear documentación de Open API menos repetitiva. Hay algunas características de Open API, como el mapeo discriminador, que requieren que todos los esquemas de la unión contengan una referencia.
Otra forma de registrar un esquema en lugar de agregar una ref
es agregarlo directamente a los componentes. Esto seguirá funcionando de la misma manera que ref
. Entonces, cada vez que nos encontremos con ese tipo de Zod, lo reemplazaremos con una referencia.
p.ej.
createDocument ( {
components : {
schemas : {
jobTitle : title , // this will register this Zod Schema as jobTitle unless `ref` in `.openapi()` is specified on the type
} ,
} ,
} ) ;
Desafortunadamente, como limitación de esta biblioteca, deberá adjuntar un campo .openapi()
o .describe()
al esquema que está pasando a los componentes o, de lo contrario, es posible que no obtenga toda la potencia de generación de componentes. Como resultado, recomiendo utilizar los componentes de registro automático en lugar del registro manual.
Los parámetros de consulta, ruta, encabezado y cookie se pueden registrar de manera similar:
// Easy auto registration
const jobId = z . string ( ) . openapi ( {
description : 'Job ID' ,
example : '1234' ,
param : { ref : 'jobRef' } ,
} ) ;
createDocument ( {
paths : {
'/jobs/{jobId}' : {
put : {
requestParams : {
header : z . object ( {
jobId ,
} ) ,
} ,
} ,
} ,
} ,
} ) ;
// or more verbose auto registration
const jobId = z . string ( ) . openapi ( {
description : 'Job ID' ,
example : '1234' ,
param : { in : 'header' , name : 'jobId' , ref : 'jobRef' } ,
} ) ;
createDocument ( {
paths : {
'/jobs/{jobId}' : {
put : {
parameters : [ jobId ] ,
} ,
} ,
} ,
} ) ;
// or manual registeration
const otherJobId = z . string ( ) . openapi ( {
description : 'Job ID' ,
example : '1234' ,
param : { in : 'header' , name : 'jobId' } ,
} ) ;
createDocument ( {
components : {
parameters : {
jobRef : jobId ,
} ,
} ,
} ) ;
Los encabezados de respuesta se pueden registrar de manera similar:
const header = z . string ( ) . openapi ( {
description : 'Job ID' ,
example : '1234' ,
header : { ref : 'some-header' } ,
} ) ;
// or
const jobIdHeader = z . string ( ) . openapi ( {
description : 'Job ID' ,
example : '1234' ,
} ) ;
createDocument ( {
components : {
headers : {
someHeaderRef : jobIdHeader ,
} ,
} ,
} ) ;
También se pueden registrar respuestas completas.
const response : ZodOpenApiResponseObject = {
description : '200 OK' ,
content : {
'application/json' : {
schema : z . object ( { a : z . string ( ) } ) ,
} ,
} ,
ref : 'some-response' ,
} ;
//or
const response : ZodOpenApiResponseObject = {
description : '200 OK' ,
content : {
'application/json' : {
schema : z . object ( { a : z . string ( ) } ) ,
} ,
} ,
} ;
createDocument ( {
components : {
responses : {
'some-response' : response ,
} ,
} ,
} ) ;
Las devoluciones de llamada también se pueden registrar
const callback : ZodOpenApiCallbackObject = {
ref : 'some-callback'
post : {
responses : {
200 : {
description : '200 OK' ,
content : {
'application/json' : {
schema : z . object ( { a : z . string ( ) } ) ,
}