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
- TRPC: https://github.com/trpc/trpc (opens in a new tab)
- Zodios: https://github.com/ecyrbe/zodios (opens in a new tab)
- ts-rest: https://github.com/ts-rest/ts-rest (opens in a new tab)
- feTS: https://github.com/ardatan/fets (opens in a new tab)
- zaCT: https://github.com/pingdotgg/zact (opens in a new tab)