Straightforward, typesafe, and feature-based fetch wrapper supporting OpenAPI types
npm install feature-fetch> Status: Experimental
feature-fetch is a straightforward, typesafe, and feature-based fetch wrapper supporting OpenAPI types.
- Lightweight & Tree Shakable: Function-based and modular design (< 6KB minified)
- Fast: Thin wrapper around the native fetch, maintaining near-native performance
- Modular & Extendable: Easily extendable with features like withRetry(), withOpenApi(), ..
- Typesafe: Build with TypeScript for strong type safety and support for openapi-typescript types
- Standalone: Only dependent on fetch, ensuring ease of use in various environments
Create a typesafe, straightforward, and lightweight fetch wrapper that seamlessly integrates with OpenAPI schemas using openapi-typescript. It aims to simplify error handling by returning results in a predictable manner with ts-results-es. Additionally, it is designed to be modular & extendable, enabling the creation of straightforward API wrappers, such as for the Google Web Fonts API (see google-webfonts-client). feature-fetch only depends on fetch, making it usable in most sandboxed environments like Figma plugins.
``ts
import { createApiFetchClient } from 'feature-fetch';
const fetchClient = createApiFetchClient({
prefixUrl: 'https://api.example.com/v1'
});
// Send request
const response = await fetchClient.get<{ id: string }>('/blogposts/{postId}', {
pathParams: {
postId: '123'
}
});
// Handle response
if (response.isOk()) {
console.log(response.value.data); // Handle successful response
} else {
console.error(response.error.message); // Handle error response or network exception
}
// Or unwrap the response, throwing an exception on error
try {
const data = response.unwrap().data;
console.log(data);
} catch (error) {
console.error(error.message);
}
`
Enhance feature-fetch to create a typesafe fetch wrapper. This feature provides common HTTP methods (get, post, put, del) ensuring requests and responses are typed.
1. Create an API Fetch Client:
Use createApiFetchClient to create a fetch client with a specified base URL.
`ts
import { createApiFetchClient } from 'feature-fetch';
const fetchClient = createApiFetchClient({
prefixUrl: 'https://api.example.com/v1'
});
`
2. Send Requests:
Use the fetch client to send requests, specifying the response type for better type safety.
`ts`
// Send request
const response = await fetchClient.get<{ id: string }>('/blogposts/{postId}', {
pathParams: {
postId: '123'
}
});
Enhance feature-fetch with OpenAPI support to create a typesafe fetch wrapper. This feature provides common HTTP methods (get, post, put, del) that are fully typed by leveraging your OpenAPI schema using openapi-typescript.
1. Generate TypeScript Definitions:
Use openapi-typescript to generate TypeScript definitions from your OpenAPI schema.
`bash`
npx openapi-typescript ./path/to/my/schema.yaml -o ./path/to/my/schema.d.ts
2. Create an OpenAPI Fetch Client:
Import the generated paths and use createOpenApiFetchClient() to create a fetch client.
`ts
import { createOpenApiFetchClient } from 'feature-fetch';
import { paths } from './openapi-paths';
const fetchClient = createOpenApiFetchClient
prefixUrl: 'https://api.example.com/v1'
});
`
3. Send Requests:
Use the fetch client to send requests, ensuring typesafe parameters and responses.
`ts`
// Send request
const response = await fetchClient.get('/blogposts/{postId}', {
pathParams: {
postId: '123'
}
});
Enhance feature-fetch to create a typesafe fetch wrapper specifically for GraphQL requests. This feature allows you to send GraphQL queries and mutations, ensuring requests and responses are typed.
1. Create a GraphQL Fetch Client:
Use withGraphQL to extend your existing fetch client with GraphQL capabilities.
`ts
import { gql, withGraphQL } from 'feature-fetch';
import createFetchClient from './createFetchClient';
const baseFetchClient = createFetchClient({
prefixUrl: 'https://api.example.com/v1/graphql'
});
const graphqlClient = withGraphQL(baseFetchClient);
`
2. Define GraphQL Queries:
Use the gql tagged template literal to define your GraphQL queries with syntax highlighting.
`ts
const GET_USER = gql
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
;`
3. Send GraphQL Requests:
Use the GraphQL-enabled fetch client to send requests, specifying the response type for better type safety.
`ts`
// Send GraphQL query
const response = await graphqlClient.query<
{ id: number },
{ user: { id: string; name: string; email: string } }
>(GET_USER, {
variables: {
id: '123'
}
});
When handling API error responses (response.isErr()), response can be one of three Error types, each representing a different kind of failure.
Indicates a failure in network communication, such as loss of connectivity.
`ts`
if (response.isErr() && response.error instanceof NetworkError) {
console.error('Network error:', response.error.message);
}
Occurs when the server returns a response with a status code indicating an error (e.g., 4xx or 5xx).
`ts`
if (response.isErr() && response.error instanceof RequestError) {
console.error('Request error:', response.error.message, 'Status:', response.error.status);
}
A general exception type that can encompass other error scenarios not covered by NetworkError or RequestError, for example when the response couldn't be parsed, ..
`ts`
if (response.isErr() && response.error instanceof FetchError) {
console.error('Service error:', response.error.message);
}
`ts
if (response.isErr()) {
const error = response.error;
if (isStatusCode(error, 404)) {
console.error('Not found:', error.data);
}
if (error instanceof NetworkError) {
console.error('Network error:', error.message);
} else if (error instanceof RequestError) {
console.error('Request error:', error.message, 'Status:', error.status);
} else if (error instanceof FetchError) {
console.error('Service error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
`
Retries each request using an exponential backoff strategy if a network exceptions (NetworkError) or HTTP 429 (Too Many Requests) response occur.
`ts
import { createApiFetchClient, withRetry } from 'feature-fetch';
const fetchClient = withRetry(
createApiFetchClient({
prefixUrl: 'https://api.example.com/v1'
}),
{
maxRetries: 3
}
);
`
- maxRetries: Maximum number of retry attempts
Delays each request by a specified number of milliseconds before sending it.
`ts
import { createApiFetchClient, withDelay } from 'feature-fetch';
const fetchClient = withDelay(
createApiFetchClient({
prefixUrl: 'https://api.example.com/v1'
}),
1000
);
`
- delayInMs: Delay duration in milliseconds
@0no-co/graphql.web is listed as a dependency because it's dynamically imported in the getQueryString()` function. If the function isnβt used, Webpack's tree shaking should exclude it from the final bundle. This ensures that only necessary modules are included, keeping your build clean.