No description
Find a file
autocommit 764b675653
Some checks failed
Build and Publish / build-and-publish (push) Failing after 44s
deps-upgrade(deps): ⬆️ Update dependency versions in package.json for minor/patch upgrades
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 04:31:03 -07:00
.forgejo/workflows chore: initial commit 2026-01-21 12:01:07 -08:00
.turbo chore: initial commit 2026-01-21 12:01:07 -08:00
node_modules chore: initial commit 2026-01-21 12:01:07 -08:00
src chore(api-client): 🔧 Enhance create-api-client with new features, test coverage, and documentation updates 2026-01-22 15:31:35 -08:00
.gitignore chore(gitignore): Add missing patterns 2026-01-21 13:26:02 -08:00
CHANGELOG.md chore(api-client): 🔧 Enhance create-api-client with new features, test coverage, and documentation updates 2026-01-22 15:31:35 -08:00
eslint.config.js chore: initial commit 2026-01-21 12:01:07 -08:00
package.json deps-upgrade(deps): ⬆️ Update dependency versions in package.json for minor/patch upgrades 2026-06-10 04:31:03 -07:00
README.md chore: trigger CI publish 2026-01-30 16:00:59 -08:00
tsconfig.json chore: initial commit 2026-01-21 12:01:07 -08:00
tsconfig.tsbuildinfo chore: initial commit 2026-01-21 12:01:07 -08:00
tsup.config.ts chore(build): Optimize tsup config for faster builds via plugin tuning, minification, and asset optimization 2026-01-21 15:29:15 -08:00

@lilith/http-client

Minimal HTTP client for the Lilith platform. This is a thin wrapper around Axios that provides transport layer functionality only.

Purpose

Provides a simple, configurable HTTP client factory without authentication concerns. Authentication is handled separately by @lilith/http-auth-interceptor.

Features

  • Simple Axios instance factory
  • Configurable base URL, timeout, and headers
  • TypeScript support
  • Composable with middleware (auth, logging, retry, etc.)

Installation

pnpm add @lilith/http-client

Usage

Basic Usage (No Authentication)

import { createApiClient } from '@lilith/http-client';

const client = createApiClient({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

const response = await client.get('/users');

With Authentication

For authentication, use @lilith/http-auth-interceptor:

import { createApiClient } from '@lilith/http-client';
import { createAuthInterceptor } from '@lilith/http-auth-interceptor';

// Create HTTP client
const client = createApiClient({
  baseURL: 'https://api.example.com'
});

// Add authentication interceptor
createAuthInterceptor(client, {
  getToken() {
    return localStorage.getItem('auth_token');
  },
  async refreshToken() {
    const refresh = localStorage.getItem('refresh_token');
    const response = await fetch('/auth/refresh', {
      method: 'POST',
      body: JSON.stringify({ refreshToken: refresh })
    });
    const data = await response.json();
    localStorage.setItem('auth_token', data.accessToken);
    return data.accessToken;
  },
  onRefreshFailed() {
    localStorage.clear();
    window.location.href = '/login';
  }
});

// Now all requests include auth token and handle 401 automatically
const response = await client.get('/users');

With Custom Interceptors

const client = createApiClient({
  baseURL: 'https://api.example.com'
});

// Add request logging
client.interceptors.request.use((config) => {
  console.log('Request:', config.method, config.url);
  return config;
});

// Add response logging
client.interceptors.response.use(
  (response) => {
    console.log('Response:', response.status);
    return response;
  },
  (error) => {
    console.error('Error:', error.message);
    throw error;
  }
);

Configuration

ApiClientConfig

interface ApiClientConfig {
  /**
   * Base URL for API requests
   * @default process.env.VITE_API_URL || 'http://localhost:4000/api'
   */
  baseURL?: string;

  /**
   * Request timeout in milliseconds
   * @default 10000
   */
  timeout?: number;

  /**
   * Default headers to include with every request
   * @default { 'Content-Type': 'application/json' }
   */
  headers?: Record<string, string>;
}

Architecture

┌─────────────────────────────────────┐
│  Application Code                   │
│  (import { createApiClient })       │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│  @lilith/http-client                │
│  (Transport: Axios wrapper)         │
└──────────────┬──────────────────────┘
               │
               ▼  (optional)
┌─────────────────────────────────────┐
│  @lilith/http-auth-interceptor      │
│  (Auth: Token injection + refresh)  │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│  axios                               │
│  (HTTP library)                     │
└─────────────────────────────────────┘

Migration from v1.x

Before (v1.x - Auth Built-In)

import { createApiClient } from '@lilith/http-client';

const client = createApiClient({
  baseURL: 'https://api.example.com',
  tokenStorageKey: 'auth_token',
  refreshTokenStorageKey: 'refresh_token',
  enableTokenRefresh: true,
  handle401Redirects: true,
});

After (v2.x - Layered Architecture)

import { createApiClient } from '@lilith/http-client';
import { createAuthInterceptor } from '@lilith/http-auth-interceptor';

const client = createApiClient({
  baseURL: 'https://api.example.com'
});

createAuthInterceptor(client, {
  getToken: () => localStorage.getItem('auth_token'),
  refreshToken: async () => {
    const refresh = localStorage.getItem('refresh_token');
    const response = await fetch('/auth/refresh', {
      method: 'POST',
      body: JSON.stringify({ refreshToken: refresh })
    });
    const data = await response.json();
    localStorage.setItem('auth_token', data.accessToken);
    return data.accessToken;
  },
  onRefreshFailed: () => {
    localStorage.clear();
    window.location.href = '/login';
  }
});

Breaking Changes

  1. Removed auth configuration options: tokenStorageKey, refreshTokenStorageKey, enableTokenRefresh, handle401Redirects, loginRoute, onTokenRefresh, onRefreshFailed
  2. Removed custom interceptor options: onRequest, onResponseError, enableLogging
  3. Simplified to transport only: Use @lilith/http-auth-interceptor for authentication

Error Handling

Error types are still exported from @lilith/http-client:

import { isApiError, getErrorMessage } from '@lilith/http-client';

try {
  await client.post('/users', userData);
} catch (error) {
  if (isApiError(error)) {
    console.error(error.response?.data.message);
  }
  toast.error(getErrorMessage(error));
}

License

MIT