Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Generate a headless Typescript API client from an OpenAPI spec - optionally with a @tanstack/react-query client using queryOptions

License

NotificationsYou must be signed in to change notification settings

astahmer/typed-openapi

Repository files navigation

Generate a Typescript API client from an OpenAPI spec

Seethe online playground

Screenshot 2023-08-08 at 00 48 42

pkg.pr.new

Features

  • Headless API client,bring your own fetcher (fetch, axios, ky, etc...) ! (You can generate that file with--default-fetcher)
  • Generates a fully typesafe API client with just types by default (instant suggestions)
  • Type-safe error handling: with discriminated unions and configurable success/error status codes
  • withResponse & throwOnStatusError: Get a union-style response object or throw on configured error status codes, with full type inference
  • TanStack Query integration: withwithResponse andselectFn options for advanced success/error handling
  • Or you can also generate a client with runtime validation using one of the following runtimes:

The generated client is a single file that can be used in the browser or in node. Runtime validation schemas areprovided by the excellenttypebox-codegen

Install & usage

pnpm add typed-openapi

It exports a bunch of functions that can be used to build your own tooling on top of it. You can look at theCLI code so see how to use them.

CLI

npx typed-openapi -h
typed-openapi/2.0.0Usage:  $ typed-openapi<input>Commands:<input>  GenerateFor more info, run anycommand with the`--help` flag:  $ typed-openapi --helpOptions:  -o, --output<path>             Output pathfor the api client ts file (defaults to`<input>.<runtime>.ts`)  -r, --runtime<n>               Runtime to usefor validation; defaults to`none`; available: Type<"arktype"|"io-ts"|"none"|"typebox"|"valibot"|"yup"|"zod"> (default: none)  --schemas-only                  Only generate schemas, skipping client generation (defaults to false) (default: false)  --include-client                Include API client types and implementation (defaults to true) (default: true)  --success-status-codes<codes>  Comma-separated list of success status codes (defaults to 2xx and 3xx ranges)  --error-status-codes<codes>    Comma-separated list of error status codes (defaults to 4xx and 5xx ranges)  --tanstack [name]               Generate tanstack client, defaults to false, can optionally specify a name (will be generated next to the main file) or absolute pathfor the generated file  --default-fetcher [name]        Generate default fetcher, defaults to false, can optionally specify a name (will be generated next to the main file) or absolute pathfor the generated file  -h, --help                      Display this message  -v, --version                   Display version number

Non-goals

  • Caring too much about the runtime validation code. If that works (thanks totypebox-codegen), that's great, otherwise I'm not really interestedin fixing it. If you are, feel free to open a PR.

  • Supporting all the OpenAPI spec. Regex, dates, files, whatever, that's not the point here.openapi-zod-client does a great job at that, but it's slow togenerate the client and the suggestions in the IDE are not instant. I'm only interested in supporting the subset ofthe spec that makes the API client typesafe and fast to provide suggetions in the IDE.

  • Splitting the generated client into multiple files. Nope. Been there, done that. Let's keep it simple.

Basically, let's focus on having a fast and typesafe API client generation instead.

Usage Examples

API Client Setup

The generated client is headless - you need to provide your own fetcher. Here are ready-to-use examples:

Type-Safe Error Handling & Response Modes

