Skip to content

Node.js

Grabkit uses the platform fetch API. In Node.js 18+, fetch is built in; no separate adapter is required. The same factory and tuple results apply as in the browser.

Keep secrets and base URLs in environment variables, not in client bundles:

lib/api.ts
import grabkit from 'grabkit';
const baseURL = process.env.API_URL;
if (!baseURL) {
throw new Error('API_URL is not set');
}
export const grab = grabkit(baseURL, {
casing: 'camelCase',
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
});

For plain JSON backends (Stripe, GitHub, many REST APIs), set format: 'json'. See Plain JSON.

On the server, prefer returning tuple errors to the caller instead of throwing, so you can map HTTP status codes explicitly:

import { grab } from '../lib/api';
export async function getUserHandler(userId: string) {
const [data, error, meta] = await grab(`GET /users/${userId}`);
if (error) {
const status =
error.name === 'GrabkitError' ? error.statusCode : 502;
return Response.json(
{ message: error.message },
{ status },
);
}
return Response.json(data, { status: meta.statusCode });
}
import express from 'express';
import { grab } from './lib/api';
const app = express();
app.get('/users/:id', async (req, res) => {
const [data, error] = await grab(`GET /users/${req.params.id}`);
if (error) {
const status = error.name === 'GrabkitError' ? error.statusCode : 502;
return res.status(status).json({ message: error.message });
}
res.json(data);
});
app/api/users/[id]/route.ts
import { grab } from '@/lib/api';
export async function GET(
_request: Request,
{ params }: { params: Promise<{ id: string }> },
) {
const { id } = await params;
const [data, error] = await grab(`GET /users/${id}`);
if (error) {
const status = error.name === 'GrabkitError' ? error.statusCode : 502;
return Response.json({ message: error.message }, { status });
}
return Response.json(data);
}
const [user, error] = await grab('POST /users', {
body: { type: 'users', name: 'Leo', email: 'leo@example.com' },
});
if (error) {
// handle validation / 4xx / 5xx
}

GrabkitValidationError (for example missing type on a JSON:API body) is thrown before fetch. Wrap writes in try/catch when you need to handle that separately from HTTP errors.

When a browser app uses TanStack Query or SWR, the browser usually calls your BFF or public API. Two common layouts:

LayoutWhere grab runsNotes
BFFNode route calls upstream with grabHides tokens; maps tuples to JSON responses
DirectBrowser grab to public APIUse public base URL; attach user tokens per request

See TanStack Query and SWR for client-side cache integration.

Pass signal through per-call options (standard fetch):

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10_000);
const [data, error] = await grab('GET /users/1', {
signal: controller.signal,
});
clearTimeout(timeout);

Aborted requests resolve as GrabkitTransportError in the tuple. See Errors & results.