client methods
Detailed reference for every Duck Query client method. Covers get, post, put, patch, del, request, byMethod, and the axios instance.
Overview
When you create a client with createDuckQueryClient<Routes>(), you get an object with
eight members. Each method is generic over your route map, so TypeScript enforces valid
paths, request shapes, and response types at compile time.
import { createDuckQueryClient } from '@gentleduck/query'
import type { ApiRoutes } from '@gentleduck/gen/nestjs'
const client = createDuckQueryClient<ApiRoutes>({
baseURL: 'http://localhost:3000',
})createDuckQueryClient
The factory function that creates a typed client.
function createDuckQueryClient<Routes>(
options?: AxiosInstance | AxiosRequestConfig,
): DuckQueryClient<Routes>Parameters:
| Parameter | Type | Description |
|---|---|---|
options | AxiosInstance or AxiosRequestConfig or undefined | Pass an existing Axios instance, or a config object to create a new one. |
Examples:
// With config (most common)
const client = createDuckQueryClient<ApiRoutes>({
baseURL: 'http://localhost:3000',
withCredentials: true,
timeout: 10000,
})
// With existing Axios instance
import axios from 'axios'
const axiosInstance = axios.create({
baseURL: 'http://localhost:3000',
headers: { 'X-App': 'my-app' },
})
const client = createDuckQueryClient<ApiRoutes>(axiosInstance)
// With no config (useful when baseURL is set via interceptor)
const client = createDuckQueryClient<ApiRoutes>()get
Sends a GET request. The body is never sent, even if the request object has one.
client.get<P extends PathsByMethod<Routes, 'GET'>>(
path: P,
req?: RouteReqMethod<Routes, P, 'GET'>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, 'GET'>>>TypeScript constraint: The path must be a route that supports the GET method. You
cannot client.get('/api/auth/signin') if that route only allows POST.
Examples:
// Simple GET
const { data: users } = await client.get('/api/users')
// GET with query parameters
const { data: users } = await client.get('/api/users', {
query: { page: 1, limit: 20, sort: 'name' },
})
// GET with path params
const { data: user } = await client.get('/api/users/:id', {
params: { id: 'u_123' },
})
// GET with path params + query + headers
const { data: user } = await client.get('/api/users/:id', {
params: { id: 'u_123' },
query: { include: 'profile' },
headers: { authorization: 'Bearer tok_abc' },
})
// GET with extra Axios config
const { data, headers } = await client.get('/api/users', undefined, {
responseType: 'json',
timeout: 5000,
})post
Sends a POST request with a JSON body.
client.post<P extends PathsByMethod<Routes, 'POST'>>(
path: P,
req: RouteReqMethod<Routes, P, 'POST'>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, 'POST'>>>Note: The req parameter is required for POST (not optional like GET).
Examples:
// POST with body
const { data: session } = await client.post('/api/auth/signin', {
body: {
username: 'duck',
password: '123456',
},
})
// POST with body + extra config
const { data } = await client.post(
'/api/auth/signin',
{
body: { username: 'duck', password: '123456' },
},
{
withCredentials: true,
timeout: 10000,
},
)
// POST with body + query params
const { data } = await client.post('/api/files/upload', {
body: { name: 'report.pdf', size: 1024 },
query: { overwrite: true },
})put
Sends a PUT request with a JSON body. Typically used for full resource updates.
client.put<P extends PathsByMethod<Routes, 'PUT'>>(
path: P,
req: RouteReqMethod<Routes, P, 'PUT'>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, 'PUT'>>>Examples:
const { data: updated } = await client.put('/api/users/:id', {
params: { id: 'u_123' },
body: {
name: 'Updated Duck',
email: 'duck@example.com',
},
})patch
Sends a PATCH request with a JSON body. Typically used for partial updates.
client.patch<P extends PathsByMethod<Routes, 'PATCH'>>(
path: P,
req: RouteReqMethod<Routes, P, 'PATCH'>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, 'PATCH'>>>Examples:
const { data: updated } = await client.patch('/api/users/:id', {
params: { id: 'u_123' },
body: {
name: 'Patched Duck', // only update the name
},
})del
Sends a DELETE request. The body is not sent.
client.del<P extends PathsByMethod<Routes, 'DELETE'>>(
path: P,
req?: RouteReqMethod<Routes, P, 'DELETE'>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, 'DELETE'>>>Note: The method is called del (not delete) because delete is a reserved word
in JavaScript.
Examples:
// Simple delete
await client.del('/api/users/:id', {
params: { id: 'u_123' },
})
// Delete with headers
await client.del('/api/users/:id', {
params: { id: 'u_123' },
headers: { authorization: 'Bearer tok_abc' },
})request
A generic method that uses config.method to determine the HTTP method.
Defaults to GET if no method is specified.
client.request<P extends RoutePath<Routes>>(
path: P,
req?: RouteReq<Routes, P>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteRes<Routes, P>>>Examples:
// Defaults to GET
const { data } = await client.request('/api/users')
// Explicit method via config
const { data } = await client.request(
'/api/auth/signin',
{ body: { username: 'duck', password: '123456' } },
{ method: 'POST' },
)Use request when you want to determine the method dynamically. For static calls,
prefer the named methods (get, post, etc.) for better type safety.
byMethod
Explicitly specify the HTTP method as the first argument. This gives you the strongest type checking: the path is constrained to routes that support the given method.
client.byMethod<M extends RouteMethods<Routes>, P extends PathsByMethod<Routes, M>>(
method: M,
path: P,
req?: RouteReqMethod<Routes, P, M>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<RouteResMethod<Routes, P, M>>>Examples:
const { data } = await client.byMethod('POST', '/api/auth/signin', {
body: { username: 'duck', password: '123456' },
})
const { data } = await client.byMethod('GET', '/api/users/:id', {
params: { id: 'u_123' },
})Use byMethod when you have the method as a variable:
async function call(method: 'GET' | 'POST', path: string) {
return client.byMethod(method, path as any)
}axios
The underlying Axios instance. Use it to configure interceptors, defaults, or make untyped requests when needed.
// Add a request interceptor
client.axios.interceptors.request.use((config) => {
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// Add a response interceptor
client.axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
redirectToLogin()
}
return Promise.reject(error)
},
)
// Access defaults
client.axios.defaults.timeout = 10000Config merging behavior
When both the req object and the config parameter provide the same field, the req
object takes precedence:
| Field | Comes from | Fallback |
|---|---|---|
params (query string) | req.query | config.params |
headers | req.headers | config.headers |
data (body) | req.body | Not applicable |
// req.query wins over config.params
await client.get('/api/users', {
query: { page: 2 }, // this is used
}, {
params: { page: 1 }, // this is ignored
})Next steps
- Types reference: all exported types for building custom route maps.
- Advanced patterns: interceptors, error handling, custom routes.