You can choose between two response styles:

  • Direct data return (default):

    constuser=awaitapi.get("/users/{id}",{path:{id:"123"}});// Throws TypedResponseError on error status (default)
  • Union-style response (withResponse):

    constresult=awaitapi.get("/users/{id}",{path:{id:"123"},withResponse:true});if(result.ok){// result.data is typed as User}else{// result.data is typed as your error schema for that status}

You can also control error throwing withthrowOnStatusError.

All errors thrown by the client are instances ofTypedResponseError and include the parsed error data.

Generic Request Method

For dynamic endpoint calls or when you need more control:

// Type-safe generic request methodconstresponse=awaitapi.request("GET","/users/{id}",{path:{id:"123"},query:{include:["profile","settings"]}});constuser=awaitresponse.json();// Fully typed based on endpoint

TanStack Query Integration

Generate TanStack Query wrappers for your endpoints with:

npx typed-openapi api.yaml --tanstack

You get:

  • Type-safe queries and mutations with full error inference
  • withResponse andselectFn for advanced error and response handling
  • All mutation errors are Response-like and type-safe, matching your OpenAPI error schemas

useQuery / fetchQuery / ensureQueryData

// Basic queryconstaccessiblePagesQuery=useQuery(tanstackApi.get('/authorization/accessible-pages').queryOptions);// Query with query parametersconstmembersQuery=useQuery(tanstackApi.get('/authorization/organizations/:organizationId/members/search',{path:{organizationId:'org123'},query:{searchQuery:'john'}}).queryOptions);// With additional query optionsconstdepartmentCostsQuery=useQuery({  ...tanstackApi.get('/organizations/:organizationId/department-costs',{path:{organizationId:params.orgId},query:{period:selectedPeriod},}).queryOptions,staleTime:30*1000,// placeholderData: keepPreviousData,// etc});

or if you need it in a routerbeforeLoad /loader:

import{tanstackApi}from'#api';awaitqueryClient.fetchQuery(tanstackApi.get('/:organizationId/remediation/accounting-lines/metrics',{path:{organizationId:params.orgId},}).queryOptions,);

useMutation

The mutation API supports both basic usage and advanced error handling withwithResponse and custom transformations withselectFn.Note: All mutation errors are Response-like objects with type-safe error inference based on your OpenAPI error schemas.

// Basic mutation (returns data only)constbasicMutation=useMutation({// Will throws TypedResponseError on error status  ...tanstackApi.mutation("post",'/authorization/organizations/:organizationId/invitations').mutationOptions,onError:(error)=>{// error is a Response-like object with typed data based on OpenAPI specconsole.log(errorinstanceofResponse);// trueconsole.log(error.status);// 400, 401, etc. (properly typed)console.log(error.data);// Typed error response body}});// With error handling using withResponseconstmutationWithErrorHandling=useMutation(tanstackApi.mutation("post",'/users',{// Returns union-style result, never throwswithResponse:true}).mutationOptions);// With custom response transformationconstcustomMutation=useMutation(tanstackApi.mutation("post",'/users',{selectFn:(user)=>({userId:user.id,userName:user.name})}).mutationOptions);// Advanced: withResponse + selectFn for comprehensive error handlingconstadvancedMutation=useMutation(tanstackApi.mutation("post",'/users',{withResponse:true,selectFn:(response)=>({success:response.ok,user:response.ok ?response.data :null,error:response.ok ?null :response.data,statusCode:response.status})}).mutationOptions);

Usage Examples:

// Basic usagebasicMutation.mutate({body:{emailAddress:'user@example.com',department:'engineering',roleName:'admin'}});// With error handling// All errors thrown by mutations are type-safe and Response-like, with parsed error data attached.mutationWithErrorHandling.mutate({body:userData},{onSuccess:(response)=>{if(response.ok){toast.success(`User${response.data.name} created!`);}else{if(response.status===400){toast.error(`Validation error:${response.data.message}`);}elseif(response.status===409){toast.error('User already exists');}}}});// Advanced usage with custom transformationadvancedMutation.mutate({body:userData},{onSuccess:(result)=>{if(result.success){console.log('Created user:',result.user.name);}else{console.error(`Error${result.statusCode}:`,result.error);}}});

useMutation without the tanstack api

If you need to make a custom mutation you could use theapi directly:

const{mutate:login, isPending}=useMutation({mutationFn:async(type:'google'|'microsoft')=>{returnapi.post(`/authentication/${type}`,{body:{redirectUri:search.redirect}});},onSuccess:(data)=>{window.location.replace(data.url);},onError:(error,type)=>{console.error(error);toast({title:t(`toast.login.${type}.error`),icon:'warning',variant:'critical',});},});

Alternatives

openapi-zod-client, which generates azodios client but can be slow to provide IDE suggestions when the OpenAPI spec islarge. Also, you might not always want to use zod or even runtime validation, hence this project.

Contributing

  • pnpm i
  • pnpm build
  • pnpm test

When you're done with your changes, please runpnpm changeset in the root of the repo and follow the instructionsdescribedhere.

About

Generate a headless Typescript API client from an OpenAPI spec - optionally with a @tanstack/react-query client using queryOptions

Topics

Resources

License

Stars

Watchers

Forks

Languages


[8]ページ先頭

©2009-2025 Movatter.jp