Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions templates/vue-starter-template/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
.nuxt
.output
.turbo
.vercel
.env
.env.*
!.env.template
dist
coverage
*.log
.DS_Store
14 changes: 9 additions & 5 deletions templates/vue-starter-template/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@

# Your Shopware 6 Store API endpoint
# https://frontends.shopware.com/installation.html
NUXT_PUBLIC_SHOPWARE_ENDPOINT="https://demo-frontends.shopware.store/store-api/"
NUXT_PUBLIC_SHOPWARE_ENDPOINT=https://demo-frontends.shopware.store/store-api/

# Sales Channel access token (Settings > Sales Channel > API access in Shopware admin)
# https://frontends.shopware.com/installation.html
NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN="SWSCNWDGMUWZM0TLVUU0YKLQVW"
NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN=SWSCNWDGMUWZM0TLVUU0YKLQVW

# Storefront domain registered in your Sales Channel (Settings > Sales Channel > Domains).
# Required for customer registration to work in local development, where localhost
# doesn't match any configured domain. In production, window.location.origin is used.
# Only set this for local development/customer registration issues. Leave empty in production.
NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL=""
NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL=

# Optional private SSR endpoint. Set this only if server-side requests should
# use a different internal URL than the public browser endpoint.
NUXT_SHOPWARE_ENDPOINT=

# Type generation (development only) — used by `pnpm generate-types` (@shopware/api-gen)
# https://frontends.shopware.com/packages/api-client.html

# Base URL of your Shopware instance (without /store-api/ suffix)
OPENAPI_JSON_URL="https://demo-frontends.shopware.store"
OPENAPI_JSON_URL=https://demo-frontends.shopware.store

# Sales Channel access key for fetching the OpenAPI schema
OPENAPI_ACCESS_KEY=""
OPENAPI_ACCESS_KEY=
41 changes: 41 additions & 0 deletions templates/vue-starter-template/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Build stage
FROM node:24-alpine AS builder

RUN corepack enable && corepack prepare pnpm@10.23.0 --activate

WORKDIR /app

# Copy package files first for better layer caching
COPY package.json pnpm-lock.yaml* ./

RUN pnpm install

# Copy source files
COPY . .

RUN pnpm build

# Production stage
FROM node:24-alpine AS runner

WORKDIR /app

# Create tmp directory for runtime writes (mount as tmpfs when running read-only)
RUN mkdir -p /app/tmp && chown node:node /app/tmp

# Copy built output from builder (standalone, no deps needed)
COPY --from=builder --chown=node:node /app/.output ./.output

# Use non-root user for security
USER node

# Point Node.js temp operations to /app/tmp
ENV TMPDIR=/app/tmp

ENV HOST=0.0.0.0
ENV PORT=3000
ENV NODE_ENV=production

EXPOSE 3000

CMD ["node", ".output/server/index.mjs"]
54 changes: 53 additions & 1 deletion templates/vue-starter-template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,43 @@ bun run preview

Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

### Docker

Build and run the production image from this directory:

```bash
docker build -t shopware-vue-starter .
docker run --rm -p 3000:3000 \
-e NUXT_PUBLIC_SHOPWARE_ENDPOINT="https://your-shop.example/store-api/" \
-e NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN="your-sales-channel-token" \
-e NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL="" \
shopware-vue-starter
```

You can also pass environment variables from a local `.env` file:

```bash
docker run --rm -p 3000:3000 --env-file .env shopware-vue-starter
```

The Docker image reads the same Nuxt public runtime variables as local development:

```bash
NUXT_PUBLIC_SHOPWARE_ENDPOINT=https://your-shop.example/store-api/
NUXT_PUBLIC_SHOPWARE_ACCESS_TOKEN=your-sales-channel-token
NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL=
NUXT_SHOPWARE_ENDPOINT=
```

`NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL` is mainly useful for local development when the Shopware sales channel domain does not match `localhost`. In production, leave it empty unless you specifically need to override it.

`NUXT_SHOPWARE_ENDPOINT` is optional. Use it only when server-side requests from the container should target a different internal URL than the browser-facing `NUXT_PUBLIC_SHOPWARE_ENDPOINT`.

The template includes a `.dockerignore` file so local `node_modules`, `.nuxt`, `.output`, and `.env` files are not copied into the image. This is important:

- your local `.env` must not be baked into the image during `docker build`
- the Shopware instance must be selected at container runtime via `docker run --env-file .env` or `-e ...`

## Styling and Shopping Experiences integration

This tempalte uses [UnoCSS](https://unocss.dev/) for styling, which is a utility-first CSS framework. It is configured to use the [Tailwind CSS](https://tailwindcss.com/) classes.
Expand All @@ -95,4 +132,19 @@ The extended template:

**Use the base template** (this one) when you want to start from scratch with minimal setup and maximum flexibility.

**Use the extended template** when you want to see a complete implementation or need a head start with pre-built components and styling.
**Use the extended template** when you want to see a complete implementation or need a head start with pre-built components and styling.

## Fastly and ISR

This template uses Nuxt route rules with ISR for public storefront pages and adds `Surrogate-Control` headers for CDN caching:

- public storefront routes: `Surrogate-Control: max-age=3600, stale-while-revalidate=86400`
- checkout, account, and wishlist routes: `Surrogate-Control: no-store`

For a Fastly-backed Node deployment, the usual setup is:

- cache `GET` and `HEAD` HTML responses when `Surrogate-Control` or cacheable `Cache-Control` is present
- bypass caching for `/checkout`, `/account`, and `/wishlist`
- avoid caching responses that set cookies or depend on user/session-specific SSR

Nuxt/Nitro provides the cache headers. Fastly still needs to be configured to respect them for HTML responses.
20 changes: 18 additions & 2 deletions templates/vue-starter-template/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ export default defineNuxtConfig({
},
routeRules: {
"/**": {
// 60-minute ISR — increase for mostly-static storefronts, decrease for frequently updated content
isr: 60 * 60,
// 24-hour ISR — reduce for frequently updated catalogs or CMS-heavy storefronts
isr: 60 * 60 * 24,
headers: {
"Surrogate-Control": "max-age=86400, stale-while-revalidate=86400",
},
},
"/**/*.svg": {
headers: {
Expand All @@ -89,19 +92,32 @@ export default defineNuxtConfig({
ssr: false,
headers: {
"Cache-Control": "no-cache, no-store, must-revalidate",
"Surrogate-Control": "no-store",
},
},
"/checkout/**": {
ssr: false,
headers: {
"Surrogate-Control": "no-store",
},
},
"/account": {
ssr: false,
headers: {
"Surrogate-Control": "no-store",
},
},
"/account/**": {
ssr: false,
headers: {
"Surrogate-Control": "no-store",
},
},
"/wishlist": {
ssr: false,
headers: {
"Surrogate-Control": "no-store",
},
},
},
imports: {
Expand Down
Loading