npm package: @immagin/client
Description: Node.js and browser client for the Immagin image processing API
Latest version: 1.0.3

# @immagin/client

Node.js and browser client for the [Immagin](https://immag.in) image processing API.

Zero dependencies. Works everywhere `fetch` is available.

## Install

```bash
npm install @immagin/client
```

## Quick start

```ts
import { Immagin } from '@immagin/client'

const client = new Immagin({ apiKey: 'imk_...' })

// Generate a signed image URL (auto-fetches tenant credentials)
const url = await client.images.url('photos/hero.jpg', [
  { resize: { width: 800, height: 600 } },
])

// Upload an image (auto-detects MIME type)
import { readFileSync } from 'node:fs'
const buffer = readFileSync('photo.jpg')
await client.images.upload(buffer, 'photos/hero.jpg')
```

## Images

### Generate image URLs

Generate signed image URLs for serving processed images. All URLs are automatically signed — the signature locks transformation parameters so they can't be tampered with.

```ts
// Original image
const url = await client.images.url('photo.jpg')

// With transformations
const thumb = await client.images.url('photo.jpg', [
  { resize: { width: 800, height: 600 } },
  { text: { text: '© My Company', position: 'bottom-right', opacity: 0.5 } },
])

// Rotate, blur, grayscale
const edited = await client.images.url('photo.jpg', [
  { rotate: { angle: 90 } },
  { blur: 3 },
  { grayscale: true },
])

// Crop a region
const cropped = await client.images.url('photo.jpg', [
  { crop: { left: 100, top: 50, width: 400, height: 300 } },
])

// Flip, sharpen, adjust colors
const adjusted = await client.images.url('photo.jpg', [
  { flip: true },
  { sharpen: 2 },
  { modulate: { brightness: 1.2, saturation: 0.8 } },
])

// Disable auto-orient (enabled by default)
const raw = await client.images.url('photo.jpg', [
  { autoOrient: false },
  { resize: { width: 800 } },
])

// Custom output format (default is WebP at quality 90)
const jpeg = await client.images.url(
  'photo.jpg',
  [{ resize: { width: 800 } }],
  { format: 'jpeg', quality: 85 },
)
```

> **Note:** `url()` uses `node:crypto` for signing and is intended for server-side use only. Tenant credentials are auto-fetched and cached on initialization.

### Get a signed upload URL

Returns a CloudFront signed URL for direct upload (expires in 5 minutes). Useful for browser uploads where you don't want to expose your API key. The URL points to a CloudFront distribution (not S3 directly), so the underlying infrastructure is not exposed.

```ts
// Server: get the signed upload URL
const { uploadUrl, key } = await client.images.signUrl('photos/hero.jpg')
// Return uploadUrl to the browser

// Browser: upload directly
await fetch(uploadUrl, {
  method: 'PUT',
  body: file,
})
```

### Upload (Node.js)

Convenience method that gets a signed URL and uploads in one call. The file goes directly to CloudFront/S3 and never passes through the API. MIME type is auto-detected from file bytes (magic bytes for JPEG, PNG, GIF, WebP, BMP, AVIF, HEIC, HEIF) or file extension.

```ts
import { readFileSync } from 'node:fs'

const buffer = readFileSync('photo.jpg')
await client.images.upload(buffer, 'uploads/photo.jpg')
```

### List

```ts
const result = await client.images.list()
// { images: [{ key, size, lastModified }], nextCursor? }

// With pagination
const page = await client.images.list({
  prefix: 'uploads/',
  limit: 50,
  cursor: result.nextCursor,
})
```

### Delete

```ts
await client.images.delete('uploads/photo.jpg')
```

### Metadata

Inspect image properties (dimensions, format, color space, etc.) without downloading the full image. The request goes through Luna, which extracts metadata using Sharp.

```ts
const meta = await client.images.metadata('photo.jpg')
// {
//   width: 1920,
//   height: 1080,
//   format: 'jpeg',
//   space: 'srgb',
//   channels: 3,
//   hasAlpha: false,
//   density: 72,
//   isProgressive: false,
//   size: 204800,
// }
```

> **Note:** `metadata()` uses `node:crypto` for signing and is intended for server-side use only.

## API Keys

Manage API keys programmatically.

### Create

```ts
const result = await client.keys.create({ name: 'Production' })
// { key: 'imk_...', prefix: 'imk_abcd', name: 'Production' }
// Save the key - it won't be shown again
```

### List

```ts
const keys = await client.keys.list()
// [{ id, name, keyPrefix, createdAt, lastUsedAt }]
```

### Revoke

```ts
await client.keys.revoke(keyId)
```

## Error handling

All API errors throw `ImmaginError` with the HTTP status and response body.

```ts
import { Immagin, ImmaginError } from '@immagin/client'

try {
  await client.images.delete('missing.jpg')
} catch (err) {
  if (err instanceof ImmaginError) {
    console.log(err.status)  // 404
    console.log(err.message) // "Not found"
  }
}
```

## Configuration

```ts
const client = new Immagin({
  apiKey: 'imk_...',              // Required
  baseUrl: 'https://...',         // Optional, defaults to https://gateway.immag.in
})
```

## License

MIT