コンテンツにスキップ
Vantage
English
Esc
navigateopen⌘Jpreview
このページの内容

Build & deploy

What's in dist/ and vantage-manifest.json. Same-origin serving, and splitting the client and server across origins (S3+CloudFront ⇄ Lambda, etc.).

vantage build emits the production artifacts into dist/. From there, serving splits into two shapes: serve the client and server together from the same origin, or split them — the client on static hosting (S3+CloudFront / Cloudflare Pages) and the server on a separate origin (Lambda / a container). For the split shape, Vantage provides an API base URL switch and CORS.

Build output

vantage build   # → dist/
dist/
├── client/                Client (index.html + hashed assets, code-split per route)
├── server/index.mjs       Only when server/ exists (esbuild / node20 / ESM bundle)
└── vantage-manifest.json  Deploy contract

vantage-manifest.json is the one contract the host reads. Its fields:

Field Meaning
schemaVersion Manifest schema version (currently 1)
mode "spa" (client only) / "fullstack" (has server). Derived from server/
client Relative path to the client output ("./client")
server Relative path to the server bundle ("./server/index.mjs"). Fullstack only
spaFallback HTML for the SPA fallback ("./client/index.html")
runtime Server runtime ("node")

Preview locally

Before deploying, run the production build locally to verify it.

vantage preview

vantage preview reads mode from vantage-manifest.json: fullstack starts the Node server (default :4173), spa serves a static preview.

Two deployment shapes

Same origin

One Node process serves both the client and /api. Minimal setup, and no CORS needed.

Split client & server

Client on S3+CloudFront etc., server on Lambda / a container. Needs the API base URL switch and CORS.

Deploy to a single origin

