# Node.js SDK

Official Node.js/TypeScript SDK for KeyEnv.

Source: https://keyenv.dev/docs/sdks/nodejs/

Official Node.js SDK for KeyEnv with full TypeScript support.

## Installation

```bash
npm install @keyenv/node
```

Or with other package managers:

```bash
yarn add @keyenv/node
pnpm add @keyenv/node
```

## Quick Start

```typescript
import { KeyEnv } from '@keyenv/node';

const client = new KeyEnv({
  token: process.env.KEYENV_TOKEN!,
});

// Load secrets into process.env
await client.loadEnv('your-project-id', 'production');
console.log(process.env.DATABASE_URL);
```

## Initialize the Client

```typescript
import { KeyEnv } from '@keyenv/node';

const client = new KeyEnv({
  token: 'your-service-token',
  timeout: 30000, // optional, default 30s
});
```

## Loading Secrets

### Load into process.env

The simplest way to use secrets in your application:

```typescript
const count = await client.loadEnv('project-id', 'production');
console.log(`Loaded ${count} secrets`);

// Now use them
console.log(process.env.DATABASE_URL);
console.log(process.env.API_KEY);
```

### Export as Object

Get secrets as a key-value object:

```typescript
const env = await client.exportSecretsAsObject('project-id', 'production');
console.log(env.DATABASE_URL);
console.log(env.API_KEY);
```

### Export as Array

Get secrets with metadata:

```typescript
const secrets = await client.exportSecrets('project-id', 'production');
for (const secret of secrets) {
  console.log(`${secret.key}=${secret.value}`);
}
```

## Managing Secrets

### Get a Single Secret

```typescript
const secret = await client.getSecret('project-id', 'production', 'DATABASE_URL');
console.log(secret.value);
console.log(secret.description);
```

### Set a Secret

Creates or updates a secret:

```typescript
await client.setSecret('project-id', 'production', 'API_KEY', 'sk_live_...');

// With description
await client.setSecret('project-id', 'production', 'API_KEY', 'sk_live_...', 'Production API key');
```

### Delete a Secret

```typescript
await client.deleteSecret('project-id', 'production', 'OLD_KEY');
```

## Bulk Operations

### Bulk Import

Import multiple secrets at once:

```typescript
const result = await client.bulkImport('project-id', 'development', [
  { key: 'DATABASE_URL', value: 'postgres://localhost/mydb' },
  { key: 'REDIS_URL', value: 'redis://localhost:6379' },
  { key: 'API_KEY', value: 'sk_test_...', description: 'Test API key' },
], { overwrite: true });

console.log(`Created: ${result.created}, Updated: ${result.updated}`);
```

### Generate .env File

```typescript
import { writeFileSync } from 'fs';

const envContent = await client.generateEnvFile('project-id', 'production');
writeFileSync('.env', envContent);
```

## Projects & Environments

### List Projects

```typescript
const projects = await client.listProjects();
for (const project of projects) {
  console.log(`${project.name} (${project.id})`);
}
```

### Get Project Details

```typescript
const project = await client.getProject('project-id');
console.log(`Project: ${project.name}`);
for (const env of project.environments) {
  console.log(`  - ${env.name}`);
}
```

### List Environments

```typescript
const environments = await client.listEnvironments('project-id');
for (const env of environments) {
  console.log(env.name);
}
```

## Environment Permissions

Manage who can access secrets in each environment.

### List Permissions

```typescript
const permissions = await client.listPermissions('project-id', 'production');
for (const perm of permissions) {
  console.log(`${perm.user_email}: ${perm.role}`);
}
```

### Set Permission

```typescript
// Grant write access to a user
await client.setPermission('project-id', 'production', 'user-id', 'write');
```

### Delete Permission

```typescript
await client.deletePermission('project-id', 'production', 'user-id');
```

### Get My Permissions

```typescript
const { permissions, is_team_admin } = await client.getMyPermissions('project-id');
for (const perm of permissions) {
  console.log(`${perm.environment_name}: ${perm.role} (can_write: ${perm.can_write})`);
}
```

