Getting started

Quick start

A working endpoint in three steps.

1. Declare a service

defineService is an identity function. It exists to preserve the literal types of your config, which is what lets everything downstream be inferred.

api.ts
import { defineService, createApi } from '@tesyl/hapi';
import { z } from 'zod';

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

const users = defineService({
  service: 'users',
  basePath: '/users',
  endpoints: {
    detail: {
      endpoint: 'detail',
      method: 'GET',
      path: '/:id',
      pathParamsSchema: z.object({ id: z.string() }),
      responseSchema: userSchema,
    },
    update: {
      endpoint: 'update',
      method: 'PATCH',
      path: '/:id',
      pathParamsSchema: z.object({ id: z.string() }),
      requestSchema: z.object({ name: z.string() }),
      responseSchema: userSchema,
    },
  },
});

2. Compose the API

createApi cascades baseUrl, headers, hooks, and defaultOptions down through every service and endpoint.

api.ts
export const api = createApi({
  baseUrl: 'https://api.example.com',
  services: { users },
});

3. Call it

UserProfile.tsx
function UserProfile({ id }: { id: string }) {
  const { data } = api.users.detail.withPathParams({ id }).useQuery();
  //      ^? User | undefined

  const save = api.users.update.withPathParams({ id }).useMutation();

  if (!data) return <Skeleton />;
  return (
    <>
      <h1>{data.name}</h1>
      <button onClick={() => save.mutate({ name: 'Ada' })}>Rename</button>
    </>
  );
}