Best Practices

Recommended patterns and practices for the LegionEdge Platform

This guide covers recommended patterns for security, performance, error handling, and reliability when building on the LegionEdge Platform.

Security

Use Environment-Specific API Keys

Never use production keys in development. Create separate keys for each environment:

// Each environment should have its own key
const client = new LegionEdge({
  apiKey: process.env.LEGIONEDGE_API_KEY!, // different per environment
  environment: process.env.LEGIONEDGE_ENV as "development" | "staging" | "production",
});

Minimize API Key Scopes

Grant only the permissions each key needs:

// A key for a read-only dashboard
const dashboardKey = await client.apiKeys.create({
  organizationId: orgId,
  name: "Dashboard Read-Only",
  scopes: ["projects:read", "resources:read", "deployments:read"],
  expiresIn: "30d",
});

Rotate Keys Regularly

Set up automated key rotation:

const rotated = await client.apiKeys.rotate(keyId, {
  gracePeriod: "24h",
});
// Update your secrets manager with rotated.plaintext

Validate Webhook Signatures

Always verify webhook payloads before processing them:

import { verifyWebhookSignature } from "@legionedge/sdk";

function handleWebhook(req: Request) {
  const isValid = verifyWebhookSignature({
    payload: req.body,
    signature: req.headers["x-legionedge-signature"] as string,
    secret: process.env.WEBHOOK_SECRET!,
  });

  if (!isValid) {
    throw new Error("Invalid webhook signature");
  }
}

Error Handling

Use Typed Error Handling

The SDK provides typed errors for each error category:

import {
  LegionEdge,
  LegionEdgeError,
  RateLimitError,
  AuthenticationError,
  NotFoundError,
} from "@legionedge/sdk";

try {
  await client.projects.get(projectId);
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${err.retryAfter}s`);
  } else if (err instanceof AuthenticationError) {
    console.error("Authentication failed. Check your API key.");
  } else if (err instanceof NotFoundError) {
    console.error(`Project ${projectId} does not exist.`);
  } else if (err instanceof LegionEdgeError) {
    console.error(`[${err.code}] ${err.message}`);
  }
}

Configure Retries

Enable automatic retries for transient errors:

const client = new LegionEdge({
  apiKey: process.env.LEGIONEDGE_API_KEY!,
  retries: {
    maxRetries: 3,
    backoffMultiplier: 2,
    retryableStatusCodes: [429, 502, 503, 504],
  },
});

Rate Limiting

Respect Rate Limits

Monitor rate limit headers and throttle requests accordingly:

const client = new LegionEdge({
  apiKey: process.env.LEGIONEDGE_API_KEY!,
  rateLimiting: {
    enabled: true,
    strategy: "adaptive", // automatically slows down near the limit
  },
});

Batch Operations

Use batch endpoints when operating on multiple resources:

// Instead of individual calls in a loop
const results = await client.projects.batchGet([
  "proj_abc",
  "proj_def",
  "proj_ghi",
]);

Caching

Cache Frequently Accessed Data

Use the SDK's built-in cache for read-heavy workloads:

const client = new LegionEdge({
  apiKey: process.env.LEGIONEDGE_API_KEY!,
  cache: {
    enabled: true,
    ttl: 60_000,        // 60 seconds
    maxEntries: 1000,
  },
});

Performance

Use Pagination Efficiently

Always paginate large result sets and avoid fetching more data than needed:

// Fetch only what you need
const activeProjects = await client.projects.list({
  organizationId: orgId,
  status: "active",
  perPage: 10,
  sort: "updated_at",
  order: "desc",
});

Select Specific Fields

Reduce payload size by requesting only the fields you need:

const projects = await client.projects.list({
  organizationId: orgId,
  fields: ["id", "name", "status"],
});

Next Steps