Skip to main content

Demo Mode

Deploy a read-only showcase of MultiWA without exposing your production data. Demo mode blocks all write operations while allowing users to explore the full dashboard experience.

Quick Setup

# API (.env)
DEMO_MODE=true

# Admin UI (.env.local)
NEXT_PUBLIC_DEMO_MODE=true

Restart both services after setting these variables.


How It Works

API Guard

When DEMO_MODE=true, a global DemoGuard intercepts every incoming request:

HTTP MethodBehavior
GET, HEAD, OPTIONS✅ Allowed — users can browse freely
POST, PUT, PATCH, DELETE❌ Blocked — returns 403

Blocked response example:

{
"statusCode": 403,
"error": "Demo Mode",
"message": "🔒 This action is disabled in demo mode. Deploy your own instance to unlock full functionality!",
"demoMode": true
}

Frontend Banner

When NEXT_PUBLIC_DEMO_MODE=true, a dismissible amber banner appears at the top of every dashboard page:

🎮 Demo Mode — You're exploring a read-only instance. Deploy your own →

The banner can be dismissed by clicking the ✕ button (per-session only).


Whitelisted Endpoints

Some endpoints must remain functional even in demo mode. These are marked with the @AllowInDemo() decorator:

EndpointReason
POST /auth/loginUsers need to log in to explore
POST /auth/refreshKeep sessions alive
POST /auth/2fa/verifyComplete 2FA login flow

Adding More Whitelisted Endpoints

To allow additional endpoints in demo mode, add the @AllowInDemo() decorator:

import { AllowInDemo } from '../../common/guards/demo.guard';

@Post('my-endpoint')
@AllowInDemo()
async myEndpoint() {
// This endpoint will work even in demo mode
}

Environment Variables

VariableAppTypeDefaultDescription
DEMO_MODEAPIBoolean stringfalseEnable read-only API guard
NEXT_PUBLIC_DEMO_MODEAdminBoolean stringfalseShow demo banner in UI

Note: Both variables must be set independently. The API guard and the frontend banner operate separately — you can enable one without the other.


Architecture

Request → DemoGuard → Route Handler

├─ GET/HEAD/OPTIONS → ✅ Pass through
├─ @AllowInDemo() → ✅ Pass through
└─ POST/PUT/PATCH/DELETE → ❌ 403 Forbidden

The guard is registered globally in AppModule using NestJS APP_GUARD provider, so it applies to all routes automatically without needing @UseGuards() on each controller.


Use Cases

  • Public demo instance — Let potential users explore MultiWA before self-hosting
  • Conference booth — Showcase features without risk of data modification
  • Documentation screenshots — Capture consistent UI screenshots
  • Training environment — Let team members explore safely