Skip to content

IainMcHugh/zaxiod

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zaxiod

A simple fetch wrapper that combines the API of axios with runtime type-safety of zod. To install:

pnpm add zaxiod # or npm, yarn

Usage

Start by setting up gateways in a single file and exporting for use.

import { zaxiod } from "zaxiod";

const baseHeaders = {
  "Content-type": "application/json",
};

export const gateway = zaxiod({
  baseURL: "http://localhost:8000",
  baseHeaders,
});

export const frontendGateway = zaxiod({
  baseURL: "http://localhost:3000/api",
  baseHeaders,
});

The next step is to create your zod schema defining your API structure.

import { z } from "zod";

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

type Ingredient = z.infer<typeof ingredientSchema>;

export { type Ingredient, ingredientSchema };

Now you can define your zod-safe API endpoints like so.

import { gateway } from "@gateways";
import { type Ingredient, ingredientSchema } from "@schema/ingredient.schema";

const findById = async (id: number) => {
  return await gateway.get(ingredientSchema)(`/ingredient/${id}`, {
    headers: { /** Some specific headers */}
  });
};

const create = async (payload: Ingredient) => {
  return await gateway.post(ingredientSchema)(`/ingredient`, payload);
};

const update = async (payload: Partial<Ingredient>) => {
  return await gateway.patch(ingredientSchema)(`/ingredient`, payload);
};

const delete = async (id: number) => {
  return await gateway.delete(ingredientSchema)(`/ingredient/${id}`);
};

export const ingredients = {
  findById,
  create,
  update,
  delete
};

And use! By passing the schema you are guaranteeing the return type to be safe-parsed by zod.

const getMyIngredient = async (): Promise<Ingredient | null> => {
  const response = await ingredients.findById(23);
  if (response.success) {
    // awaited 'data' is of type 'Ingredient'
    return await response.data;
  } else {
    // 'error' is of type 'ZodError'
    console.log(response.error.message);
    return null;
  }
};

Interceptors

Sometimes you will want to attach interceptors for all requests going through a particular gateway. A common use case for this is sending authorization tokens with each request.

const gateway = zaxiod({
  baseURL: "http://localhost:8000",
  onReq: (req) => {
    req.headers = {
      ...req.headers,
      Authorization: `Bearer ${token}`,
    };
    // must return req
    return req;
  },
  onRes: (res) => {
    logService.log(res.status);
    // must return res
    return res;
  },
});

As you will most likely need to pass the token from within the appropriate point in your code, you can structure your code like this:

const getServerProps = async (context: ServerContext) => {
  const id = context.query;
  const token = await getToken(context);
  const api = zaxiod(getGateway(token);
  const ingredientsResponse = await api.ingredients.findById(id);
  if (ingredientsResponse.success) {
    const ingredients = await ingredientsResponse.data;
    return {
      props: {
        ingredients,
      },
    };
  } else {
    context.logger.error(ingredientsResponse.error);
    return null;
  }
};

About

Axios API with zod safety

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published