Zodios : an open source HTTP client with type checking at both compile time and runtime (part 2/3)
Tags: typescript, axios, http, zodios
Published: 2022-05-24 11:40:10

In the last [article](https://dev.to/ecyrbe/how-to-get-devto-articles-with-zodios-part-12-46ci) we have seen how to use zodios to declare custom REST APIs.  
As a reminder, [Zodios](https://github.com/ecyrbe/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 :
```typescript
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)
```
And then to call this API you should do :
```typescript
const shipments = await client.get(`/v1/organizations/${MY_ORG}/shipments`);
```

Now with zodios you can declare aliases :
```typescript
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)
```
And then to use it :
```typescript
const shipments = await client.getOrgShipments();
```
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](https://github.com/ecyrbe/zodios/blob/main/src/api.ts#L21) for inspiration.

```typescript
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));
```

This is esquivalent to this more verbose declaration :
```typescript
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);
```
You can also see, that aliases for all CRUD endpoints are autogenerated.
So you can call your endpoints with them :
```typescript
const users = await client.getUsers();
```

## React Hooks
Zodios also has some React Hooks. Behind the scene, Zodios is using [react-query](https://react-query.tanstack.com/).  
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.

```typescript
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>
      )}
    </>
  );
};
```

## 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](https://github.com/ecyrbe/zodios/issues), or even better contribute with a Pull Request.  
If you like [zodios](https://github.com/ecyrbe/zodios), don't forget to give this article a thumbs up.