npm package: @stagewise/api-client
Description: Type-safe API client for the Stagewise platform, powered by Eden Treaty
Latest version: 4.0.0

# @stagewise/api-client

A type-safe Node.js / browser client for the Stagewise API, powered by [Eden Treaty](https://elysiajs.com/eden/treaty) and [ElysiaJS](https://elysiajs.com).

Every route, parameter, header, and response body is **fully typed** — you get autocomplete and compile-time safety with zero code generation.

## Installation

```bash
npm install @stagewise/api-client
# or
pnpm add @stagewise/api-client
# or
yarn add @stagewise/api-client
```

## Quick Start

```typescript
import { createApiClient } from '@stagewise/api-client';

const api = createApiClient('https://api.stagewise.io');

// Health check
const { data } = await api.v1.health.get();
console.log(data); // { status: 'ok', timestamp: '...' }
```

## Usage

### Creating a Client

```typescript
import { createApiClient } from '@stagewise/api-client';

const api = createApiClient('https://api.stagewise.io', {
  // Static headers
  headers: {
    Authorization: 'Bearer <token>',
  },
});
```

### Dynamic Headers

Pass a function to compute headers per-request (useful for auth tokens that refresh):

```typescript
const api = createApiClient('https://api.stagewise.io', {
  headers: () => ({
    Authorization: `Bearer ${getAccessToken()}`,
  }),
});
```

### Custom Fetch

Provide your own `fetch` implementation (handy for testing or server-side usage):

```typescript
const api = createApiClient('https://api.stagewise.io', {
  fetch: customFetch,
});
```

## API Namespaces

All endpoints live under the **`v1`** namespace, mirroring the backend route structure:

```typescript
const api = createApiClient('https://api.stagewise.io');

// All calls go through api.v1.*
api.v1.health.get();
api.v1.billing.plan.get();
api.v1.ai.chat.completions.post({ ... });
```

### Health

```typescript
// Basic health check
const { data } = await api.v1.health.get();

// Database health check
const { data: dbHealth } = await api.v1.health.db.get();
```

### AI — Chat Completions & Image Generation

```typescript
// Chat completion
const { data } = await api.v1.ai.chat.completions.post({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: 'Hello!' }],
  stream: false,
});

// Image generation
const { data: image } = await api.v1.ai.images.generations.post({
  model: 'dall-e-3',
  prompt: 'A futuristic city skyline',
});
```

### Billing

```typescript
// Get current plan
const { data: plan } = await api.v1.billing.plan.get();

// Get available prices
const { data: prices } = await api.v1.billing.prices.get();

// Create checkout session
const { data: checkout } = await api.v1.billing.checkout.post({
  plan: 'pro',
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel',
});

// Open customer portal
const { data: portal } = await api.v1.billing.portal.post({
  returnUrl: 'https://example.com/settings',
});

// Get prepaid credit balance
const { data: credits } = await api.v1.billing.credits.get();

// Purchase extra credits
const { data: purchase } = await api.v1.billing.credits.purchase.post({
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel',
});

// List invoices
const { data: invoices } = await api.v1.billing.invoices.get();
```

### Usage

```typescript
// Current usage stats
const { data: current } = await api.v1.usage.current.get();

// Plan limits
const { data: limits } = await api.v1.usage.limits.get();

// Usage history (last 30 days)
const { data: history } = await api.v1.usage.history.get({
  query: { days: 30 },
});
```

### Inspiration

```typescript
// List inspiration websites (paginated, filterable)
const { data: sites } = await api.v1.inspiration.index.get({
  query: { limit: 20, offset: 0 },
});

// List all tags
const { data: tags } = await api.v1.inspiration.tags.get();
```

### Context7

```typescript
// Search libraries
const { data: results } = await api.v1.context7.search.get({
  query: { query: 'react state management' },
});

// Get documentation
const { data: docs } = await api.v1.context7.docs.get({
  query: { libraryId: '...', topic: 'hooks' },
});
```

## Types

The client is fully typed. Import types directly:

```typescript
import type { App, ApiClient } from '@stagewise/api-client';

// ApiClient is the full treaty client type
// App is the raw ElysiaJS app type (for advanced use cases)
```

Every call returns `{ data, error, status }` — Eden Treaty's standard response shape. TypeScript will narrow `data` and `error` based on the status code:

```typescript
const { data, error } = await api.v1.billing.plan.get();

if (error) {
  // error is typed per status code
  console.error(error);
  return;
}

// data is fully typed here
console.log(data.plan, data.status);
```

## Error Handling

Eden Treaty returns errors as values rather than throwing:

```typescript
const { data, error, status } = await api.v1.billing.plan.get();

if (error) {
  switch (status) {
    case 401:
      console.error('Unauthorized');
      break;
    case 403:
      console.error('Forbidden');
      break;
    case 429:
      console.error('Rate limited');
      break;
    default:
      console.error('Unexpected error:', error);
  }
  return;
}

// data is safely narrowed
console.log(data);
```

## Migration from v2 (tRPC)

Version 3 replaces the tRPC-based client with Eden Treaty. Key differences:

| v2 (tRPC) | v3 (Eden Treaty) |
|---|---|
| `client.health.query()` | `api.v1.health.get()` |
| `client.agent.streamText.mutate({...})` | `api.v1.ai.chat.completions.post({...})` |
| Throws `TRPCClientError` | Returns `{ data, error, status }` |
| `createNodeApiClient()` | `createApiClient(baseUrl)` |
| `API_URL` / `API_ACCESS_KEY` env vars | Pass `baseUrl` and `headers` explicitly |

## License

MIT