DEV Community

Cover image for Zodios : an open source HTTP client with type checking at both compile time and runtime (part 2/3)
ecyrbe
ecyrbe

Posted on

Zodios : an open source HTTP client with type checking at both compile time and runtime (part 2/3)

In the last article we have seen how to use zodios to declare custom REST APIs.

As a reminder, Zodios is an open source HTTP client with type checking at runtime (zod validation) and also at compile time (typescript) without needing to write both types and validation schemas.

You just need to write your API declaration once and you're done.

In this follow up, we will take a look at some features of Zodios that can make your life easier :

  1. Alias endpoints
  2. CRUD helper
  3. React hooks

Alias endpoints

When using previous versions of zodios, you had no choice but to repeat yourself and write again your PATH both when declaring your API and when using it.

It's great for readability, but in some use cases, repeating it can be tedious.

Check this example from an issue a user sent :

const MY_ORG = 'my-org-id' as const; // user wanted to extract his organization ID from the declaration
const client = new Zodios(BASE_API_URL, [
  {
    method: 'get',
    path: `/v1/organizations/${MY_ORG}/shipments`,
    response: schipmentSchema
  }
] as const)
Enter fullscreen mode Exit fullscreen mode

And then to call this API you should do :

const shipments = await client.get(`/v1/organizations/${MY_ORG}/shipments`);
Enter fullscreen mode Exit fullscreen mode

Now with zodios you can declare aliases :

const MY_ORG = 'my-org-id' as const; // if your org changes you only need to change it once here
const client = new Zodios(BASE_API_URL, [
  {
    method: 'get',
    path: `/v1/organizations/${MY_ORG}/shipments`,
    alias: 'getOrgShipments',
    response: schipmentSchema
  }
] as const)
Enter fullscreen mode Exit fullscreen mode

And then to use it :

const shipments = await client.getOrgShipments();
Enter fullscreen mode Exit fullscreen mode

You no longer need to repeat yourself.

CRUD Helper

This feature might not suit all your needs, but if you have simple CRUD declarations, you can use asCrudApi() helper to not repeat yourself.

If you have more complex APIs, but your endpoints are standardized, you can create your own helper, take a look at asCrudApi() implementation for inspiration.

import { Zodios, asCrudApi } from '@zodios/core';

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
});
const apiClient = new Zodios(BASE_URL, 
asCrudApi('user',userSchema));
Enter fullscreen mode Exit fullscreen mode

This is esquivalent to this more verbose declaration :

const apiClient = new Zodios(BASE_URL, [
  {
    method: "get",
    path: "/users",
    alias: "getUsers",
    description: "Get all users",
    response: z.array(userSchema),
  },
  {
    method: "get",
    path: "/users/:id",
    alias: "getUser",
    description: "Get a user",
    response: userSchema,
  },
  {
    method: "post",
    path: "/users",
    alias: "createUser",
    description: "Create a user",
    parameters: [
      {
        name: "body",
        type: "Body",
        description: "The object to create",
        schema: userSchema.partial(),
      },
    ],
    response: userSchema,
  },
  {
    method: "put",
    path: "/users/:id",
    alias: "updateUser",
    description: "Update a user",
    parameters: [
      {
        name: "body",
        type: "Body",
        description: "The object to update",
        schema: userSchema,
      },
    ],
    response: userSchema,
  },
  {
    method: "patch",
    path: "/users/:id",
    alias: "patchUser",
    description: "Patch a user",
    parameters: [
      {
        name: "body",
        type: "Body",
        description: "The object to patch",
        schema: userSchema.partial(),
      },
    ],
    response: userSchema,
  },
  {
    method: "delete",
    path: "/users/:id",
    alias: "deleteUser",
    description: "Delete a user",
    response: userSchema,
  },
] as const);
Enter fullscreen mode Exit fullscreen mode

You can also see, that aliases for all CRUD endpoints are autogenerated.
So you can call your endpoints with them :

const users = await client.getUsers();
Enter fullscreen mode Exit fullscreen mode

React Hooks

Zodios also has some React Hooks. Behind the scene, Zodios is using react-query.

You can also use your alias for hooks. They will be automatically prefixed with use<Alias>.

For example, alias getUsers can be used with useGetUsers hook.

import React from "react";
import { Zodios } from "@zodios/core";
import { ZodiosHooks } from "@zodios/react";
import { z } from "zod";

const baseUrl = "https://jsonplaceholder.typicode.com";

const userSchema = z
  .object({
    id: z.number(),
    name: z.string(),
  });

const zodios = new Zodios(baseUrl, asCrudApi('user',userSchema));
const zodiosHooks = new ZodiosHooks("jsonplaceholder", zodios);

const Users = () => {
  const {
    data: users,
    isLoading,
    error,
    invalidate: invalidateUsers, // zodios also provides invalidation helpers
  } = zodiosHooks.useGetUsers();
  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.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      )}
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Conlusion

That all for today. Zodios is still evolving. If you have use cases you'd like to see in Zodios, please, create an issue on github, or even better contribute with a Pull Request.

If you like zodios, don't forget to give this article a thumbs up.

Discussion (0)