Skip to main content

Overview

@empathyco/x-adapter is a utility library for creating type-safe API clients. It provides tools to connect with any API endpoint, transform requests into API-compatible formats, make HTTP requests, and transform responses back into desired shapes.

Installation

npm install @empathyco/x-adapter

Main Exports

Endpoint Adapter

Core functionality for creating API adapters.
endpointAdapterFactory
function
Factory function to create endpoint adapters with custom options
EndpointAdapter
interface
Interface for adapters that connect with API endpoints
ExtendableEndpointAdapter
interface
Endpoint adapter with extend functionality for composition

HTTP Clients

HTTP client implementations for making requests.
fetchHttpClient
function
HTTP client using the native Fetch API
HttpClient
interface
Interface for HTTP client implementations

Mappers

Utilities for transforming requests and responses.
identityMapper
function
Pass-through mapper that returns the input unchanged
Mapper
interface
Interface for mapper functions that transform data

Schemas

Schema validation utilities.
schemaValidator
function
Validates data against defined schemas

Creating an Adapter

Basic Example

import { endpointAdapterFactory } from '@empathyco/x-adapter'

interface SearchRequest {
  query: string
  rows: number
}

interface SearchResponse {
  results: Array<{ id: string; title: string }>
  totalResults: number
}

const searchAdapter = endpointAdapterFactory<SearchRequest, SearchResponse>({
  endpoint: 'https://api.example.com/search',
  requestMapper: (request) => ({
    q: request.query,
    size: request.rows
  }),
  responseMapper: (response) => ({
    results: response.items,
    totalResults: response.total
  })
})

// Use the adapter
const response = await searchAdapter({
  query: 'shoes',
  rows: 20
})

Endpoint Adapter Options

endpoint
string | Mapper<Request, string>
The API endpoint URL. Can be a static string or a function that generates the URL from the request
httpClient
HttpClient
The HTTP client to use for requests. Defaults to fetchHttpClient
defaultRequestOptions
RequestOptions
Default options applied to every request (headers, method, etc.)
requestMapper
Mapper<Request, Record<string, any>>
Function to transform the request object before sending to the API
responseMapper
Mapper<any, Response>
Function to transform the API response into the desired format

Advanced Usage

Dynamic Endpoints

const productAdapter = endpointAdapterFactory({
  endpoint: (request) => `https://api.example.com/products/${request.productId}`,
  responseMapper: (response) => ({
    ...response,
    price: parseFloat(response.price)
  })
})

Extending Adapters

const baseAdapter = endpointAdapterFactory({
  endpoint: 'https://api.example.com',
  defaultRequestOptions: {
    headers: {
      'Content-Type': 'application/json'
    }
  }
})

// Extend with authentication
const authenticatedAdapter = baseAdapter.extends({
  defaultRequestOptions: {
    headers: {
      'Authorization': 'Bearer token123'
    }
  }
})

Custom Request Mapper

const searchAdapter = endpointAdapterFactory({
  endpoint: '/api/search',
  requestMapper: (request) => {
    const params: Record<string, any> = {
      q: request.query
    }
    
    if (request.filters) {
      params.filters = request.filters.map(f => f.id).join(',')
    }
    
    if (request.sort) {
      params.sort = request.sort
    }
    
    return params
  }
})

Response Transformation

const facetsAdapter = endpointAdapterFactory({
  endpoint: '/api/facets',
  responseMapper: (response) => ({
    facets: response.facets.map(facet => ({
      id: facet.facet_id,
      label: facet.facet_name,
      filters: facet.values.map(value => ({
        id: value.value_id,
        label: value.value_name,
        totalResults: value.count,
        selected: false
      }))
    }))
  })
})

Type Definitions

EndpointAdapter

interface EndpointAdapter<Request, Response> {
  (request: Request, options?: RequestOptions): Promise<Response>
}

ExtendableEndpointAdapter

interface ExtendableEndpointAdapter<Request, Response>
  extends EndpointAdapter<Request, Response> {
  extends: <NewRequest = Request, NewResponse = Response>(
    options: Partial<EndpointAdapterOptions<NewRequest, NewResponse>>
  ) => ExtendableEndpointAdapter<NewRequest, NewResponse>
}

Mapper

type Mapper<Input, Output> = (
  input: Input,
  context?: Record<string, any>
) => Output

RequestOptions

interface RequestOptions {
  endpoint?: string
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
  headers?: Record<string, string>
  parameters?: Record<string, any>
}

Error Handling

try {
  const response = await searchAdapter({
    query: 'shoes',
    rows: 20
  })
} catch (error) {
  if (error.message === 'Tried to make a request without an endpoint') {
    // Handle missing endpoint
  }
  // Handle other errors
}

Dependencies

The package depends on:
  • @empathyco/x-deep-merge - Deep merging utilities
  • @empathyco/x-utils - General utility functions

Resources

GitHub Repository

View source code

x-components

See how adapters integrate with components

Build docs developers (and LLMs) love