DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Write typesafe APIs with zodios
ecyrbe
ecyrbe

Posted on

Write typesafe APIs with zodios

Writing client APIs, that gives you autocompletion is time consuming. You allways endup writing the same kind of boilerplate code or have to use openapi code generators that are a pain to maintain.

Zodios was created to make creating frontend (and optionally backend) API client a breeze.
It's is an open source REST API toolbox with end-to-end typesafety.
It allows you to create a REST API with a clean, intuitive and declarative syntax.
It's best used with TypeScript, but it's also usable with pure JavaScript.

Declaring returned data with zod

You can predefine some schemas to reuse them in your API definition. Zodios will use these definition to make runtime checks. Your API client is then typesafe at runtime.

import { Zodios, asErrors } from "@zodios/core";
import z from "zod";

const errors = asErrors([
  {
    status: "default",
    schema: z.object({
      error: z.object({
        code: z.number(),
        message: z.string(),
      }),
    }),
  },
]);

const user = z.object({
  id: z.number(),
  name: z.string(),
  age: z.number().positive(),
  email: z.string().email(),
});
Enter fullscreen mode Exit fullscreen mode

Declaring your API endpoints

Then you can define your API endpoints directly in the Zodios constructor.

const apiClient = new Zodios('/api', [
  {
    method: "get",
    path: "/users",
    alias: "getUsers",
    parameters: [
      {
        name: "limit",
        type: "Query",
        schema: z.number().positive().default(10),
      },
      {
        name: "page",
        type: "Query",
        schema: z.number().positive().default(0),
      },
    ],
    response: z.object({
      nextPage: z.number(),
      users: z.array(user),
    }),
  },
  {
    method: "get",
    path: "/users/:id",
    alias: "getUser",
    response: user,
    errors,
  },
  {
    method: "post",
    path: "/users",
    alias: "createUser",
    parameters: [
      {
        name: "user",
        type: "Body",
        schema: user.omit({ id: true }),
      },
    ],
    response: user,
    errors,
  },  
]);
Enter fullscreen mode Exit fullscreen mode

Calling your APIs

And finally you can use it to fetch data from your API.

// get all users
const users = await apiClient.getUsers();
// get user by id
const user = await apiClient.getUser({ params: { id: 1 } });
// create user
const newUser = await apiCLient.createUser({ name: "John", age: 20, email: "jodn@doe.com"});
Enter fullscreen mode Exit fullscreen mode

Using Zodios with React

Zodios provides you react hooks based on react-query. Zodios manages your query keys, for you, no need to handle them by hand.

const zodiosHooks = new ZodiosHooks("myAPI", apiClient);

const Users = () => {
  const {
    data: users,
    isLoading,
    error,
    fetchNextPage,
    invalidate: invalidateUsers, // zodios also provides invalidation helpers
  } = zodiosHooks.useInfiniteQuery(
    "/users",
    { queries: { limit: 10 } },
    {
      getPageParamList: () => {
        return ["page"];
      },
      getNextPageParam: () => {
        return {
          queries: {
            page: page.nextPage + 1,
          },
        };
      },
    }
  );
  const { mutate } = zodiosHooks.useCreateUser(undefined, {
    onSuccess: () => invalidateUsers(),
  });

  return (
    <>
      <h1>Users</h1>
      <button onClick={() => mutate({ name: "john doe" })}>add user</button>
      {isLoading && <div>Loading...</div>}
      {error && <div>Error: {(error as Error).message}</div>}
      {users && (
        <>
          <ul>
            {users.pages.flatMap((page) =>
              page.map((user) => <li key={user.id}>{user.name}</li>)
            )}
          </ul>
          <button onClick={() => fetchNextPage()}>more</button>
        </>
      )}
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

What's next

Zodios has much more tools under the hood.
API client has a powerfull plugin system.
Zodios also provides an 'express' adapter, so you can also create your server APIs safely with nodeJS.
It integrates also nicely with 'NextJS'.

Take a look a zodios website

Top comments (0)

This post blew up on DEV in 2020:

js visualized

πŸš€βš™οΈ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! πŸ₯³

Happy coding!