Custom Plugins

Build and integrate custom plugins for the LegionEdge Platform

The LegionEdge Platform plugin system lets you extend platform functionality by hooking into the request lifecycle, adding custom endpoints, and integrating with third-party services.

Plugin Structure

A plugin is a JavaScript/TypeScript module that exports a definePlugin function:

import { definePlugin } from "@legionedge/sdk/plugins";

export default definePlugin({
  name: "my-custom-plugin",
  version: "1.0.0",
  description: "Adds custom logging and validation",

  hooks: {
    beforeRequest: async (context) => {
      console.log(`[${context.method}] ${context.url}`);
      return context;
    },

    afterResponse: async (context, response) => {
      console.log(`Response: ${response.status} in ${response.duration}ms`);
      return response;
    },

    onError: async (context, error) => {
      console.error(`Request failed: ${error.message}`);
      // Return undefined to re-throw, or return a response to recover
    },
  },
});

Registering Plugins

Register plugins when creating the SDK client:

import { LegionEdge } from "@legionedge/sdk";
import loggingPlugin from "./plugins/logging";
import validationPlugin from "./plugins/validation";

const client = new LegionEdge({
  apiKey: process.env.LEGIONEDGE_API_KEY!,
  plugins: [loggingPlugin, validationPlugin],
});

Plugins execute in the order they are registered.

Lifecycle Hooks

Plugins can hook into these lifecycle events:

HookTimingUse Case
beforeRequestBefore the HTTP request is sentAdd headers, transform body, validate input
afterResponseAfter a successful responseTransform response, log metrics
onErrorWhen an error occursError reporting, fallback logic
onRetryBefore a retry attemptAdjust retry strategy, log retries
onInitWhen the client is initializedValidate configuration, register resources
onDestroyWhen the client is destroyedClean up connections, flush buffers

Example: Custom Header Plugin

export default definePlugin({
  name: "custom-headers",
  version: "1.0.0",

  hooks: {
    beforeRequest: async (context) => {
      context.headers.set("X-Custom-Trace-Id", generateTraceId());
      context.headers.set("X-Client-Version", "2.1.0");
      return context;
    },
  },
});

Example: Caching Plugin

const cache = new Map<string, { data: unknown; expiresAt: number }>();

export default definePlugin({
  name: "simple-cache",
  version: "1.0.0",

  hooks: {
    beforeRequest: async (context) => {
      if (context.method !== "GET") return context;

      const cached = cache.get(context.url);
      if (cached && cached.expiresAt > Date.now()) {
        return { ...context, cachedResponse: cached.data };
      }
      return context;
    },

    afterResponse: async (context, response) => {
      if (context.method === "GET") {
        cache.set(context.url, {
          data: response.data,
          expiresAt: Date.now() + 60_000,
        });
      }
      return response;
    },
  },
});

Plugin API

Plugins receive a PluginContext object with access to:

interface PluginContext {
  method: string;
  url: string;
  headers: Headers;
  body: unknown;
  params: Record<string, string>;
  config: LegionEdgeConfig;
  logger: Logger;
  metadata: Map<string, unknown>; // share data between hooks
}

Publishing Plugins

Share plugins with the community by publishing to npm:

npm publish --access public

Use the legionedge-plugin keyword in your package.json so others can discover it:

{
  "name": "@acme/legionedge-plugin-audit",
  "keywords": ["legionedge-plugin"],
  "main": "dist/index.js",
  "types": "dist/index.d.ts"
}

Next Steps