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.

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/server to store the middleware application, so most examples in our documentation will use this folder structure.

apps/
| server/ <- your server middleware application
| web/ <- your frontend application

Installing Dependencies

To start, you need to install the @vue-storefront/middleware package. It contains the core functionality of the middleware that you can extend with integrations.

npm
npm i @vue-storefront/middleware consola ts-node-dev

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 @vue-storefront/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 "@vue-storefront/middleware";
import consola from "consola";
import config from "../middleware.config";

(async () => {
  const app = await createServer({ integrations: config.integrations });
  const host = process.argv[2] ?? "0.0.0.0";
  const port = Number(process.argv[3]) || 4000;

  app.listen(port, host, () => {
    consola.success(`API server listening on http://${host}:${port}`);
  });
})();

With our middleware file set up, we can use ts-node-dev to run our application.

package.json
{
  "scripts": {
    "dev": "ts-node-dev src/index.ts"
  }
}

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.js 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
export const integrations = {
  example: {
    location: "@vue-storefront/example-api/server",
    configuration: {
      // configuration for the integration (see the integration's installation guide for details)
    },
    extensions: (baseExtensions) => [
      ...baseExtensions,
      // your additional extensions
    ],
    customQueries: {},
  },
};

Local Development

TypeScript Configuration

If you want to have the same tsconfig.json options as our boilerplate, you can reference the boilerplate configuration.

Using nodemon

To make local development smoother, you can use nodemon to watch for changes in your middleware application and restart the server automatically.

npm i -D nodemon

Finally, we can create a nodemon.json file to set the files to watch and the command that we want to run when a change is made.

nodemon.json
{
  "watch": ["**/*"],
  "ext": "ts",
  "exec": "ts-node-dev src/index.ts"
}

Then, you can add a script to your package.json file to run the middleware with nodemon.

{
  "scripts": {
    "dev": "nodemon middleware.js"
  }
}

Next Steps