### Bulk Set Permissions

```typescript
await client.bulkSetPermissions('project-id', 'production', [
  { userId: 'user-1', role: 'write' },
  { userId: 'user-2', role: 'read' },
]);
```

### Project Defaults

```typescript
// Get default permissions
const defaults = await client.getProjectDefaults('project-id');

// Set default permissions for new team members
await client.setProjectDefaults('project-id', [
  { environmentName: 'development', defaultRole: 'write' },
  { environmentName: 'staging', defaultRole: 'read' },
  { environmentName: 'production', defaultRole: 'none' },
]);
```

## Error Handling

```typescript
import { KeyEnv, KeyEnvError } from '@keyenv/node';

try {
  await client.getSecret('project-id', 'production', 'MISSING_KEY');
} catch (error) {
  if (error instanceof KeyEnvError) {
    console.error(`Error ${error.status}: ${error.message}`);

    if (error.status === 401) {
      console.error('Invalid or expired token');
    } else if (error.status === 403) {
      console.error('Access denied');
    } else if (error.status === 404) {
      console.error('Secret not found');
    }
  }
}
```

## TypeScript Types

The SDK exports all types for TypeScript users:

```typescript
import type {
  Secret,
  SecretWithValue,
  Project,
  Environment,
  BulkImportResult
} from '@keyenv/node';
```

## API Reference

### Constructor Options

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `token` | `string` | Yes | - | Service token |
| `timeout` | `number` | No | `30000` | Request timeout (ms) |

### Methods

| Method | Description |
|--------|-------------|
| `getCurrentUser()` | Get current user/token info |
| `listProjects()` | List all accessible projects |
| `getProject(id)` | Get project with environments |
| `listEnvironments(projectId)` | List environments in a project |
| `listSecrets(projectId, env)` | List secret keys (no values) |
| `exportSecrets(projectId, env)` | Export secrets with values |
| `exportSecretsAsObject(projectId, env)` | Export as key-value object |
| `getSecret(projectId, env, key)` | Get single secret |
| `setSecret(projectId, env, key, value)` | Create or update secret |
| `deleteSecret(projectId, env, key)` | Delete secret |
| `bulkImport(projectId, env, secrets)` | Bulk import secrets |
| `loadEnv(projectId, env)` | Load secrets into process.env |
| `generateEnvFile(projectId, env)` | Generate .env file content |
| `listPermissions(projectId, env)` | List permissions for an environment |
| `setPermission(projectId, env, userId, role)` | Set user's permission |
| `deletePermission(projectId, env, userId)` | Delete user's permission |
| `bulkSetPermissions(projectId, env, permissions)` | Bulk set permissions |
| `getMyPermissions(projectId)` | Get current user's permissions |
| `getProjectDefaults(projectId)` | Get default permissions |
| `setProjectDefaults(projectId, defaults)` | Set default permissions |

## Examples

### Express.js Application

```typescript
import express from 'express';
import { KeyEnv } from '@keyenv/node';

async function main() {
  // Load secrets before starting server
  const client = new KeyEnv({ token: process.env.KEYENV_TOKEN! });
  await client.loadEnv(process.env.KEYENV_PROJECT!, 'production');

  const app = express();

  app.get('/', (req, res) => {
    res.json({ status: 'ok' });
  });

  app.listen(process.env.PORT || 3000);
}

main();
```

### Next.js API Route

```typescript
// pages/api/config.ts
import { KeyEnv } from '@keyenv/node';

let secretsLoaded = false;

export default async function handler(req, res) {
  if (!secretsLoaded) {
    const client = new KeyEnv({ token: process.env.KEYENV_TOKEN! });
    await client.loadEnv(process.env.KEYENV_PROJECT!, 'production');
    secretsLoaded = true;
  }

  res.json({
    apiUrl: process.env.API_URL,
    // Don't expose sensitive secrets!
  });
}
```
