UNPKG

fetchero

Version:

_A type-safe, proxy-based HTTP & GraphQL client for modern applications._

398 lines (273 loc) 7.84 kB
# **Fetchero** _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. --- ## **Table of Contents** 1. [Features](#features) 2. [Installation](#installation) 3. [Basic Usage](#basic-usage) - [Create an instance](#create-an-instance) - [REST & GraphQL as standalone clients](#or-individually) 4. [REST Client](#rest-client) - [Making requests](#making-requests) - [Dynamic path segments](#dynamic-path-segments) - [Override base URL or headers](#override-base-url-or-headers) 5. [GraphQL Client](#graphql-client) - [Queries](#queries) - [Mutations](#mutations) - [Subscriptions](#subscriptions) - [Override base URL & headers](#override-base-url--headers) 6. [Interceptors](#interceptors) 7. [Error Handling](#error-handling) 8. [Response Shape](#response-shape) 9. [Examples](#examples) 10. [TypeScript Support](#typescript-support) 11. [API Overview](#api-overview) 12. [Why Fetchero?](#why-fetchero) --- ## **Features** - **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. --- ## **Installation** ```bash npm install fetchero ``` or ```bash yarn add fetchero ``` --- ## **Basic Usage** ### **Create an instance** ```ts import { createFetchero } from 'fetchero'; const api = createFetchero({ baseUrl: 'https://api.example.com', headers: { Authorization: 'Bearer token' }, }); ``` ### **Or individually** If you only need one client type (REST or GraphQL): ```ts import { rest, gql } from 'fetchero'; const restClient = rest({ baseUrl: '...' }); const gqlClient = gql({ baseUrl: '...' }); ``` --- ## **REST Client** ### **Making requests** ```ts // 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' } }); ``` ### **Dynamic path segments** ```ts // GET /users/123/posts/456 const res = await api.rest .users(123) .posts(456) .get(); ``` ### **Or you may like short syntax** ```ts // 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' } }); ``` ### **Override base URL or headers** ```ts // 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(); ``` --- ## **GraphQL Client** Fetchero provides a **fluent query builder** for GraphQL. ### **Queries** ```ts // Basic query const res = await api.gql.query.getUser({ id: 123 }).select('id name email'); ``` This builds and executes: ```graphql query { getUser(id: 123) { id name email } } ``` ### **Mutations** ```ts const res = await api.gql.mutation .createUser({ name: 'John' }) .select('id name'); ``` ### **Subscriptions** ```ts const res = await api.gql.subscription .onMessage({ roomId: 1 }) .select('id content'); ``` --- ### **Passing Arguments & Variables** Fetchero automatically converts JS objects into **typed GraphQL variables**. #### **Plain Arguments (Auto-inferred types)** ```ts await api.gql.query.getUser({ id: 1 }).select('id name'); ``` **Builds:** ```graphql query my_query($id_0: Int) { getUser(id: $id_0) { id name } } ``` #### **Custom Types** Wrap values in `{ value, type }` to define the GraphQL type explicitly: ```ts await api.gql.query .getUser({ id: 1, status: { value: 'ACTIVE', type: 'StatusEnum!' } }) .select('id name status'); ``` **Builds:** ```graphql query my_query($id_0: Int, $status_0: StatusEnum!) { getUser(id: $id_0, status: $status_0) { id name status } } ``` #### **Nested Input Objects** You can pass complex input types like this: ```ts await api.gql.mutation.updateUser({ id: 1, profile: { type: 'UserProfileInput', value: { age: 30, email: 'john@example.com' }, }, }); ``` **Builds:** ```graphql mutation my_mutation($id_0: Int, $profile_0: UserProfileInput) { updateUser(id: $id_0, profile: $profile_0) } ``` #### **Rules for Arguments** - **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. --- ### **Override base URL & headers** ```ts await api.gql.query .getUser({ id: 123 }) .base('https://graphql.alt.com') .headers({ Authorization: 'Bearer new' }) .select('id name'); ``` --- ## **Interceptors** Intercept and modify **requests** and **responses** globally. ```ts 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; }, }, }); ``` --- ## **Error Handling** All errors are standardized: ```ts 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`. --- ## **Response Shape** Every request returns: ```ts interface FetcherResponse<T> { data: T | null; errors?: Array<{ message?: string; extensions: { code?: string; message?: string }; }>; } ``` --- ## **Examples** ### **Chained REST call with query & headers** ```ts await api.rest .users(42) .headers({ 'X-Custom': 'yes' }) .posts.get({ query: { page: 2 } }); ``` ### **GraphQL query with variables** ```ts await api.gql.query .searchUsers({ name: { type: 'String!', value: 'John' } }) .select('id name email'); ``` --- ## **TypeScript Support** Type definitions are included out of the box for: - REST responses - GraphQL queries/mutations - Errors & interceptors --- ## **API Overview** ### **REST** - `api.rest[resource]` Chainable resource paths - Methods: `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()` - Modifiers: `.base(url)`, `.headers({ ... })` ### **GraphQL** - `api.gql.query.field(args).select(fields)` - Operations: `query`, `mutation`, `subscription` - Modifiers: `.base(url)`, `.headers({ ... })` --- ## **Why Fetchero?** - No need to manually build URLs or queries. - Fluent, chainable API. - Works equally well for REST & GraphQL. - Easy integration with TypeScript & interceptors.