Getting started

Clover ☘️

Server routes augmented with Zod and OpenAPI


Cover from Midjourney

⚠️

If you find inaccuracies or anything missing, please open an issue on GitHub!

Installation

pnpm i @sarim.garden/clover

Introduction

Clover is a library that allows you to define your server routes using Zod and OpenAPI. You can use Clover for making your server routes type-safe and self-documenting, and as a lighterweight TRPC/Zodios replacement.

You can use Clover with any framework/runtime that supports WinterCG style functions e.g.

const handler = (request: Request) => {
  return new Response("Hello World!");
};

Server

Here's what a handler augmented with Clover looks like:

// server.ts
 
import { makeRequestHandler } from "@sarim.garden/clover";
import { z } from "zod";
 
export const { handler } = makeRequestHandler({
  method: "GET",
  path: "/hello",
  description: "Returns a greeting",
  input: z.object({
    name: z.string(),
  }),
  output: z.object({
    greeting: z.string(),
  }),
  run: async ({ input, sendOutput }) => {
    return sendOutput({
      greeting: `Hello ${input.name}!`,
    });
  },
});

You can learn more about how to add authentication, about Clover's heuristics for query parameters, path parameters, request bodies etc. by reading the server documentation.

OpenAPI

You can generate OpenAPI documentation from your server routes.

// server.ts
 
export const { handler, openAPIPathsObject } = makeRequestHandler({
  // ...same as above
});
// openapi.ts
 
import { OpenAPIObject, OpenAPIPathsObject } from "@sarim.garden/clover";
import { openAPIPathsObject } from "./server";
 
// it's ugly, I know, but this nicely combines
// multiple openAPI definitions from different route handlers :)
const pathsObject: OpenAPIPathsObject = [
  openAPIPathsObject,
  // ...add others here
].reduce((acc, curr) => {
  Object.keys(curr).forEach((k) => {
    acc[k] = {
      ...acc[k],
      ...curr[k],
    };
  });
  return acc;
}, {});
 
export const document: OpenAPIObject = {
  info: {
    title: "My API",
    version: "1.0.0",
  },
  openapi: "3.0.0",
  paths: pathsObject,
};

You can now serve this document as JSON, and use it with Swagger UI or similar tools.

Client

Clover also provides an optional client fetcher. It's nothing fancy, it just wraps the Fetch API and provides type safety.

// server.ts
 
const { handler, clientConfig } = makeRequestHandler({
  // ...same as above
});
 
export { handler as GET };
export type clientTypes = typeof clientConfig;
// client.ts
 
import { makeFetcher } from "@sarim.garden/clover";
import type { clientTypes } from "./server";
 
const fetcher = makeFetcher({
  baseUrl: "https://example.com",
});
 
const { greeting } = await fetcher<clientTypes>({
  input: {
    name: "Sarim",
  },
});

You can read more about how the fetcher works in the client documentation.

Credits

Similar projects

Enabling libraries