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).
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.
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.
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.