Une bibliothèque Typescript pour utiliser Zod Schemas pour créer de la documentation OpenAPI v3.x
Installer via npm
, yarn
ou pnpm
:
npm install zod zod-openapi
# # or
yarn add zod zod-openapi
# # or
pnpm install zod zod-openapi
Cela mute Zod pour ajouter une méthode .openapi()
supplémentaire. Appelez-le en haut de votre (vos) point(s) d'entrée. Vous pouvez y parvenir de deux manières différentes, selon vos préférences.
import 'zod-openapi/extend' ;
import { z } from 'zod' ;
z . string ( ) . openapi ( { description : 'hello world!' , example : 'hello world' } ) ;
Ceci est utile si vous disposez d'une instance spécifique de Zod ou d'une instance de Zod d'une autre bibliothèque que vous souhaitez cibler.
import { z } from 'zod' ;
import { extendZodWithOpenApi } from 'zod-openapi' ;
extendZodWithOpenApi ( z ) ;
z . string ( ) . openapi ( { description : 'hello world!' , example : 'hello world' } ) ;
.openapi()
Utilisez la méthode .openapi()
pour ajouter des métadonnées à un type Zod spécifique. La méthode .openapi()
prend un objet avec les options suivantes :
Option | Description |
---|---|
Options OpenAPI | Cela prendra n'importe quelle option que vous mettriez sur un SchemaObject. |
effectType | Utilisé pour remplacer le type de création d'un effet Zod |
header | Utiliser pour fournir des métadonnées pour les en-têtes de réponse |
param | Utiliser pour fournir des métadonnées pour les paramètres de requête |
ref | Utilisez-le pour enregistrer automatiquement un schéma en tant que composant réutilisable |
refType | Utilisez ceci pour définir le type de création d'un composant qui n'est pas référencé dans le document. |
type | Utilisez-le pour remplacer le type généré. Si cela est fourni, aucune métadonnée ne sera générée. |
unionOneOf | Définissez sur true pour forcer un seul ZodUnion à afficher oneOf au lieu de anyOf . Voir CreateDocumentOptions pour une option globale |
createDocument
Crée un objet de documentation 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
prend un argument CreateDocumentOptions
facultatif qui peut être utilisé pour modifier la façon dont le document est créé.
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
Crée un objet de schéma OpenAPI avec tous les composants enregistrés. Les objets de schéma OpenAPI 3.1.0 sont entièrement compatibles avec le schéma 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
prend un paramètre CreateSchemaOptions
facultatif qui peut également prendre les mêmes options que CreateDocumentOptions ainsi que les options suivantes :
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/
} )
Les paramètres de requête, de chemin, d'en-tête et de cookie peuvent être créés à l'aide de la clé requestParams
sous la clé method
comme suit :
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 vous souhaitez déclarer des paramètres de manière plus traditionnelle, vous pouvez également les déclarer à l'aide de la clé settings. Les définitions seront alors toutes combinées.
createDocument ( {
paths : {
'/jobs/{a}' : {
put : {
parameters : [
z . string ( ) . openapi ( {
param : {
name : 'job-header' ,
in : 'header' ,
} ,
} ) ,
] ,
} ,
} ,
} ,
} ) ;
Là où vous déclareriez normalement le type de média, définissez le schema
comme votre schéma Zod comme suit.
createDocument ( {
paths : {
'/jobs' : {
get : {
requestBody : {
content : {
'application/json' : { schema : z . object ( { a : z . string ( ) } ) } ,
} ,
} ,
} ,
} ,
} ,
} ) ;
Si vous souhaitez utiliser la syntaxe OpenAPI pour vos schémas, ajoutez simplement un schéma OpenAPI au champ schema
.
De la même manière que pour le corps de la demande, définissez simplement le schema
comme votre schéma Zod comme suit. Vous pouvez définir les en-têtes de réponse à l’aide de la touche 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 vous permet de définir des composants réutilisables et cette bibliothèque vous permet de les répliquer de deux manières distinctes.
Si nous prenons l'exemple de createDocument
et créons à la place title
comme suit
const title = z . string ( ) . openapi ( {
description : 'Job title' ,
example : 'My job' ,
ref : 'jobTitle' , // <- new field
} ) ;
Partout où title
est utilisé dans les schémas du document, il sera créé comme référence.
{ "$ref" : " #/components/schemas/jobTitle " }
title
sera ensuite affiché sous forme de schéma dans la section des composants de la documentation.
{
"components" : {
"schemas" : {
"jobTitle" : {
"type" : " string " ,
"description" : " Job title " ,
"example" : " My job "
}
}
}
}
Cela peut être un moyen extrêmement puissant de créer une documentation Open API moins répétitive. Certaines fonctionnalités de l'Open API, telles que le mappage de discriminateur, nécessitent que tous les schémas de l'union contiennent une référence.
Une autre façon d'enregistrer un schéma au lieu d'ajouter une ref
consiste à l'ajouter directement aux composants. Cela fonctionnera toujours de la même manière que ref
. Ainsi, chaque fois que nous rencontrons ce type Zod, nous le remplacerons par une référence.
par exemple.
createDocument ( {
components : {
schemas : {
jobTitle : title , // this will register this Zod Schema as jobTitle unless `ref` in `.openapi()` is specified on the type
} ,
} ,
} ) ;
Malheureusement, comme limitation de cette bibliothèque, vous devrez attacher un champ .openapi()
ou .describe()
au schéma que vous transmettez aux composants, sinon vous risquez de ne pas obtenir toute la puissance de la génération de composants. Par conséquent, je recommande d’utiliser les composants d’enregistrement automatique plutôt que l’enregistrement manuel.
Les paramètres de requête, de chemin, d'en-tête et de cookie peuvent être enregistrés de la même manière :
// 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 ,
} ,
} ,
} ) ;
Les en-têtes de réponse peuvent être enregistrés de la même manière :
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 ,
} ,
} ,
} ) ;
Des réponses entières peuvent également être enregistrées
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 ,
} ,
} ,
} ) ;
Les rappels peuvent également être enregistrés
const callback : ZodOpenApiCallbackObject = {
ref : 'some-callback'
post : {
responses : {
200 : {
description : '200 OK' ,
content : {
'application/json' : {
schema : z . object ( { a : z . string ( ) } ) ,
}