Vue Storefront is now Alokai! Learn More
Installation

Installation

If you're building your Alokai application from scratch, you'll need to set up the middleware to connect to your backend services.

Node.js 22 is required. While Node.js 20 may work, it is not officially supported and has not been thoroughly tested.

Creating the Application

Since the middleware is a separate app, it should be built outside of your frontend Alokai application. We recommend using a monorepo to keep both applications in a single repository.

Our storefronts use an apps/storefront-middleware to store the middleware application, so most examples in our documentation will use this folder structure.

apps/
| storefront-middleware/ <- your server middleware application
| storefront-unified-nextjs/ <- your frontend application

Installing Dependencies

To start, you need to install the @alokai/connect package. It contains the core functionality of the middleware that you can extend with integrations. The middleware will be available in the @alokai/connect/middleware subpath.

npm
npm i @alokai/connect
npm i -D tsx typescript

If you just made a new folder for your middleware, this command will also create a package.json file to your project's root directory.

Running the Application

The middleware uses ESM (ES Modules). Make sure your package.json includes "type": "module".

The @alokai/connect/middleware package exposes a createServer function that you can use to initialize the Express application that runs the Alokai middleware.

The createServer function accepts an integrations object and returns an Express.js application that can be used to listen on a port.

src/index.ts
import { createServer } from "@alokai/connect/middleware";
import loadEnv from "./config/env.js";

// Load .env files before importing config, which reads process.env at module eval time
await loadEnv();
const { config } = await import("../middleware.config.js");

const port = Number(process.env.API_PORT) || 4000;

const app = await createServer(config);

app.listen(port, "", () => {
  console.log(`API server listening on port ${port}`);
});

loadEnv loads .env files in development and test environments. In production, environment variables should be provided by the hosting platform. A minimal implementation:

src/config/env.ts
export default async function loadEnv() {
  if (["development", "test"].includes(process.env.NODE_ENV ?? "")) {
    const { config } = await import("dotenv");
    config();
  }
}

Config is imported dynamically (await import(...)) to ensure environment variables are loaded before middleware.config.ts evaluates. Static imports are hoisted and would run before loadEnv().

With our middleware file set up, we can use tsx watch to run our application during development.

package.json
{
  "type": "module",
  "scripts": {
    "dev": "tsx watch --clear-screen=false src/index.ts",
    "start": "node lib/src/index.js"
  }
}

Configuration Options

The createServer function accepts a second parameter for configuring various middleware options:

src/index.ts
const app = await createServer(
  { integrations: config.integrations },
  {
    // CORS configuration
    cors: {
      origin: "http://localhost:3000",
      credentials: true,
    },
    // Body parser configuration
    bodyParser: {
      limit: "50mb",
    },
    // Cookie parser configuration
    cookieParser: {
      secret: "secret",
    },
    // File upload configuration
    fileUpload: {
      enabled: true, // Enable/disable file upload functionality
      maxFileSize: 5242880, // Maximum file size in bytes (default: 5MB)
      maxFiles: 5, // Maximum number of files per upload
      allowedMimeTypes: ["image/*", "application/pdf"], // Allowed file types
      fieldNames: [], // Accepted form field names for file uploads
    },
  }
);

CORS Configuration

Configure Cross-Origin Resource Sharing (CORS) settings. By default, http://localhost:4000 is included in the allowed origins.

Body Parser Configuration

Configure the body-parser middleware settings for handling request bodies.

Configure the cookie-parser middleware settings for handling cookies.

File Upload Configuration

Configure file upload handling for multipart/form-data requests. You can either provide static options or a function that returns configuration based on the request:

fileUpload: (req) => ({
  enabled: req.headers["x-enable-upload"] === "true",
  maxFileSize: 5242880,
  maxFiles: 5,
  allowedMimeTypes: ["image/*", "application/pdf"],
  fieldNames: [],
});

Available options:

  • enabled: Enable/disable file upload functionality (default: false)
  • maxFileSize: Maximum file size in bytes (default: 5MB) // Maximum file size is limited to 10MB
  • maxFiles: Maximum number of files per upload (default: 5)
  • allowedMimeTypes: Array of allowed MIME types (default: ["image/*", "application/pdf"])
  • fieldNames: Array of accepted form field names for file uploads (default: [])

When file uploads are enabled, uploaded files are available in the req.files object within your API endpoints:

export const upload = (context) => {
  const { files } = context.req;
  // Handle uploaded files
  return Promise.resolve({
    status: 200,
    message: "ok",
  });
};

For the performance reasons, file uploads are disabled by default. It is recommended to enable them only when needed and use headers to control file upload behavior.

You can also dynamically control file upload behavior on a per-request basis. This is particularly useful when you want to enable uploads only for specific scenarios, such as:

  • Authenticated requests
  • Requests with specific headers
  • Requests from certain origins
  • Different file size limits for different endpoints

Here's an example of dynamic configuration based on request headers:

src/index.ts
const app = await createServer(
  { integrations: config.integrations },
  {
    fileUpload: (req) => ({
      enabled: req.headers["x-enable-upload"] === "true",
      maxFileSize: req.headers["x-upload-size"]
        ? parseInt(req.headers["x-upload-size"])
        : 5242880,
      maxFiles: 5,
      allowedMimeTypes: ["image/*", "application/pdf"],
      fieldNames: [],
    }),
  }
);

In this example:

  • File uploads are only enabled when the x-enable-upload: true header is present
  • The maximum file size can be controlled via the x-upload-size header
  • Other options remain static but could also be made dynamic based on your needs

Adding Integrations

Integrations contain code extend the middleware with additional functionality to make it easy to work with different third-party services.

Alokai has available integrations that you can use out of the box, but you can also create your own integrations to connect to any service that you need.

Most integrations are made up of two parts:

  1. An SDK module that extends the Alokai SDK to add methods to your frontend
  2. An API Client that extends the middleware to add new API endpoints or modify the Express.js application itself

We recommend creating a middleware.config.ts file located at the project's root that you can use to configure API Clients for your integrations.

Each integration will be an object with a unique key that you can find in each integration's installation guide. This key is used for communication with the middleware, so changing it might cause the integration to break.

The object for each integration can contain the following properties:

  • location - the path to the API Client file in your node_modules folder
  • configuration - the configuration for the integration (see the integration's installation guide for details)
  • extensions - an optional function that can be used to extend the integration with additional functionality
  • customQueries - an optional object that can be used to add custom queries to the integration
middleware.config.ts
import type { MiddlewareConfig } from "@alokai/connect/middleware";

export const config = {
  integrations: {
    example: {
      location: "@vsf-enterprise/example-api/server",
      configuration: {
        // configuration for the integration (see the integration's installation guide for details)
      },
      extensions: (baseExtensions) => [
        ...baseExtensions,
        // your additional extensions
      ],
    },
  },
} satisfies MiddlewareConfig;

Local Development

TypeScript Configuration

The middleware uses "module": "ESNext" and "moduleResolution": "Bundler" in tsconfig.json. Path aliases (@/*) are configured via typescript-transform-paths. Refer to your project's apps/storefront-middleware/tsconfig.json as the reference.

Watch Mode with tsx

The recommended way to run the middleware during local development is tsx watch, which automatically restarts the server when files change — no additional tooling required.

package.json
{
  "scripts": {
    "dev": "tsx watch --clear-screen=false src/index.ts"
  }
}

tsx supports TypeScript and ESM out of the box, so no transpilation step is needed during development.

Next Steps