æ¬èšäºã¯ã Advent Calendar 2025 ãã®12æ¥ç®ã®èšäºã§ãã çµè« ããã³ããšã³ãã§ã¯ãOrvalã§zodã§ã¯ãªãFetch Clientãçæããããšãæ€èšããŠã¿ãŠã¯ãããã§ããããïŒ Orvalãšã¯ Orval ãšã¯ãOpenAPIããTypeScriptã®ã³ãŒããçæã§ããããŒã«ã§ãã äŸãã°ã以äžã®ãããªã³ãŒããçæã§ããŸãã Fetch Client React Query Zod Hono ãã®ããã«ãOrvalã¯æ§ã
ãªçš®é¡ã®ã³ãŒããçæã§ãããšãŠã䟿å©ãªããŒã«ã§ãã ããããäœ¿ãæ¹ã«ãã£ãŠã¯ãéã«ä¿å®æ§ã®äœäžãåŒãèµ·ããå¯èœæ§ããããŸãã ç¹ã«ããããã³ããšã³ãã§zodãçæãããŠãããããšã課é¡ãšãªãã±ãŒã¹ããããŸãã ããã§ãæ¬èšäºã§ã¯ããã³ããšã³ãã§Orvalã«zodãçæãããããšã«ã€ããŠãããã€ãã®èгç¹ããèå¯ããŠãããŸãã Fetch Clientãšããéžæè¢ ããã³ããšã³ãã«ãããŠããªãOrvalã䜿çšããã®ã§ããããïŒ ããã¯ãAPIã®ã¬ã¹ãã³ã¹ã«åãæ¬²ããããã§ãã zodãçæãããããšã§åãåŸãããšãã§ããŸãããFetch Clientãçæãããããšã§ãåæ§ã«å®çŸã§ããããç°¡æœã«ã§ããå¯èœæ§ããããŸãã zodãšFetch Clientã®æ¯èŒ ã§ã¯ãzodãçæãããå ŽåãšFetch Clientãçæãããå Žåã®ã³ãŒããæ¯èŒããŠã¿ãŸãããã OpenAPI openapi: 3.0.0servers: - url: 'http://petstore.swagger.io/v2'info: description: >- This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. version: 1.0.0 title: OpenAPI Petstore license: name: Apache-2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0.html'tags: - name: pet description: Everything about your Pets - name: store description: Access to Petstore orders - name: user description: Operations about userpaths: /pet: post: tags: - pet summary: Add a new pet to the store description: '' operationId: addPet responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '405': description: Invalid input security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' put: tags: - pet summary: Update an existing pet description: '' operationId: updatePet externalDocs: url: "http://petstore.swagger.io/v2/doc/updatePet" description: "API documentation for the updatePet operation" responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found '405': description: Validation exception security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true style: form explode: false deprecated: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: '200': description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' '400': description: Invalid status value security: - petstore_auth: - 'read:pets' /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: >- Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true style: form explode: false schema: type: array items: type: string responses: '200': description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' '400': description: Invalid tag value security: - petstore_auth: - 'read:pets' deprecated: true '/pet/{petId}': get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: '' operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 responses: '405': description: Invalid input security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string delete: tags: - pet summary: Deletes a pet description: '' operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: '400': description: Invalid pet value security: - petstore_auth: - 'write:pets' - 'read:pets' '/pet/{petId}/uploadImage': post: tags: - pet summary: uploads an image description: '' operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: '200': description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] /store/order: post: tags: - store summary: Place an order for a pet description: '' operationId: placeOrder responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid Order requestBody: content: application/json: schema: $ref: '#/components/schemas/Order' description: order placed for purchasing the pet required: true '/store/order/{orderId}': get: tags: - store summary: Find purchase order by ID description: >- For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 5 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid ID supplied '404': description: Order not found delete: tags: - store summary: Delete purchase order by ID description: >- For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: string responses: '400': description: Invalid ID supplied '404': description: Order not found /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser responses: default: description: successful operation security: - api_key: [] requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Created user object required: true /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: '' operationId: createUsersWithArrayInput responses: default: description: successful operation security: - api_key: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: '' operationId: createUsersWithListInput responses: default: description: successful operation security: - api_key: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/login: get: tags: - user summary: Logs user into the system description: '' operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' - name: password in: query description: The password for login in clear text required: true schema: type: string responses: '200': description: successful operation headers: Set-Cookie: description: >- Cookie authentication key for use with the `api_key` apiKey authentication. schema: type: string example: AUTH_KEY=abcde12345; Path=/; HttpOnly X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time content: application/xml: schema: type: string application/json: schema: type: string '400': description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: '' operationId: logoutUser responses: default: description: successful operation security: - api_key: [] '/user/{username}': get: tags: - user summary: Get user by user name description: '' operationId: getUserByName parameters: - name: username in: path description: The name that needs to be fetched. Use user1 for testing. required: true schema: type: string responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' '400': description: Invalid username supplied '404': description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be deleted required: true schema: type: string responses: '400': description: Invalid user supplied '404': description: User not found security: - api_key: [] requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Updated user object required: true delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: '400': description: Invalid username supplied '404': description: User not found security: - api_key: []externalDocs: description: Find out more about Swagger url: 'http://swagger.io'components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: '#/components/schemas/User' description: List of user object required: true Pet: content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' description: Pet object that needs to be added to the store required: true securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' scopes: 'write:pets': modify pets in your account 'read:pets': read your pets api_key: type: apiKey name: api_key in: header schemas: Order: title: Pet Order description: An order for a pets from the pet store type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean default: false xml: name: Order Category: title: Pet category description: A category for a pet type: object properties: id: type: integer format: int64 name: type: string pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' xml: name: Category User: title: a User description: A User who is purchasing from the pet store type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User Tag: title: Pet Tag description: A tag for a pet type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag Pet: title: a Pet description: A pet for sale in the pet store type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: '#/components/schemas/Category' name: type: string example: doggie photoUrls: type: array xml: name: photoUrl wrapped: true items: type: string tags: type: array xml: name: tag wrapped: true items: $ref: '#/components/schemas/Tag' status: type: string description: pet status in the store deprecated: true enum: - available - pending - sold xml: name: Pet ApiResponse: title: An uploaded response description: Describes the result of uploading an image resource type: object properties: code: type: integer format: int32 type: type: string message: type: string openapi : 3.0.0 servers : - url : ' http://petstore.swagger.io/v2 ' info : description : >- This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. version : 1.0.0 title : OpenAPI Petstore license : name : Apache-2.0 url : ' https://www.apache.org/licenses/LICENSE-2.0.html ' tags : - name : pet description : Everything about your Pets - name : store description : Access to Petstore orders - name : user description : Operations about user paths : /pet : post : tags : - pet summary : Add a new pet to the store description : '' operationId : addPet responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 405 ' : description : Invalid input security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : $ref : ' #/components/requestBodies/Pet ' put : tags : - pet summary : Update an existing pet description : '' operationId : updatePet externalDocs : url : " http://petstore.swagger.io/v2/doc/updatePet " description : " API documentation for the updatePet operation " responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Pet not found ' 405 ' : description : Validation exception security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : $ref : ' #/components/requestBodies/Pet ' /pet/findByStatus : get : tags : - pet summary : Finds Pets by status description : Multiple status values can be provided with comma separated strings operationId : findPetsByStatus parameters : - name : status in : query description : Status values that need to be considered for filter required : true style : form explode : false deprecated : true schema : type : array items : type : string enum : - available - pending - sold default : available responses : ' 200 ' : description : successful operation content : application/xml : schema : type : array items : $ref : ' #/components/schemas/Pet ' application/json : schema : type : array items : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid status value security : - petstore_auth : - ' read:pets ' /pet/findByTags : get : tags : - pet summary : Finds Pets by tags description : >- Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId : findPetsByTags parameters : - name : tags in : query description : Tags to filter by required : true style : form explode : false schema : type : array items : type : string responses : ' 200 ' : description : successful operation content : application/xml : schema : type : array items : $ref : ' #/components/schemas/Pet ' application/json : schema : type : array items : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid tag value security : - petstore_auth : - ' read:pets ' deprecated : true ' /pet/{petId} ' : get : tags : - pet summary : Find pet by ID description : Returns a single pet operationId : getPetById parameters : - name : petId in : path description : ID of pet to return required : true schema : type : integer format : int64 responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Pet not found security : - api_key : [] post : tags : - pet summary : Updates a pet in the store with form data description : '' operationId : updatePetWithForm parameters : - name : petId in : path description : ID of pet that needs to be updated required : true schema : type : integer format : int64 responses : ' 405 ' : description : Invalid input security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : content : application/x-www-form-urlencoded : schema : type : object properties : name : description : Updated name of the pet type : string status : description : Updated status of the pet type : string delete : tags : - pet summary : Deletes a pet description : '' operationId : deletePet parameters : - name : api_key in : header required : false schema : type : string - name : petId in : path description : Pet id to delete required : true schema : type : integer format : int64 responses : ' 400 ' : description : Invalid pet value security : - petstore_auth : - ' write:pets ' - ' read:pets ' ' /pet/{petId}/uploadImage ' : post : tags : - pet summary : uploads an image description : '' operationId : uploadFile parameters : - name : petId in : path description : ID of pet to update required : true schema : type : integer format : int64 responses : ' 200 ' : description : successful operation content : application/json : schema : $ref : ' #/components/schemas/ApiResponse ' security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : content : multipart/form-data : schema : type : object properties : additionalMetadata : description : Additional data to pass to server type : string file : description : file to upload type : string format : binary /store/inventory : get : tags : - store summary : Returns pet inventories by status description : Returns a map of status codes to quantities operationId : getInventory responses : ' 200 ' : description : successful operation content : application/json : schema : type : object additionalProperties : type : integer format : int32 security : - api_key : [] /store/order : post : tags : - store summary : Place an order for a pet description : '' operationId : placeOrder responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Order ' application/json : schema : $ref : ' #/components/schemas/Order ' ' 400 ' : description : Invalid Order requestBody : content : application/json : schema : $ref : ' #/components/schemas/Order ' description : order placed for purchasing the pet required : true ' /store/order/{orderId} ' : get : tags : - store summary : Find purchase order by ID description : >- For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions operationId : getOrderById parameters : - name : orderId in : path description : ID of pet that needs to be fetched required : true schema : type : integer format : int64 minimum : 1 maximum : 5 responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Order ' application/json : schema : $ref : ' #/components/schemas/Order ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Order not found delete : tags : - store summary : Delete purchase order by ID description : >- For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId : deleteOrder parameters : - name : orderId in : path description : ID of the order that needs to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Order not found /user : post : tags : - user summary : Create user description : This can only be done by the logged in user. operationId : createUser responses : default : description : successful operation security : - api_key : [] requestBody : content : application/json : schema : $ref : ' #/components/schemas/User ' description : Created user object required : true /user/createWithArray : post : tags : - user summary : Creates list of users with given input array description : '' operationId : createUsersWithArrayInput responses : default : description : successful operation security : - api_key : [] requestBody : $ref : ' #/components/requestBodies/UserArray ' /user/createWithList : post : tags : - user summary : Creates list of users with given input array description : '' operationId : createUsersWithListInput responses : default : description : successful operation security : - api_key : [] requestBody : $ref : ' #/components/requestBodies/UserArray ' /user/login : get : tags : - user summary : Logs user into the system description : '' operationId : loginUser parameters : - name : username in : query description : The user name for login required : true schema : type : string pattern : ' ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ ' - name : password in : query description : The password for login in clear text required : true schema : type : string responses : ' 200 ' : description : successful operation headers : Set-Cookie : description : >- Cookie authentication key for use with the `api_key` apiKey authentication. schema : type : string example : AUTH_KEY=abcde12345; Path=/; HttpOnly X-Rate-Limit : description : calls per hour allowed by the user schema : type : integer format : int32 X-Expires-After : description : date in UTC when token expires schema : type : string format : date-time content : application/xml : schema : type : string application/json : schema : type : string ' 400 ' : description : Invalid username/password supplied /user/logout : get : tags : - user summary : Logs out current logged in user session description : '' operationId : logoutUser responses : default : description : successful operation security : - api_key : [] ' /user/{username} ' : get : tags : - user summary : Get user by user name description : '' operationId : getUserByName parameters : - name : username in : path description : The name that needs to be fetched. Use user1 for testing. required : true schema : type : string responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/User ' application/json : schema : $ref : ' #/components/schemas/User ' ' 400 ' : description : Invalid username supplied ' 404 ' : description : User not found put : tags : - user summary : Updated user description : This can only be done by the logged in user. operationId : updateUser parameters : - name : username in : path description : name that need to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid user supplied ' 404 ' : description : User not found security : - api_key : [] requestBody : content : application/json : schema : $ref : ' #/components/schemas/User ' description : Updated user object required : true delete : tags : - user summary : Delete user description : This can only be done by the logged in user. operationId : deleteUser parameters : - name : username in : path description : The name that needs to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid username supplied ' 404 ' : description : User not found security : - api_key : [] externalDocs : description : Find out more about Swagger url : ' http://swagger.io ' components : requestBodies : UserArray : content : application/json : schema : type : array items : $ref : ' #/components/schemas/User ' description : List of user object required : true Pet : content : application/json : schema : $ref : ' #/components/schemas/Pet ' application/xml : schema : $ref : ' #/components/schemas/Pet ' description : Pet object that needs to be added to the store required : true securitySchemes : petstore_auth : type : oauth2 flows : implicit : authorizationUrl : ' http://petstore.swagger.io/api/oauth/dialog ' scopes : ' write:pets ' : modify pets in your account ' read:pets ' : read your pets api_key : type : apiKey name : api_key in : header schemas : Order : title : Pet Order description : An order for a pets from the pet store type : object properties : id : type : integer format : int64 petId : type : integer format : int64 quantity : type : integer format : int32 shipDate : type : string format : date-time status : type : string description : Order Status enum : - placed - approved - delivered complete : type : boolean default : false xml : name : Order Category : title : Pet category description : A category for a pet type : object properties : id : type : integer format : int64 name : type : string pattern : ' ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ ' xml : name : Category User : title : a User description : A User who is purchasing from the pet store type : object properties : id : type : integer format : int64 username : type : string firstName : type : string lastName : type : string email : type : string password : type : string phone : type : string userStatus : type : integer format : int32 description : User Status xml : name : User Tag : title : Pet Tag description : A tag for a pet type : object properties : id : type : integer format : int64 name : type : string xml : name : Tag Pet : title : a Pet description : A pet for sale in the pet store type : object required : - name - photoUrls properties : id : type : integer format : int64 category : $ref : ' #/components/schemas/Category ' name : type : string example : doggie photoUrls : type : array xml : name : photoUrl wrapped : true items : type : string tags : type : array xml : name : tag wrapped : true items : $ref : ' #/components/schemas/Tag ' status : type : string description : pet status in the store deprecated : true enum : - available - pending - sold xml : name : Pet ApiResponse : title : An uploaded response description : Describes the result of uploading an image resource type : object properties : code : type : integer format : int32 type : type : string message : type : string orval.config.ts /** * @summary Add a new pet to the store */export const addPetBodyCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetBody = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetBodyCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store');export const addPetResponseCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetResponse = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetResponseCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store'); /** * @ summary Add a new pet to the store */ export const addPetBodyCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetBody = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetBodyCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; export const addPetResponseCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetResponse = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetResponseCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; zodã®å Žå: /** * @summary Add a new pet to the store */export const addPetBodyCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetBody = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetBodyCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store');export const addPetResponseCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetResponse = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetResponseCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store'); /** * @ summary Add a new pet to the store */ export const addPetBodyCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetBody = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetBodyCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; export const addPetResponseCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetResponse = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetResponseCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; Fetch Clientã®å Žå: /** * @summary Add a new pet to the store */export type addPetResponse200 = { data: Pet; status: 200;};export type addPetResponse405 = { data: null; status: 405;};export type addPetResponseComposite = addPetResponse200 | addPetResponse405;export type addPetResponse = addPetResponseComposite & { headers: Headers;};export const getAddPetUrl = () => { return `/pet`;};export const addPet = async ( petBody: PetBody, options?: RequestInit,): Promise<addPetResponse> => { const res = await fetch(getAddPetUrl(), { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, body: JSON.stringify(petBody), }); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); const data: addPetResponse['data'] = body ? JSON.parse(body) : {}; return { data, status: res.status, headers: res.headers } as addPetResponse;}; /** * @ summary Add a new pet to the store */ export type addPetResponse200 = { data : Pet ; status : 200 ; } ; export type addPetResponse405 = { data : null ; status : 405 ; } ; export type addPetResponseComposite = addPetResponse200 | addPetResponse405 ; export type addPetResponse = addPetResponseComposite & { headers : Headers ; } ; export const getAddPetUrl = () => { return ` /pet ` ; } ; export const addPet = async ( petBody : PetBody , options ?: RequestInit , ) : Promise < addPetResponse > => { const res = await fetch ( getAddPetUrl () , { ... options , method : ' POST ' , headers : { ' Content-Type ' : ' application/json ' , ... options ?. headers }, body : JSON . stringify ( petBody ) , } ) ; const body = [ 204 , 205 , 304 ] . includes ( res . status ) ? null : await res . text () ; const data : addPetResponse [ ' data ' ] = body ? JSON . parse ( body ) : {} ; return { data , status : res . status , headers : res . headers } as addPetResponse ; } ; zodãçæãããå Žåãçæãããã®ã¯ãããŸã§ãzodã®ã¹ããŒããªã®ã§ãå®éã«fetchããåŠçã¯èªåã§æžãå¿
èŠããããŸãã ããã«å¯ŸããŠãFetch Clientãçæãããå Žåã¯fetchããåŠçãŸã§çæããŠãããã®ã§ããã€ã©ãŒãã¬ãŒããæžããããšãã§ããŸãã åå®å
šæ§ã«ã€ããŠã®èå¯ Fetch Clientã§çæãããã³ãŒããèŠãŠããããšããããŸããã as âã䜿çšããŠåã®ã¢ãµãŒã·ã§ã³ãè¡ã£ãŠããŸãã ããã§ããFetch Clientã§çæããã³ãŒãã¯å³å¯ã«ã¯åå®å
šã§ã¯ãããŸããã ããããããã§èããŠã¿ããããšããããŸãã åå®å
šã§ã¯ãªãããšã«ãã£ãŠãåé¡ãçºçããã®ã¯ã©ã®ãããªã±ãŒã¹ã§ããããïŒ ããã¯ãOpenAPIã®ã¹ããŒããšå®éã«ããã¯ãšã³ãããè¿ã£ãŠããã¹ããŒããç°ãªãã±ãŒã¹ã§ãã ãããŠãããã¯æãããŠããã³ããšã³ãã®ããããã©ã³ã¿ã€ã äžã§æ€ç¥ãã¹ãããšãªã®ã§ããããïŒ ãããèžãŸãããšãOpenAPIãšããã¯ãšã³ãã®éœéœ¬ãããã³ããšã³ãã®ã©ã³ã¿ã€ã äžã§å€æããã®ã¯çæ³çãªã¿ã€ãã³ã°ãšã¯èšããªããããããŸããã ãããã£ãŠããã®åé¡ã¯ããã¯ãšã³ãã®è²¬åãšèããããã¯ãšã³ãåŽã®ãã¹ãã§å¯ŸåŠããæ¹ãé©åã ãšèããããŸãã ããã¯ãšã³ãã¯ããã³ããšã³ããä¿¡é ŒããŠã¯ãããŸããããããã³ããšã³ãã¯ããã¯ãšã³ããä¿¡é Œãããšããèãæ¹ãã§ããŸãã ã©ã³ã¿ã€ã æ€èšŒã«ã€ããŠã®èãæ¹ å®éåé¡ãã©ã³ã¿ã€ã ãšã©ãŒãçºçãããã©ãããã®ãïŒããšããæžå¿µãããããšæããŸãã ãã®å ŽåãçŽ çŽã«ãšã©ãŒãthrowãããšããéžæè¢ããããŸãã OpenAPIãšããã¯ãšã³ãã«éœéœ¬ããããšããèŽåœçãªåé¡ãçºçããŠããå Žåãããã³ããšã³ãåŽã§ã§ããããšã¯éãããŠããŸãã ãŸããNext.jsãªã error.tsx âãé
眮ããŠããããšã§ããšã©ãŒç»é¢ã衚瀺ããããšãã§ããŸãã catchããåŸã©ãããã®ãïŒ ã念é ã«ãããŠãšã©ãŒãã³ããªã³ã°ãèšèšããŸãããã ã¯ãšãªãã©ã¡ãŒã¿ããªã¯ãšã¹ãããã£ã®ããªããŒã·ã§ã³ã«ã€ã㊠ããã³ããšã³ãããããã¯ãšã³ãã®APIã«ãªã¯ãšã¹ããéãåã«ãã¯ãšãªãã©ã¡ãŒã¿ããªã¯ãšã¹ãããã£ã®ããªããŒã·ã§ã³ãè¡ããããšãããŠãŒã¹ã±ãŒã¹ããããšæããŸãã ããã§ã¯ãäžã€ã®èãæ¹ãšããŠããã®æ®µéã§ã®ããªããŒã·ã§ã³ã®å¿
èŠæ§ã«ã€ããŠæ€èšããŠã¿ãŸãã å®éã¯ããã®çŽåŸã«ããã¯ãšã³ããããªããŒã·ã§ã³ãè¡ããŸãã ãããã£ãŠãããã³ããšã³ãåŽã§ã®éè€ããããªããŒã·ã§ã³ã¯çç¥ã§ããå Žåãå€ãã§ãã ãŸããããã³ããšã³ãã§è¡ãããªããŒã·ã§ã³ã¯UXã®ããã§ãããšããèŠç¹ãæã€ããšãéèŠã§ãã ã€ãŸããã»ãã¥ãªãã£ãäžæ£ãªå€ãé²ãããã®ããªããŒã·ã§ã³ã¯ããã¯ãšã³ãã§è¡ããããã³ããšã³ããšããã¯ãšã³ãã§äºåºŠåãããªããŒã·ã§ã³ãè¡ãå¿
èŠæ§ã¯äœããšèããããšãã§ããŸãã ãã©ãŒã ã®ããªããŒã·ã§ã³ã«ã€ã㊠UXã®ããã®ãã©ãŒã ã®ããªããŒã·ã§ã³ã§ãzodã®ã¹ããŒããæ¬²ãããªãã±ãŒã¹ããããããããŸããã ãã®å Žåã¯ããã©ãŒã ã®ã¹ããŒããAPIã®ã¹ããŒããšã¯å¥ã«å®çŸ©ããããšãããããããŸãã ãªããªãããã©ãŒã ã®ã¹ããŒãã¯APIã®ã¹ããŒããšå¿
ã察å¿ããŠãããšã¯éããªãããã§ãã äŸãã°ãéµäŸ¿çªå·ãå
¥åãããšããAPIåŽã§ã¯åè§æ°åã®æååãæåŸ
ããŸããããã©ãŒã åŽã§ã¯UXã®ããã«å
šè§æ°åã®æååãåãåããããã«ãããå ŽåããããŸãã ãŸããæ°å€å
¥åã§ããAPIåŽã§ã¯æ°å€åãæåŸ
ããŸããããã©ãŒã åŽã§ã¯äžæçã«æååãšããŠæ±ããã«ã³ãåºåãã®è¡šç€ºã«å¯Ÿå¿ãããå ŽåããããŸãã ãã以å€ã§ããAPIåŽã¯å®æåœ¢ã®ãªããžã§ã¯ããæåŸ
ããŸããããã©ãŒã ã§ã¯æ®µéçã«ç°ãªã圢ç¶ã®ããŒã¿ãæ±ããããªå ŽåããããŸãã ãã®ãããªå ŽåãAPIåŽã®ã¹ããŒãã¯æµçšã§ããŸãããããã³ããšã³ãåŽã§ç¬èªã«å®çŸ©ããå¿
èŠããããŸãã ãããªããšãAPIã®ã¹ããŒããæµçšãããã®ãšãããªããã®ãæ··åšããããšã«ãªããŸãã ãããŠãçŸç¶æµçšã§ããŠããã¹ããŒãããåŸãã倿Žãããå¯èœæ§ããããŸãã ã€ãŸããAPIã®ã¹ããŒããšãã©ãŒã ã®ã¹ããŒãã¯æ¬è³ªçã«ç°ãªããã®ã§ãããšèããããšãã§ããŸãã ãããã£ãŠããã©ãŒã ã®ã¹ããŒãã¯ä»®ã«APIã®ã¹ããŒããšäžèŽããŠããŠããå¥ã§å®çŸ©ããããšãæ€èšããŠã¿ãŠã¯ãããã§ããããã zodãçæããæ¹ãé©ããŠããã±ãŒã¹ ãããŸã§ãzodãçæããªãéžæè¢ã«ã€ããŠèå¯ããŠããŸããããzodãçæããæ¹ãé©ããŠããã±ãŒã¹ããããŸãã å€éšãµãŒãã¹ã®APIã䜿çšããå Žå èªåãã¡ã管çããŠããªãå€éšAPIã䜿çšããå Žåã¯ãzodã«ããã©ã³ã¿ã€ã æ€èšŒãæå¹ãªéžæè¢ãšãªããŸãã ãªããªããOpenAPIã¹ããŒããšããã¯ãšã³ãã®å®è£
ã«éœéœ¬ããã£ãŠããããã¯ãšã³ãåŽã§ä¿®æ£ããããšãã§ããªãããã§ãã ãŸããå€éšAPIã¯äºåãªã仿§ã倿ŽãããããšããããŸãã ãã®ãããªå Žåãããã³ããšã³ãåŽã§é²åŸ¡çã«ã©ã³ã¿ã€ã æ€èšŒããäžæ£ãªããŒã¿ãæ©æã«æ€åºããããšã§ãäºæããªããšã©ãŒãé²ãããšãã§ããŸãã ããã¯ãšã³ãã§TypeScriptã䜿çšããŠããå Žå ããã¯ãšã³ãã§TypeScriptã䜿çšããŠããå ŽåãOrvalã«ããzodçæã掻çšããããšã§ãããã¯ãšã³ãåŽã§ããªããŒã·ã§ã³ãè¡ãããšãã§ããŸãã ãã ããããã¯ããã³ããšã³ãã®è©±ã§ã¯ãªããããã¯ãšã³ãã®è©±ã§ãã (ãããŸã§ãããã³ããšã³ãã«ãããããšåŒ·èª¿ããŠããã®ã¯ãã®ããã§ã) ããã¯ãšã³ãã§ã¯ãããã³ããšã³ãããéãããŠãããªã¯ãšã¹ãããã£ãã¯ãšãªãã©ã¡ãŒã¿ãä¿¡é Œãã¹ãã§ã¯ãããŸããã ãããã£ãŠãããã¯ãšã³ãåŽã§ã©ã³ã¿ã€ã ããªããŒã·ã§ã³ãè¡ãå¿
èŠããããŸãã ãã®ãšããOpenAPIããzodã¹ããŒããçæããããšã§ãããªããŒã·ã§ã³ã®ã³ãŒããèªåçæã§ããä¿å®æ§ãåäžãããããšãã§ããŸãã è£è¶³: APIã®åã®äœ¿ãæ¹ã«ã€ã㊠zodã®è©±ãšã¯å°ãéžããŸãããOrvalã䜿ãéã«æ³šæãããç¹ãšããŠãAPIã®åãã³ã³ããŒãã³ãããAPIã¯ã©ã€ã¢ã³ããŸã§äœ¿ãåããšããã¢ã³ããã¿ãŒã³ããããŸãã åæãšããŠãAPIã®ã¬ã¹ãã³ã¹ã¯JSONã§ãããããã¯ã·ãªã¢ã©ã€ãºãããDTOã«éããŸããã JSONã¯ãã¡ã€ã³ç¥èãæããããã¡ã€ã³ã¢ãã«ãšããŠæ©èœããªããããOrvalãçæããåãããã³ããšã³ãåŽã§ãããããã¡ã€ã³ã¢ãã«ã§ãããã®ããã«çŽæ¥äŸåãããšãæ§ã
ãªç®æã§äžæŽåãçºçããå¯èœæ§ããããŸãã ãã®ãããããã³ããšã³ãã§ã¯ããã³ããšã³ãçšã®ãã¡ã€ã³ã¢ãã«ãå®çŸ©ããããšãããããããŸãã ãããŠãDTOããã¡ã€ã³ã¢ãã«ã«å€æããMapperãå®è£
ããããšããäžã€ã®æå¹ãªã¢ãããŒãã§ãã ãã®èŸºãã®è©±ã¯ä»¥äžã®èšäºãåèã«ãªãããããããããã°èªãã§ã¿ãŠãã ããã ããã³ããšã³ããšã³ãžãã¢ããèªåã¯JSONè²ä»ãä¿ããšèªèããçç±ãèå¯ãã ããã³ããšã³ãã«ããããåãã®è²¬ä»»åè§£ã«å¯Ÿãã1ã€ã®ã¢ãããŒã ãŸãšã æ¬èšäºã§ã¯ãããã³ããšã³ãã§Orvalã䜿çšããéã«ãzodã§ã¯ãªãFetch Clientãçæãããšããéžæè¢ã«ã€ããŠèå¯ããŸããã éèŠãªãã€ã³ãã¯ä»¥äžã®4ã€ã§ãã Fetch Clientãçæããããšã§ãzodãããç°¡æœã«åãã€ããAPIéä¿¡ã®åŠçãæžãã ã©ã³ã¿ã€ã äžã§ã®OpenAPIãšããã¯ãšã³ãã®éœéœ¬æ€åºã¯ãããã¯ãšã³ãã®ãã¹ãã§å¯ŸåŠããèãæ¹ããã ãã©ãŒã ã®ã¹ããŒããšAPIã®ã¹ããŒãã¯æ¬è³ªçã«ç°ãªãç®çãæã€ APIã®ã¬ã¹ãã³ã¹ã¯ã·ãªã¢ã©ã€ãºãããDTOã§ããããã¡ã€ã³ã¢ãã«ãšã¯åºå¥ããŠæ±ãããšãæãŸãã ãããã¯äžã€ã®èãæ¹ã§ããããããžã§ã¯ãã®ç¶æ³ãããŒã ã®æ¹éã«ãã£ãŠæé©ãªéžæã¯ç°ãªããŸãã ããããã®ã¢ãããŒãã®ãã¬ãŒããªããçè§£ããäžã§ããããžã§ã¯ãã«é©ããæ¹æ³ãéžæããããšãããããããŸãã