fetchservices
v0.2.4
Published
A wrapper service to make http fetch requests.
Maintainers
Readme
FetchServices
The name given FetchService is a lightweight HTTP client fetch service based on ofetch.
The service provides a simple, type-safe fetch layer for consistent REST operation handling. It has built-in support for request/response interceptors, as well as optional authentication token, logging, aborting, and ETag cache helpers.
Overview
Installation
npm install fetchservices
fetchservicesis published as an ESM package only.
Quick Start
import FetchService from "fetchservices";
const api = new FetchService({ baseURL: "https://api.example.com" });
// GET request
const users = await api.get<{ id: number; name: string }[]>("/users");
// POST request
const created = await api.post<{ id: number; name: string }>("/users", {
name: "Alice",
});API
new FetchService(options?)
Create a new HTTP service instance. Pass any valid ofetch request options.
const api = new FetchService({ baseURL: "https://api.example.com" });api.get<T>(url, params?, headers?)
Send a GET request.
api.post<T>(url, body?, params?, headers?)
Send a POST request.
api.put<T>(url, body?, params?, headers?)
Send a PUT request.
api.delete<T>(url, params?, headers?, body?)
Send a DELETE request.
api.upload<T>(url, data, params?, headers?)
Send a multipart file upload request.
api.download(url, params?, headers?)
Download a binary payload and return an ArrayBuffer.
api.fetch<T>(request, options?)
Make a low-level request with a raw fetch-style request or options.
Singleton
The FetchService stores the last created instance as singleton.
import FetchService from "fetchservices";
// create a new FetchService
new FetchService({ baseURL: "https://api.example.com" });
const api = FetchService.getInstance();Interceptors
The FetchService supports request and response interceptors. You can add your own or use the built-in interceptors.
An interceptor can be added with api.addInterceptor(interceptor).
Interceptor keys are strings and can be used to remove registered interceptors with removeInterceptor(key).
Built-in interceptors
import FetchService from "fetchservices";
import {
LoggingInterceptor,
AbortInterceptor,
BearerTokenInterceptor,
ETagCacheInterceptor,
} from "fetchservices/interceptors";
// initialise a new FetchService
const api = new FetchService({ baseURL: "https://api.example.com" });
// add interceptors to the FetchService instance
const loggerId = api.addInterceptor(new LoggingInterceptor());
const abortId = api.addInterceptor(new AbortInterceptor());
const authId = api.addInterceptor(
new BearerTokenInterceptor(() => localStorage.getItem("token") ?? undefined),
);
const cacheInterceptor = new ETagCacheInterceptor(".*\/users\/.*");
api.addInterceptor(cacheInterceptor);
// you can remove any interceptor later on
api.removeInterceptor(loggerId);Custom interceptor
You can create custom interceptors using a class approach or as a casual object.
// class style
import { AbstractFetchInterceptor } from "fetchservices";
class CustomInterceptor extends AbstractFetchInterceptor {
onRequest({ request, options }) {
console.debug("starting", request);
}
onResponse({ response }) {
console.debug("response status", response.status);
}
}
// add the interceptor to the FetchService instance
api.addInterceptor(new CustomInterceptor(".*\/api/path\/.*"));// object style
import type { FetchInterceptor } from "fetchservices";
const customInterceptor: FetchInterceptor = {
urlPattern: RegExp(".*\/api/path\/.*"),
onRequest({ request, options }) {
console.debug("starting", request);
},
onResponse({ response }) {
console.debug("response status", response.status);
},
};
// add the interceptor to the FetchService instance
api.addInterceptor(customInterceptor);Authentication
The package also includes an abstract AuthService class, which can be used to implement an authentication workflow with FetchService integration.
AuthService
The AuthService is an abstract base class for authentication workflows and already integrates with FetchService.
Based on the abstract class, you can implement any authentication provider you like. For an example, look at KeyCloakAuthService.
import { AuthService } from "fetchservices/auth";
class MyAuthService extends AuthService {
isAuthenticated() {
return false;
}
login() {
return Promise.resolve(undefined);
}
logout() {
return Promise.resolve(undefined);
}
refreshToken() {
return Promise.resolve(undefined);
}
tokenExpired() {
return true;
}
getToken() {
return "";
}
getProfile() {
return null;
}
}Authorization header injection
The AuthService can automatically add bearer tokens to outgoing requests.
auth.addAuthorizationHeader(() => auth.getToken());Unauthorized handling
auth.setUnauthorizedHandler((context) => {
console.warn("user is unauthorized", context.response.status);
});Singleton
The AuthService stores the last created instance as singleton.
import { AuthService } from "fetchservices/auth";
class MyAuthService extends AuthService { ... }
// initialise any AuthService
new MyAuthService();
const auth = AuthService.getInstance();KeyCloakAuthService
The KeyCloakAuthService implements the abstract AuthService for a Keycloak provider and requires keycloak-js as an additional dependency.
Note:
keycloak-jsis an optional dependency. You need to install it only when you want to use this service.
import FetchService from "fetchservices";
import { KeyCloakAuthService } from "fetchservices/auth";
// initialise a new FetchService
const api = new FetchService({ baseURL: "https://api.example.com" });
// initialise Keycloak based AuthService with the FetchService
const auth = new KeyCloakAuthService(api, {
url: "https://auth.example.com/auth",
realm: "myrealm",
clientId: "my-client",
minValidity: 30,
});
await auth.connectClient({ redirectUri: window.location.origin }, true);Controllers
The package also exports controller base classes for structured request handling. A controller provides a way to organise and cluster API endpoints based on entities.
BaseController
Extend the abstract BaseController class to create a structured class for api requests.
import FetchService from "fetchservices";
import { BaseController } from "fetchservices/controller";
class MyController extends BaseController {
fetchSomething() {
return this.api.get("/items");
}
}CrudController
This is a simple CRUD controller for REST endpoints that already implement basic CRUD operations.
import FetchService from "fetchservices";
import { BaseController } from "fetchservices/controller";
class UserController extends CrudController<{ id: number; name: string }> {}
// initialise a new FetchService
const api = new FetchService({ baseURL: "https://api.example.com" });
// initialise UserController with the FetchService
const users = new UserController(api, "/users");
await users.create({ id: 0, name: "Alice" });
await users.read(1);
await users.update(1, { id: 1, name: "Bob" });
await users.delete(1);Middleware support
The CrudController and MiddlewareController classes provide hooks for request and response transformation.
import { CrudController } from "fetchservices/controller";
class MyController extends CrudController<MyEntity> {
protected onRequest(entity: MyEntity) {
return { ...entity, timestamp: Date.now() };
}
protected onResponse(dto: any) {
return dto as MyEntity;
}
}Exported modules
FetchService– default HTTP client wrapperFetchInterceptor/AbstractFetchInterceptor– interceptor types and base classLoggingInterceptor– logs requests and responsesAbortInterceptor– adds abort support viaAbortControllerBearerTokenInterceptor– addsAuthorization: BearerheadersETagCacheInterceptor– supports ETag based cachingAuthService– abstract auth service base classKeyCloakAuthService– Keycloak authentication adapterBaseController– controller base class with middleware helpersCrudController– REST-style create/read/update/delete controllerMiddlewareController– response/request transformation helpers
Contributing
Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.
Versioning
We use SemVer for versioning. For the versions available, see the tags on this repository.
License
This project is licensed under the MIT License - see the LICENSE file for details.
