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 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.
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.
{
"scripts": {
"dev": "ts-node-dev src/index.ts"
}
}
Configuration Options
The createServer
function accepts a second parameter for configuring various middleware options:
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.
Cookie Parser Configuration
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 10MBmaxFiles
: 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:
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:
- An SDK module that extends the Alokai SDK to add methods to your frontend
- 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 yournode_modules
folderconfiguration
- 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 functionalitycustomQueries
- an optional object that can be used to add custom queries to the integration
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.
{
"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"
}
}