@open-kingdom/shared-frontend-data-access-external-api

v0.0.2-16

Published

A unified integration layer for connecting any third-party REST API to the application's Redux store via RTK Query. The library provides `createAxiosBaseQuery` — an Axios-backed RTK Query base query factory — as the foundation for adding new external API

Readme

@open-kingdom/shared-frontend-data-access-external-api

A unified integration layer for connecting any third-party REST API to the application's Redux store via RTK Query. The library provides createAxiosBaseQuery — an Axios-backed RTK Query base query factory — as the foundation for adding new external API integrations. The Cat Facts API is included as a reference implementation demonstrating the expected pattern; it is not the purpose of the library.


Adding a New External API

In your application, import createAxiosBaseQuery and use it as the baseQuery for a new RTK Query createApi instance. This lives in your app's own code — not inside this library.

// src/lib/weather-api/weather.api.ts (in your application)
import { createApi } from '@reduxjs/toolkit/query/react';
import { createAxiosBaseQuery } from '@open-kingdom/shared-frontend-data-access-external-api';

export const weatherApi = createApi({
  reducerPath: 'weatherApi',
  baseQuery: createAxiosBaseQuery({
    baseUrl: 'https://api.openweathermap.org/data/2.5',
    prepareHeaders: (headers) => {
      headers.set('X-Api-Key', process.env.VITE_WEATHER_API_KEY ?? '');
      return headers;
    },
  }),
  endpoints: (builder) => ({
    getCurrentWeather: builder.query<WeatherResponse, string>({
      query: (city) => ({ url: '/weather', params: { q: city } }),
    }),
  }),
});

export const WeatherApiKey = weatherApi.reducerPath;
export const weatherApiReducer = weatherApi.reducer;
export const weatherApiMiddleware = weatherApi.middleware;
export const { useGetCurrentWeatherQuery } = weatherApi;

Register the reducer and middleware in your store:

// src/store.ts (in your application)
import { configureStore } from '@reduxjs/toolkit';
import { WeatherApiKey, weatherApiReducer, weatherApiMiddleware } from './lib/weather-api/weather.api';

export const store = configureStore({
  reducer: {
    [WeatherApiKey]: weatherApiReducer,
    // ...other reducers
  },
  middleware: (getDefault) => getDefault().concat(weatherApiMiddleware),
});

Then use the generated hook anywhere in your components:

import { useGetCurrentWeatherQuery } from './lib/weather-api/weather.api';

function WeatherWidget({ city }: { city: string }) {
  const { data, isLoading, error } = useGetCurrentWeatherQuery(city);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Could not load weather</p>;
  return <p>{data?.weather[0].description}</p>;
}

Exports

Base Query Factory

| Export | Type | Description | | ------------------------------ | ----------------------------------------------- | ----------------------------------------------------- | | createAxiosBaseQuery(config) | (config: AxiosBaseQueryConfig) => BaseQueryFn | Creates an RTK Query baseQuery function using Axios | | AxiosBaseQueryConfig | interface | Configuration for the Axios base query | | AxiosBaseQueryArgs | interface | Per-request arguments passed to the query | | AxiosBaseQueryError | interface | Shape of errors returned on Axios failures | | AxiosBaseQueryMeta | interface | Shape of response metadata |

Cat Facts API (reference implementation)

| Export | Type | Description | | ----------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | catFactsApi | Api<...> | RTK Query createApi instance for https://catfact.ninja, reducerPath: 'catFactsApi' | | CatFactsApiKey | 'catFactsApi' | The reducer path string constant | | catFactsApiReducer | Reducer | catFactsApi.reducer | | catFactsApiMiddleware | Middleware | catFactsApi.middleware | | useGetFactQuery | QueryHook<CatFact, void> | RTK Query hook for GET /fact | | useGetFactsQuery | QueryHook<PaginatedResponse<CatFact>, PaginationParams \| void> | RTK Query hook for GET /facts | | useGetBreedsQuery | QueryHook<PaginatedResponse<Breed>, PaginationParams \| void> | RTK Query hook for GET /breeds |


