A type-safe, proxy-based HTTP & GraphQL client for modern applications.
fetchero is a lightweight, flexible, and highly intuitive library for making REST and GraphQL requests.
It uses Proxies for a chainable API, interceptors for pre/post-processing, and enhanced error handling for safer, predictable responses.
Whether you're consuming REST APIs or working with GraphQL backends, Fetchero simplifies the process with a declarative, fluent syntax that feels natural and reduces boilerplate.
- Features
- Installation
- Basic Usage
- REST Client
- GraphQL Client
- Interceptors
- Error Handling
- Response Shape
- Examples
- TypeScript Support
- API Overview
- Why Fetchero?
-
Proxy-based REST client — Build endpoints dynamically using dot-chaining and path arguments. No manual string concatenation.
-
GraphQL client with query builder — Write GraphQL queries fluently, with support for variables and field selection.
-
Base URL & dynamic headers — Override base URLs and headers globally or per-request.
-
Interceptors — Hook into requests and responses for logging, authentication, and transformation.
-
Error handling — Standardized error objects with meaningful messages and GraphQL error normalization.
-
Fully TypeScript ready — Get autocompletion and type safety for your API calls.
npm install fetchero
or
yarn add fetchero
import { createFetchero } from 'fetchero';
const api = createFetchero({
baseUrl: 'https://api.example.com',
headers: { Authorization: 'Bearer token' },
});If you only need one client type (REST or GraphQL):
import { rest, gql } from 'fetchero';
const restClient = rest({ baseUrl: '...' });
const gqlClient = gql({ baseUrl: '...' });// GET /users
const res = await api.rest.users.get();
// GET /users/123
const res = await api.rest.users(123).get();
// GET /users/123?active=true
const res = await api.rest.users(123).get({ query: { active: true } });
// POST /users with body
const res = await api.rest.users.post({ body: { name: 'John' } });// GET /users/123/posts/456
const res = await api.rest
.users(123)
.posts(456)
.get();// GET /users
const res = await api.rest('users').get();
// GET /users/123
const res = await api.rest('users', 123).get();
// POST /users/123/posts/456
const res = await api
.rest('users', 123, 'posts', 345)
.post({ body: { name: 'John' } });// Different base URL
await api.rest.users.base('https://another-api.com').get();
// Add/override headers
await api.rest.users.headers({ Authorization: 'Bearer new-token' }).get();Fetchero provides a fluent query builder for GraphQL.
// Basic query
const res = await api.gql.query.getUser({ id: 123 }).select('id name email');This builds and executes:
query {
getUser(id: 123) {
id
name
email
}
}const res = await api.gql.mutation
.createUser({ name: 'John' })
.select('id name');const res = await api.gql.subscription
.onMessage({ roomId: 1 })
.select('id content');Fetchero automatically converts JS objects into typed GraphQL variables.
await api.gql.query.getUser({ id: 1 }).select('id name');Builds:
query my_query($id_0: Int) {
getUser(id: $id_0) {
id
name
}
}Wrap values in { value, type } to define the GraphQL type explicitly:
await api.gql.query
.getUser({ id: 1, status: { value: 'ACTIVE', type: 'StatusEnum!' } })
.select('id name status');Builds:
query my_query($id_0: Int, $status_0: StatusEnum!) {
getUser(id: $id_0, status: $status_0) {
id
name
status
}
}You can pass complex input types like this:
await api.gql.mutation.updateUser({
id: 1,
profile: {
type: 'UserProfileInput',
value: { age: 30, email: 'john@example.com' },
},
});Builds:
mutation my_mutation($id_0: Int, $profile_0: UserProfileInput) {
updateUser(id: $id_0, profile: $profile_0)
}-
Plain values → Auto-inferred type (
Int,String,Float). -
{ value, type }→ Explicit GraphQL type. -
Nested objects → Use
{ type: 'MyInputType', value: { ... } }. -
Invalid values (arrays, functions, etc.) → Throw an error.
await api.gql.query
.getUser({ id: 123 })
.base('https://graphql.alt.com')
.headers({ Authorization: 'Bearer new' })
.select('id name');Intercept and modify requests and responses globally.
import { createFetchero } from 'fetchero';
const api = createFetchero({
baseUrl: 'https://api.example.com',
interceptors: {
request: async config => {
console.log('Outgoing Request:', config);
return config;
},
response: async response => {
console.log('Incoming Response:', response.data);
return response.data;
},
},
});All errors are standardized:
const res = await api.rest.users(999).get();
if (res.errors) {
console.log(res.errors[0].extensions.message); // "Not Found"
}GraphQL errors are automatically normalized using errorCompose.
Every request returns:
interface FetcherResponse<T> {
data: T | null;
errors?: Array<{
message?: string;
extensions: { code?: string; message?: string };
}>;
}await api.rest
.users(42)
.headers({ 'X-Custom': 'yes' })
.posts.get({ query: { page: 2 } });await api.gql.query
.searchUsers({ name: { type: 'String!', value: 'John' } })
.select('id name email');Type definitions are included out of the box for:
-
REST responses
-
GraphQL queries/mutations
-
Errors & interceptors
-
api.rest[resource]— Chainable resource paths -
Methods:
.get(),.post(),.put(),.patch(),.delete() -
Modifiers:
.base(url),.headers({ ... })
-
api.gql.query.field(args).select(fields) -
Operations:
query,mutation,subscription -
Modifiers:
.base(url),.headers({ ... })
-
No need to manually build URLs or queries.
-
Fluent, chainable API.
-
Works equally well for REST & GraphQL.
-
Easy integration with TypeScript & interceptors.