Zodios
repository·main·Indexed 24 days ago
https://github.com/ecyrbe/zodiosA TypeScript-first API client and optional server framework that leverages Axios for HTTP requests and Zod for schema validation. It provides end-to-end type safety, autocompletion for URLs and parameters, and automatic response validation. The ecosystem includes specialized packages such as @zodios/express for REST APIs, @zodios/react and @zodios/solid for TanStack Query wrappers, and openapi-zod-client for generating clients from OpenAPI specifications.
What's inside Zodios
- Zodios is a REST API toolbox designed for end-to-end typesafety. It provides a clean, intuitive, and declarative syntax for creating REST APIs. While optimized for TypeScript to provide full autocompletion and typesafety, it is also compatible with pure JavaScript.
Explore the Zodios community ecosystem
mainBeyond the official Zodios packages, the community provides tools to extend your workflow, such as generating clients from OpenAPI specifications or using shorthand syntax for API definitions.
Package Description openapi-zod-client generate a zodios client from an openapi specification api-definition-shorthand shorthand for zodios api definitions Explore the Zodios Ecosystem
mainZodios provides several specialized packages to extend its functionality:
openapi-zod-client: Generate a Zodios client from an OpenAPI specification.@zodios/express: Provides full end-to-end type safety for REST APIs (similar to tRPC).@zodios/plugins: A collection of plugins for Zodios.@zodios/react: A@tanstack/react-querywrapper for Zodios.@zodios/solid: A@tanstack/solid-querywrapper for Zodios.
What is Zodios Context?
mainZodios Context allows you to declare a typed context object that is available in all your Zodios handlers. This is useful for accessing data that is typically attached to the request object in Express apps (such asreq.user) while ensuring that the data is properly typed throughout your handlers using Zod schemas.Understand plugin execution order
mainThe execution order of Zodios plugins follows these rules:
- Global plugins (not attached to an endpoint) execute first.
- Endpoint-specific plugins execute next.
- Request Interceptors: Executed in the order they were declared.
- Response Interceptors: Executed in reverse order of their declaration.
Example flow for a request and response:
- Request: Global Plugin $\rightarrow$ Endpoint Plugin $\rightarrow$ Specific Plugin
- Response: Specific Plugin $\rightarrow$ Endpoint Plugin $\rightarrow$ Global Plugin
apiClient.use("getUser", pluginLog('2')); apiClient.use(pluginLog('1')); apiClient.use("get", "/users/:id", pluginLog('3')); apiClient.get("/users/:id", { params: { id: 7 } }); // output: // request 1 // request 2 // request 3 // response 3 // response 2 // response 1Use Alias Hooks for Endpoints
mainIf you define an
aliasin your API definition, Zodios generates specialized hooks for those endpoints. This provides full auto-completion and automatic key management.Query Aliases
Used for
GETrequests. Returns aQueryResultcontaining the response data, all standardreact-queryproperties, the generatedkey, and aninvalidatehelper.// Example: identical to hooks.useQuery("/users") const { data: users, isLoading, isError, invalidate, key } = hooks.useGetUsers();Immutable Query Aliases
Used for
POSTrequests that act as queries. These are only available if you setimmutable: truein your API definition.// Example: identical to hooks.useImmutableQuery("/users/search") const { data: users, isLoading, isError } = hooks.useSearchUsers({ name: "John" });Mutation Aliases
Used for
POST,PUT,PATCH, orDELETEendpoints.// Example: identical to usePost("/users") const { mutate } = hooks.useCreateUser();Zodios Package Ecosystem
mainZodios is modular. You can use frontend and backend packages independently by sharing the API definition between teams. The ecosystem includes:
- @zodios/core: The core library containing the typesafe API client. Can be used standalone.
- @zodios/plugins: A collection of plugins for the API client.
- @zodios/react: React hooks for the client, built on top of
tanstack-query. - @zodios/solid: Solid hooks for the client, built on top of
tanstack-query. - @zodios/express: A typesafe adapter for Express.
- @zodios/openapi: Helpers to generate OpenAPI specs and Swagger UI from Zodios API definitions.
Use endpoint aliases for type-safe requests
mainIf you define an
aliasin your API definition, you can call that alias directly on the Zodios instance instead of using generic HTTP methods. This provides the best developer experience and type safety.Query Aliases (GET, etc.)
Used for endpoints that do not require a body.
function [alias](config?: ZodiosRequestOptions): Promise<Response>;- Use
paramsto pass path parameters. - Use
queriesto pass query parameters.
Mutation Aliases (POST, PUT, PATCH, DELETE)
Used for endpoints that require a body.
function [alias](body: BodyParam, config?: ZodiosRequestOptions): Promise<Response>;- Use
Define a Zodios API definition
mainA Zodios API definition is a centralized JavaScript array of endpoint descriptions used to declare your REST API endpoints. This object is designed to be shared between your server and client code to ensure type safety and consistency. If you do not control the API server, you can still use an API definition solely for your client-side Zodios instance.
Each endpoint in the array is an object describing the HTTP method, path, and response schema.
Use Zodios with Solid.js and TanStack Query
mainZodios provides a
ZodiosHooksclass that integrates your Zodios API client with@tanstack/solid-query. This allows you to use TanStack Query's powerful data fetching patterns (like infinite queries and mutations) while maintaining full type safety from your Zod schema definitions.To use it:
- Define your schemas using
zod. - Define your API using
makeApi. - Instantiate
Zodioswith your base URL and API definition. - Instantiate
ZodiosHookspassing a key and yourZodiosinstance. - Wrap your application in a
QueryClientProviderfrom@tanstack/solid-query. - Use the methods on the
ZodiosHooksinstance (e.g.,createInfiniteQuery,createMutation, or alias-based methods likecreateCreateUser) within your Solid components.
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"; import { makeApi, Zodios } from "@zodios/core"; import { ZodiosHooks } from "../src"; import { z } from "zod"; // 1. Define Schemas const userSchema = z.object({ id: z.number(), name: z.string() }).required(); const usersSchema = z.array(userSchema); // 2. Define API const api = makeApi([ { method: "get", path: "/users", alias: "getUsers", response: usersSchema, }, { method: "post", path: "/users", alias: "createUser", parameters: [{ name: "body", type: "Body", schema: z.object({ name: z.string() }).required() }], response: userSchema, }, ]); // 3. Setup Zodios and Hooks const zodios = new Zodios("https://api.example.com", api); const zodiosHooks = new ZodiosHooks("api-key", zodios); // 4. Use in Component const Users = () => { const users = zodiosHooks.createGetUsers({ params: { limit: 10 } }); const userMutation = zodiosHooks.createCreateUser(undefined, { onSuccess: () => users.invalidate(), }); return ( <button onClick={() => userMutation.mutate({ name: "john" })}>Create</button> ); }; // 5. Provide QueryClient const queryClient = new QueryClient(); export const App = () => ( <QueryClientProvider client={queryClient}> <Users /> </QueryClientProvider> );- Define your schemas using
Install Zodios with NextJS
mainFor NextJS projects, install
@zodios/expressalong with the NextJS and React dependencies.npm install @zodios/core @zodios/express next zod axios react react-domSend multipart/form-data requests
mainZodios supports
multipart/form-datausing therequestFormat: "form-data"option.Node.js Users: You must install the
form-datapackage and polyfillglobalThis.FormDatabefore importing Zodios:globalThis.FormData = require("form-data");.Alternatively, you can use your own multipart library (like
form-dataon Node) by defining the parameter schema asz.instanceof(FormData)and passing the headers manually.// Option 1: Using integrated requestFormat const apiClient = new Zodios( "https://mywebsite.com", [{ method: "post", path: "/upload", alias: "upload", description: "Upload a file", requestFormat: "form-data", parameters:[ { name: "body", type: "Body", schema: z.object({ file: z.instanceof(File), }), } ], response: z.object({ id: z.number(), }), }], ); const id = await apiClient.upload({ file: document.querySelector('#file').files[0] });// Option 2: Using custom FormData library (e.g. on Node) import FormData from 'form-data'; const apiClient = new Zodios( "https://mywebsite.com", [{ method: "post", path: "/upload", alias: "upload", description: "Upload a file", parameters:[ { name: "body", type: "Body", schema: z.instanceof(FormData), } ], response: z.object({ id: z.number(), }), }], ); const form = new FormData(); form.append('file', document.querySelector('#file').files[0]); const id = await apiClient.upload(form, { headers: form.getHeaders() });