Type Definitions

AxiosBaseQueryConfig

| Property | Type | Required | Default | Description | | ---------------- | ------------------------------------------------------------ | -------- | ------- | ---------------------------------------------------------------------- | | baseUrl | string | Yes | — | The base URL for all requests made by this query function | | prepareHeaders | (headers: AxiosHeaders, api: BaseQueryApi) => AxiosHeaders | No | — | Optional hook to inject headers (e.g. auth tokens) before each request |

AxiosBaseQueryArgs

| Property | Type | Required | Default | Description | | --------- | ------------------------------ | -------- | ------- | ---------------------------- | | url | string | Yes | — | Path appended to baseUrl | | method | AxiosRequestConfig['method'] | No | 'GET' | HTTP method | | params | Record<string, unknown> | No | — | URL query parameters | | body | unknown | No | — | Request body | | headers | Record<string, string> | No | — | Per-request header overrides |

Additional properties are passed through to the Axios config object.

AxiosBaseQueryError

| Property | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------- | | status | number | No | HTTP status code | | data | unknown | No | Response body from the failed request | | headers | unknown | No | Response headers |

AxiosBaseQueryMeta

| Property | Type | Required | Description | | --------- | -------------------- | -------- | ------------------------------------- | | headers | unknown | No | Response headers | | status | number | No | HTTP status code | | config | AxiosRequestConfig | No | The Axios config used for the request |

CatFact

| Property | Type | Description | | -------- | -------- | ---------------------------- | | fact | string | The fact text | | length | number | Character length of the fact |

Breed

| Property | Type | Description | | --------- | -------- | ------------------ | | breed | string | Breed name | | country | string | Country of origin | | origin | string | Origin description | | coat | string | Coat type | | pattern | string | Coat pattern |

PaginatedResponse<T>

| Property | Type | Description | | -------------- | -------- | -------------------------------------- | | current_page | number | Current page number | | data | T[] | Array of items for the current page | | last_page | number | Total number of pages | | total | number | Total number of items across all pages |

PaginationParams

| Property | Type | Required | Description | | -------- | -------- | -------- | ------------------------ | | page | number | No | Page number to fetch | | limit | number | No | Number of items per page |

Additional properties are forwarded to the query string.


Setup

Adding the Cat Facts reference API to the store

import { configureStore } from '@reduxjs/toolkit';
import { CatFactsApiKey, catFactsApiReducer, catFactsApiMiddleware } from '@open-kingdom/shared-frontend-data-access-external-api';

export const store = configureStore({
  reducer: {
    [CatFactsApiKey]: catFactsApiReducer, // 'catFactsApi'
  },
  middleware: (getDefault) => getDefault().concat(catFactsApiMiddleware),
});

Usage Examples

Fetch a random cat fact

import { useGetFactQuery } from '@open-kingdom/shared-frontend-data-access-external-api';

function CatFactWidget() {
  const { data, isLoading, error } = useGetFactQuery();

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error loading fact</p>;
  return <p>{data?.fact}</p>;
}

Fetch paginated facts

import { useGetFactsQuery } from '@open-kingdom/shared-frontend-data-access-external-api';

function CatFactsList() {
  const { data } = useGetFactsQuery({ page: 1, limit: 10 });

  return (
    <ul>
      {data?.data.map((item, i) => (
        <li key={i}>{item.fact}</li>
      ))}
    </ul>
  );
}

Fetch cat breeds

import { useGetBreedsQuery } from '@open-kingdom/shared-frontend-data-access-external-api';

function BreedList() {
  const { data } = useGetBreedsQuery({ page: 1, limit: 25 });

  return (
    <ul>
      {data?.data.map((breed) => (
        <li key={breed.breed}>
          {breed.breed} — {breed.country}
        </li>
      ))}
    </ul>
  );
}

Testing

nx test shared-frontend-data-access-external-api