The simplest shape. Place all of dist/ on a host that runs Node (a container / VM) and start the generated server. One process serves both static assets and /api/*, so there is nothing to switch and no CORS to configure.

# on the host where dist/ lives
PORT=8080 node dist/server/index.mjs

The server listens on PORT (or 3000 if unset). The client calls relative /api/... paths on the same origin.

Split the client and server (S3+CloudFront ⇄ Lambda)

Here the client lives on CDN static hosting and the server on a separate origin. A common pairing is “dist/client on S3+CloudFront, dist/server/index.mjs on Lambda / a container”. Because the origins differ, two things are required.

1. Switch the API base URL

Client API calls go through apiFetch / apiUrl from @squadbase/vantage/query. This is the one seam that calls /api, and it’s where the base URL is swapped.

import { apiFetch, useQuery } from "@squadbase/vantage/query";

useQuery({
  queryKey: ["monthly-analysis"],
  // Keep the path as it maps in server/api (include /api)
  queryFn: () => apiFetch("/api/monthly-analysis").then((r) => r.json()),
});

The base URL is read from PUBLIC_API_BASE_URL. Unset means empty, so requests stay same-origin relative paths (/api/monthly-analysis). Set it and the value is prepended (https://api.example.com/api/monthly-analysis). Two ways to set it:

(a) Override via env — put it in .env or a CI environment variable. Because of the PUBLIC_ prefix it is inlined into the client bundle.

# .env (or a CI env var)
PUBLIC_API_BASE_URL=https://api.example.com

(b) Specify at build time — pass --api-base-url to vantage build.

vantage build --api-base-url https://api.example.com

2. Allow CORS

Once the client and server are on different origins, the browser requires CORS. Setting CORS_ORIGIN on the server enables CORS on /api (including the preflight OPTIONS response). Unset means CORS is off, leaving same-origin serving unchanged.

# environment variables on the server (Lambda / container)
CORS_ORIGIN=https://app.example.com          # a single origin
# CORS_ORIGIN=https://a.example.com,https://b.example.com   # comma-separated for several
# CORS_ORIGIN=*                              # allow all (not usable with credentials)
CORS_CREDENTIALS=true                        # only if you allow cookies / credentials
Variable Role
CORS_ORIGIN Allowed origin(s): * / a single value / comma-separated. Unset → CORS off
CORS_CREDENTIALS When true, allow credentials (cookies, etc.). Not used with *

The allowed methods are set automatically to the ones Vantage APIs can use (GET,POST,PUT,PATCH,DELETE,OPTIONS). This behavior is shared by the dev server and the production Node server, so you can verify CORS during development.

3. Put the client on static hosting

Upload dist/client/ to S3 (+ CloudFront) or Cloudflare Pages. As an SPA, unknown paths must fall back to spaFallback (index.html).

  • S3 + CloudFront: use a CloudFront “custom error response” to rewrite 403 / 404 to /index.html with status 200.
  • S3 static website hosting: set the error document to index.html.
  • Cloudflare Pages: SPA fallback is on by default.

4. Put the server on Lambda / a container

dist/server/index.mjs is a Node HTTP server that listens on PORT (the manifest’s runtime is "node"). It runs anywhere a Node process runs.

  • Container (Cloud Run / ECS·Fargate / Render / Fly, etc.): use node dist/server/index.mjs as the start command and pass PORT.
  • AWS Lambda: since it is a listening Node server, a container image + AWS Lambda Web Adapter (or another “run an HTTP server on Lambda” approach) is a good fit.
  • Pass secrets (DB credentials, API keys) as server environment variables. They are only readable via ApiContext.env and never reach the client.

Deploy to AWS Lambda (a concrete walkthrough)

The generated dist/server/index.mjs is a Node HTTP server that listens on PORT. Lambda, on the other hand, invokes a handler per event, so the natural way to run a listening server there is to put the AWS Lambda Web Adapter (LWA) in front of it. LWA proxies Function URL / API Gateway events to the HTTP server running locally. No Vantage code changes are needed.

esbuild bundles into a single file dist/server/index.mjs, so you don’t need to carry node_modules.

# Dockerfile
FROM public.ecr.aws/docker/library/node:20-slim

# Bundle the Lambda Web Adapter as an extension (bump the version as needed)
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter

WORKDIR /var/task
# Copy all of dist/ (the server resolves ../client, so keep the layout)
COPY dist ./dist

# Keep LWA and the Node server on the same port (LWA defaults to 8080)
ENV PORT=8080
CMD ["node", "dist/server/index.mjs"]
vantage build                                   # produce dist/
docker build -t vantage-api .

# Log in to ECR and push (<acct> / <region> are yours)
aws ecr get-login-password --region ap-northeast-1 \
  | docker login --username AWS --password-stdin <acct>.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag vantage-api <acct>.dkr.ecr.ap-northeast-1.amazonaws.com/vantage-api:latest
docker push <acct>.dkr.ecr.ap-northeast-1.amazonaws.com/vantage-api:latest

Create a Lambda function from this image and enable a Function URL to get an HTTPS endpoint. Use it as the /api/* origin in CloudFront, or point the client’s PUBLIC_API_BASE_URL at it directly.

2. Set the Lambda environment variables

Pass server config and secrets as Lambda environment variables (they never reach the client).

Variable Example Purpose
CORS_ORIGIN https://d111111abcdef8.cloudfront.net Allow the client’s origin
CORS_CREDENTIALS true Only if you use cookies / credentials
SECRET_*, etc. sk_live_xxx Secrets read via ApiContext.env

PORT is already set in the Dockerfile (matching LWA’s default 8080).

3. Point the client at Lambda

At client build time, set PUBLIC_API_BASE_URL to the Lambda (or the CloudFront in front of it) origin.

vantage build --api-base-url https://d111111abcdef8.cloudfront.net

apiUrl("/api/monthly-analysis") then calls that origin with /api/monthly-analysis appended.

Production environment variables at a glance

The boundary is the prefix (see also Environment variables).

Kind Set where When it applies Example
Public client value Build time (.env / --api-base-url) Baked into the build PUBLIC_API_BASE_URL
Server config Server environment variable Read at startup PORT, CORS_ORIGIN, CORS_CREDENTIALS
Secret Server environment variable Read at startup (ApiContext.env) SECRET_API_KEY, etc.

Pre-deploy checklist

  • vantage check passes with no errors
  • vantage build (with --api-base-url for a split setup, or PUBLIC_API_BASE_URL in .env)
  • vantage preview verifies it locally
  • For a split setup, set CORS_ORIGIN (the client’s origin) on the server
  • Configure SPA fallback (unknown paths → index.html) on the static host
  • Keep secrets in server environment variables only (never expose to the client)

このページは役に立ちましたか?