@edgefirst-dev/api-client - v0.2.0
    Preparing search index...

    @edgefirst-dev/api-client - v0.2.0

    @edgefirst-dev/api-client

    The APIClient class provides a flexible and extensible HTTP client for making API requests. It supports common HTTP methods (GET, POST, PUT, PATCH, DELETE) and allows customization of the request and response handling through the before and after interceptor hooks.

    bun add @edgefirst-dev/api-client
    

    Import the APIClient and create a new instance to make API requests.

    import { APIClient } from "@edgefirst-dev/api-client";

    let client = new APIClient("https://api.example.com");
    let response = await client.get("/users/1");

    You can customize the request and response handling by extending the APIClient class and overriding the before and after methods.

    class CustomAPIClient extends APIClient {
    async before(request: Request) {
    // Add a custom header to the request
    request.headers.set("X-Custom-Header", "value");

    return request;
    }

    async after(request: Request, response: Response) {
    if (response.status === 401) {
    // Handle unauthorized error
    throw new Error("Unauthorized");
    }

    return response;
    }
    }

    let client = new CustomAPIClient("https://api.example.com");
    let response = await client.get("/users/1");

    You can also define custom methods in the subclass to encapsulate common API calls.

    import { ObjectParser } from "@edgefirst-dev/data/parser";

    class CustomAPIClient extends APIClient {
    async fetchUserData(id: number) {
    let response = await this.get(`/users/${id}`);
    let data = await response.json();
    return new ObjectParser(data);
    }
    }

    let client = new CustomAPIClient("https://api.example.com");
    let user = await client.fetchUserData(1);
    let userName = user.string("name");

    By overriding the constructor, you can provide a default base URL.

    class CustomAPIClient extends APIClient {
    constructor() {
    super("https://api.example.com");
    }
    }

    let client = new CustomAPIClient();

    You can add interceptors to the APIClient instance to customize the request and response handling.

    let client = new APIClient("https://api.example.com");

    client.interceptors.before.on(async (request) => {
    // Add a custom header to the request
    request.headers.set("X-Custom-Header", "value");

    return request;
    });

    client.interceptors.after.on(async (request, response) => {
    if (response.status === 401) {
    // Handle unauthorized error
    throw new Error("Unauthorized");
    }

    return response;
    });

    You can also remove interceptors using the off method.

    async function beforeInterceptor(request) {
    // Add a custom header to the request
    request.headers.set("X-Custom-Header", "value");

    return request;
    }

    let client = new APIClient("https://api.example.com");

    client.interceptors.before.on(beforeInterceptor); // Add the interceptor
    client.interceptors.before.off(beforeInterceptor); // Remove the interceptor

    The sub-class interceptors run before the instance interceptors.

    class CustomAPIClient extends APIClient {
    async before(request: Request) {
    // Add a custom header to the request
    request.headers.set("X-Custom-Header", "1");

    return request;
    }
    }

    let client = new CustomAPIClient("https://api.example.com");

    client.interceptors.before.on(async (request) => {
    // Add a custom header to the request
    request.headers.set("X-Custom-Header", "2");

    return request;
    });

    Here the X-Custom-Header will be set to 2 in the request because the instance interceptor overrides the header set by the sub-class interceptor.

    You can easily test your API calls using the APIClient with msw to mock the API responses.

    import { http, HttpResponse } from "msw";
    import { setupServer } from "msw/native"; // or "msw/node" or "msw/browser"

    let server = setupServer(
    http.get("https://api.example.com/users/1", (req, res, ctx) => {
    return res(ctx.json({ id: 1, name: "John Doe" }));
    })
    );

    server.listen();

    let client = new APIClient("https://api.example.com");
    let response = await client.get("/users/1");
    let data = await response.json(); // { id: 1, name: "John Doe" }

    The APIClient class never throws an error except for network errors. You can handle API errors by checking the response status code.

    let client = new APIClient("https://api.example.com");

    try {
    let response = await client.get("/users/1");
    if (response.status === 401) {
    // Handle unauthorized error
    throw new Error("Unauthorized");
    }
    // Handle other errors or success
    } catch (error) {
    // Handle network error
    console.error(error);
    }

    Alternatively, you can use the after interceptor to handle common API errors.

    class CustomAPIClient extends APIClient {
    async after(request: Request, response: Response) {
    if (response.status === 401) {
    // Handle unauthorized error
    throw new Error("Unauthorized");
    }

    return response;
    }
    }

    Or you can use the on method to add an interceptor to handle common API errors.

    let client = new APIClient("https://api.example.com");

    client.interceptors.after.on(async (_, response) => {
    if (response.status === 401) {
    // Handle unauthorized error
    throw new Error("Unauthorized");
    }

    return response;
    });

    You can set a timeout for the API requests using the signal option.

    let client = new APIClient("https://api.example.com");
    await client.get("/users/1", { signal: AbortSignal.timeout(5000) });

    See LICENSE