Vercel
Vercel is a cloud platform primarily used for deploying web applications, especially those built with modern JavaScript frameworks. You can use the @archibald/vercel integration package to let your Archibald project run on Vercel functions
For more information about Vercel refer to official Vercel documentation.
Repository Integration
There are two ways how to install a vercel integration into an archibald project.
Option A: Archibald CLI Command
A quick way to set up the Integration in Vercel, specially for new projects, is the command included into the Archibald CLI.
npm run archibald add vercel
This command simply executes the following, manual steps:
Option B: Manually adding Vercel
In order to begin working on Vercel with Archibald, first npm install the corresponding @archibald/vercel package.
npm install --save @archibald/vercel
Add the following entry to your archibald.json:
...
"project": {
...
"vercel": {
"active": true,
}
},
This setting will extend the archibald build command, so you can tell Archibald to specifically build for Vercel. The new build step will copy your build output (usually the dist-folder) into a new folder .vercel, that is then used by the cloud environment.
During serving your Application with archibald serve, this option is not available to you. Generally the way you develop your Application locally will not change in any way.
You may want to adapt your build commands in your package.json. archibald build now accepts a new parameter --vercel or -vrcl.
For more information on the Vercel Build Output API visit the Vercel Output API (v3) Documentation.
Modifying the Server Entry
In order for your Archibald Project to work in the Vercel Cloud Functions environment, we need to provide a defined point of entry for the cloud function. This works by modifying your existing **/*/server/index.ts-file.
// add this import
import { handler } from '@archibald/vercel';
...
// you will most likely already have a bootstrap function like this
async function bootstrap() {
...
const app = await Factory.create(Server);
await app.start();
}
// instead of calling bootstrap() directly add this
// export needed for Vercel environments
export default handler(bootstrap)
This handler function will directly execute bootstrap() outside of Vercel environments.
Archibald detects Vercel Environments through an environment Variable
process.env['VERCEL'] === '1'.
Vercel Configuration
The default configuration that we set up for you in the @archibald/vercel-Package looks as follows:
archibald.json
This is the default configuration that we ship for hosting Archibald projects on Vercel.
{
"version": 3,
"routes": [
{
"src": '/public/(.*)',
"handle": 'resource'
}
]
};
You can learn more about it in the Vercel Documentation Build Output API (v3).
All our static resources, Javascript & CSS chunks, favicons, etc., usually hosted on NodeJS, will be covered by Vercel's EdgeCDN. Other requests, including 404, are going to one defined Vercel Function Environment.
You can customize this behaviour to your needs in the archibald.json-file in your repository root in the project section. During the build, this configuration will then be copied over into the .vercel output directory and respected by Vercel to configure your environment.
archibald.json
You can also customize the NodeJS environment that Vercel runs in. Per default, we will use 'nodejs24.x', but you can set another runtime.
...
"project": {
...
"vercel": {
"active": true,
"runtime": "nodejs24.x"
"config": {
// vercel.json
}
}
},
...
Split Deployment (per-route functions)
By default an Archibald app deploys as a single Vercel function: one index.func that boots the whole server — every controller, route and module — and dispatches every request. This is simple and stays the default.
You can optionally split controllers into separate functions, so each controller (or a configured group of controllers) is deployed as its own Vercel function on its own route. This gives smaller per-route responsibilities, independent cold-starts and per-route isolation. It is fully opt-in and non-breaking: with splitting disabled, the build output is identical to the single-function deploy described above.
Enabling the split
Add a split block to the vercel section of your archibald.json:
...
"project": {
...
"vercel": {
"active": true,
"runtime": "nodejs24.x",
"split": {
"active": true,
"groups": {
"commerce": ["CartController", "OrderController"],
"account": ["AccountController"]
},
"fallbackName": "fallback"
}
}
},
...
| Field | Required | Description |
|---|---|---|
active | yes | Opt-in. When false (the default), the app deploys as a single function. |
groups | no | Explicit grouping of controllers: groupName -> [ControllerNames]. When omitted, the default is one function per controller. |
fallbackName | no | Name of the catch-all function. Defaults to fallback. |
What goes where
- Each named group becomes a function that serves only the routes of the controllers it owns.
- The fallback function serves everything not claimed by a named group: the SSR page-render catch-all, the built-in
Resource/Debugcontrollers, any inline function-handler routes, and any controller you did not assign to a group. - Vercel routes requests by URL: static assets are served first, then each group's specific route patterns are matched, and finally a trailing
/(.*)wildcard sends the rest to the fallback function.
No server-entry changes needed
Your **/*/server/index.ts needs no changes — keep exporting the handler as usual (export default handler(bootstrap)). Each generated per-group function sets the ARC_SERVER_GROUP environment variable and re-exports your unchanged bundle; Factory.create reads that variable and scopes the server to the group, so only its controllers and routes are registered. Shared setup (services, plugins, decorators, modules, cookies, config) still runs in full in every function, so each one is a self-contained server.
If you need programmatic control you can still pass a group explicitly with
Factory.create(Server, { group }); the env var is only the default fallback.
If your project registers the SSR render catch-all itself (an inline {path*} route), use this.registerRenderFallback(path, handler) instead of this.server.route(...). It self-guards, so the render catch-all is only registered in the fallback function (and in single-function mode), never in an API function.
How the build works
When vercel.split.active is true, archibald build --vercel:
- Boots the built server once in a collect mode (
ARC_COLLECT_MANIFEST) that registers everything and dumps the resolved route table — so build-time paths match runtime paths exactly. In this mode Archibald neutralizesstart(), so the collect step never binds a port. - Derives the group → controllers → route mapping from that manifest and your
splitconfig. - Generates one entry per group under
dist/server/groups/and emits.vercel/output/functions/<group>.funcfor each group plus the fallback. - Writes a
config.jsonrouting table (static first, then group patterns, then the fallback wildcard).
If the manifest cannot be collected, the build falls back to the standard single-function output, so a split misconfiguration never breaks your deploy.
Splitting only affects the Vercel build output. Local development with
archibald serveis unchanged — the app always runs as a single server.
Project Setup
Once you have created your repository in Gitlab and pushed your project with the above changes, go to the Vercel Dashboard. Search for your repo in the netconomy space and import.
You should be able to Login, using your Gitlab Account.
Configuration
In the next step, fill out the Configure Project form according to the following preset:
- Project Name: your-project
- Framework Preset: Other
// configure this according to your package.json.
// Make sure this is --quiet and includes the described --vercel command line option
- Build Command: npm run build -- --quiet --vercel
Feel free to customize this form to your project needs, and don't be afraid to hit deploy now, as you can change all these settings and more later in the Project Settings.
Settings
To install the @archibald/\* dependencies during the deployment process in Vercel, you will need to add an Environment Variable. In your project, go to the project Settings within the Vercel dashboard.
Under Environment Variables add an entry NPM_RC that includes a valid .npmrc, including a token for accessing the dependencies from the netconomy package registry. For more information, check the Vercel documentation on this Topic.
Don't forget that we don't have access to any non-public APIs like Commerce within the Vercel Environment. You will have to set environment secrets and respect them in your servers API.
Deploying Storybook to Vercel
You can host your project's Storybook on Vercel the same way you host the application, as its own Vercel project. A Storybook build is entirely static, so no Vercel Function is created — the build is served directly from Vercel's Edge CDN.
Enable it in your archibald.json under the cli.storybook section:
...
"cli": {
...
"storybook": {
...
"vercel": {
"active": true
}
}
},
You can enable this automatically together with the application integration by passing the --storybook (-sb) flag to the CLI command:
npm run archibald add vercel -- --storybook
With it enabled, the archibald storybook command in production mode accepts a --vercel (-vrcl) flag. It transforms the Storybook build output (dist/stories by default) into a static Vercel Build Output under .vercel/output:
archibald storybook -m production --vercel
In the Vercel Dashboard, import your repository as a separate project (distinct from the application project) and set the Build Command to the above. The default config we ship is a minimal static configuration:
{
"version": 3
}
You can override it with a custom Vercel configuration:
...
"cli": {
...
"storybook": {
...
"vercel": {
"active": true,
"config": {
// vercel.json
}
}
}
},
Unlike the application deployment, the Storybook deployment does not modify your
server/index.tsand requires no runtime, as it ships no Vercel Function.
Vercel Edge Caching
As a prerequisite for caching to work you need to extend your
RenderControllerfromDefaultRenderControllerthis class automatically adds the caching headers to your responses. As an alternative you can calladdCachingon the ResponseObject in your Controller.
To configure serverside rendered HTML to be cached through Vercel's Edge Cache you can set Cache Control Headers like so:
...
function HomePage() {
useVary('Accept-Language');
useCacheControl([
{
type: 'CDN-Cache-Control',
directives: {
'max-age': '30m'
}
},
{
type: 'Cache-Control',
directives: {
'max-age': '15m
}
}
]);
return <HomepageTemplate />
}
...
Skew Protection
Vercel's Skew Protection keeps clients running a previous deployment working during a rollout. Archibald cooperates with it automatically — see the generic Deployment Skew Protection feature for the full model.
To enable it:
- Turn on Skew Protection in your Vercel project settings (Vercel sets
VERCEL_SKEW_PROTECTION_ENABLED=1and providesVERCEL_DEPLOYMENT_ID). - Keep the existing
export default handler(bootstrap)server entry — no other application code change is required. On Vercel, thehandlerwrapper registers a Vercel-specific provider before the server boots, which:- writes the
__vdplcookie on SSR HTML responses (scoped to the public asset path), - appends
?dpl=<deploymentId>to SSR-emitted asset URLs, - sends/echoes the deployment id via the
x-deployment-idheader.
- writes the
- To additionally enable the client-side behaviours (tagging every BFF request with the
deployment id, one-shot recovery from a stale chunk load, and reload-on-mismatch), set
server.skewProtection.active: trueinarchibald.json— this bakes the skew config into the client bundle. The Vercel provider still overrides the server-side specifics above.
The provider is inert unless both VERCEL=1 and VERCEL_SKEW_PROTECTION_ENABLED=1 are present, so
it has no effect in local development or on other platforms. It is fully compatible with the
split deployment path.