Alokai

Middleware

Create and configure the Alokai middleware server.

The middleware is the integration layer of an Alokai application: an HTTP server that mounts your backend integrations (commerce, CMS, payment, ...) behind one consistent API.

import { createServer } from "@alokai/connect/middleware";

Create a server

createServer bootstraps the middleware and returns a Node.js http.Server, ready to listen(). Every configured integration is exposed under /:integrationName/:extensionName?/:functionName, alongside operational endpoints: /healthz (liveness), /readyz (readiness), and /alokai-metrics (Prometheus metrics).

middleware.js
import { createServer } from "@alokai/connect/middleware";
import { config } from "./middleware.config.js";

const server = await createServer(config, {
  cors: {
    origin: "http://localhost:3000",
    credentials: true,
  },
});

server.listen(4000, () => {
  console.log("Middleware listening on port 4000");
});

Configuration

The first argument defines what the server runs: your integrations plus application-wide error handling, logging, and security headers.

Prop

Type

Server options

The second argument tunes the HTTP layer. Everything is optional and comes with production-ready defaults.

Prop

Type

Integrations

Each key of integrations mounts one backend under its own URL segment: the commerce key becomes /commerce/<method> on the server.

middleware.config.ts
export const config = {
  integrations: {
    commerce: {
      location: "@vsf-enterprise/sapcc-api/server",
      configuration: {
        // integration-specific settings
      },
      extensions: (extensions) => [...extensions, myExtension],
    },
  },
};

Prop

Type

Resilience for outbound calls is built in: pass retry for automatic retries and circuitBreaker to stop cascading failures.

Prop

Type

Extensions

Extensions add or override API methods, hook into the request lifecycle, and can extend the underlying Express app. They are plain objects attached to an integration's extensions list.

my-extension.ts
import type { ApiClientExtension } from "@alokai/connect/middleware";

export const myExtension: ApiClientExtension = {
  name: "my-extension",
  extendApiMethods: {
    async getProductWithReviews(context, params) {
      const product = await context.api.getProduct(params);
      const reviews = await fetchReviews(params.id);
      return { ...product, reviews };
    },
  },
  hooks: (req, res) => ({
    afterCall({ response, callName }) {
      if (req.method === "GET" && callName === "getProduct") {
        res.set("Cache-Control", "public, max-age=3600");
      }
      return response;
    },
  }),
};

The lifecycle hooks an extension can implement:

Prop

Type

Error handling

Throw createHttpError from API methods and extensions to control the response status and keep error payloads consistent; anything else is normalized to a safe 500 so internals never leak to the client.

import { createHttpError, HttpStatusCode } from "@alokai/connect/middleware";

async function deleteProduct(context, params) {
  try {
    await context.api.deleteProduct(params.id);
  } catch (error) {
    throw context.createHttpError({
      statusCode: HttpStatusCode.INTERNAL_SERVER_ERROR,
      message: "Failed to delete product",
      cause: error, // preserved for logs, hidden from the client
    });
  }
}

Where an error surfaces is configurable at two levels - an integration-specific errorHandler wins over the global one defined in MiddlewareConfig.

Logging

Every request carries a structured logger with integration and method context already attached. Inside API methods, extensions, and error handlers, retrieve it with getLogger:

import { getLogger } from "@alokai/connect/middleware";

async function getProduct(context, params) {
  const logger = getLogger(context);
  logger.info("Fetching product", { productId: params.id });
  // ...
}

Application-wide behavior (verbosity, stack traces, secret redaction) is configured on the Logger page.

On this page