Skip to content

Functional API

Zlient v2 introduces a purely functional way to define endpoints. This removes the boilerplate of class inheritance and strict class hierarchies.

1. The createEndpoint Method

The entry point is client.createEndpoint(config).

typescript
const endpoint = client.createEndpoint({
  method: 'GET',
  path: '/users',
  // ... schemas
});

2. Dynamic Paths

You can define dynamic paths using a function. The function receives the inferred type of your pathParams schema.

typescript
const getUser = client.createEndpoint({
  method: 'GET',
  // `params` is fully typed as { id: string }
  path: (params) => `/users/${params.id}`,
  
  // This schema drives the type of `params` above
  pathParams: z.object({
    id: z.string()
  })
});

3. Strict Schemas

You can validate every part of the request lifecycle:

  • request: The JSON body (for POST/PUT).
  • query: The URL search parameters.
  • pathParams: The dynamic path segments.
  • response: The expected JSON response from the server.
typescript
// 1. GET with Query Params & Response Map
const searchUsers = client.createEndpoint({
  method: 'GET',
  path: '/users/search',
  
  query: z.object({
    q: z.string(),
    page: z.number().default(1)
  }),
  
  response: {
    200: z.object({
      results: z.array(z.object({ id: z.string(), name: z.string() })),
      total: z.number()
    })
  }
});

// 2. POST with Request Body
const createUser = client.createEndpoint({
  method: 'POST',
  path: '/users',
  
  // Validate request body
  request: z.object({
    name: z.string().min(2),
    email: z.string().email(),
    role: z.enum(['admin', 'user'])
  }),
  
  // Validate response body
  response: z.object({
    id: z.string(),
    createdAt: z.string().datetime()
  })
});

// 3. GET with Path Params
const getUser = client.createEndpoint({
  method: 'GET',
  // Type-safe path construction
  path: ({ id }) => `/users/${id}`,
  
  pathParams: z.object({
    id: z.string().uuid()
  }),
  
  response: z.object({
    id: z.string(),
    name: z.string()
  })
});

4. Execution

To execute an endpoint, call it directly as a function.

typescript
const result = await searchUsers({
  query: { q: 'alice', page: 2 }
});

// result.results is typed!

5. Abort Signals & timeouts

You can pass standard fetch options to the function call by including them in the params object.

typescript
const controller = new AbortController();

await searchUsers({
  query: { q: 'alice' },
  signal: controller.signal,
  headers: { 'X-Custom': '123' }
});

Released under the MIT License.