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' }
});