Deployment
This guide covers everything you need to deploy your Fanfare integration to production, including environment configuration, security considerations, and monitoring.
Pre-Deployment Checklist
Before deploying to production, verify the following:
1. Use Production Credentials
Ensure you are using production API keys, not test keys:
// Production
<FanfareProvider
organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID}
publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY} // pk_live_...
environment="production"
>
Production keys:
- Start with
pk_live_
- Connect to
https://api.fanfare.io
- Use your production organization data
2. Remove Debug Mode
Disable debug logging in production:
<FanfareProvider
organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID}
publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY}
debug={false} // Always false in production
logging={{ level: "error" }} // Only log errors
>
3. Verify Environment Variables
Ensure all required environment variables are set in your production environment:
# Required
NEXT_PUBLIC_FANFARE_ORG_ID=org_your_production_id
NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=pk_live_your_production_key
# Optional
NEXT_PUBLIC_FANFARE_ENVIRONMENT=production
4. Test Production Configuration Locally
Before deploying, test with production credentials locally:
# Create a .env.production.local file (gitignored)
NEXT_PUBLIC_FANFARE_ORG_ID=org_production_123
NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=pk_live_xyz789
# Run with production config
npm run build && npm run start
Backend Integration
Validating Admission Tokens
When customers are admitted, they receive an admission token. Your backend must validate this token before completing the transaction.
// Your backend API
import { createHmac } from "crypto";
interface AdmissionValidation {
valid: boolean;
experienceId?: string;
consumerId?: string;
admittedAt?: string;
expiresAt?: string;
}
async function validateAdmissionToken(token: string): Promise<AdmissionValidation> {
const response = await fetch("https://api.fanfare.io/v1/admissions/validate", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.FANFARE_SECRET_KEY}`,
"X-Organization-Id": process.env.FANFARE_ORG_ID!,
},
body: JSON.stringify({ token }),
});
if (!response.ok) {
return { valid: false };
}
return response.json();
}
Security Note: Never expose your secret API key (sk_live_...) in client-side code. Only use it in your backend.
Webhook Integration
Set up webhooks to receive real-time notifications about experience events:
Common webhook events:
| Event | Description |
|---|
queue.admitted | Consumer was admitted from queue |
draw.completed | Draw finished, winners selected |
auction.ended | Auction ended |
admission.expired | Admission token expired |
Example webhook handler:
// pages/api/webhooks/fanfare.ts (Next.js)
import { NextApiRequest, NextApiResponse } from "next";
import { createHmac } from "crypto";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "POST") {
return res.status(405).end();
}
// Verify webhook signature
const signature = req.headers["x-fanfare-signature"] as string;
const payload = JSON.stringify(req.body);
const expectedSignature = createHmac("sha256", process.env.FANFARE_WEBHOOK_SECRET!).update(payload).digest("hex");
if (signature !== expectedSignature) {
return res.status(401).json({ error: "Invalid signature" });
}
// Handle the event
const event = req.body;
switch (event.type) {
case "queue.admitted":
// Handle queue admission
await handleQueueAdmission(event.data);
break;
case "draw.completed":
// Handle draw completion
await handleDrawCompletion(event.data);
break;
default:
console.log("Unhandled event type:", event.type);
}
res.status(200).json({ received: true });
}
Security Considerations
Content Security Policy (CSP)
Allow connections to Fanfare APIs in your CSP:
// next.config.js
const securityHeaders = [
{
key: "Content-Security-Policy",
value: `
default-src 'self';
connect-src 'self' https://api.fanfare.io https://beacon.fanfare.io;
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
`.replace(/\n/g, ""),
},
];
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: securityHeaders,
},
];
},
};
CORS Configuration
The Fanfare API handles CORS automatically. Ensure your domain is registered in your Fanfare dashboard.
Rate Limiting
The Fanfare API has rate limits to protect against abuse:
| Endpoint | Limit |
|---|
| Queue entry | 100 requests/minute per IP |
| Draw entry | 100 requests/minute per IP |
| Auction bidding | 60 requests/minute per consumer |
| Status polling | 120 requests/minute per consumer |
The SDK handles rate limit errors gracefully with exponential backoff.
Bundle Size
Verify your bundle size includes only what you need:
# Analyze bundle
npm run build -- --analyze
The SDK supports tree-shaking. If you only use queues, draw/auction code is not included.
Lazy Loading
Load Fanfare components lazily if they are not needed on initial page load:
import { lazy, Suspense } from "react";
const QueueWidget = lazy(() => import("@fanfare/react").then((m) => ({ default: m.QueueWidget })));
function ProductPage() {
return (
<div>
<h1>Product Details</h1>
{/* QueueWidget loads only when rendered */}
<Suspense fallback={<div>Loading queue...</div>}>
<QueueWidget experienceId="exp_123" />
</Suspense>
</div>
);
}
Preconnect to API
Add preconnect hints to reduce connection latency:
<head>
<link rel="preconnect" href="https://api.fanfare.io" />
<link rel="preconnect" href="https://beacon.fanfare.io" />
</head>
Monitoring and Observability
Error Tracking
Integrate with your error tracking service:
import * as Sentry from "@sentry/react";
import { FanfareProvider } from "@fanfare/react";
function App() {
return (
<Sentry.ErrorBoundary fallback={<ErrorFallback />}>
<FanfareProvider
organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID!}
publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY!}
>
<YourApp />
</FanfareProvider>
</Sentry.ErrorBoundary>
);
}
Analytics Events
Track Fanfare events in your analytics:
import { useFanfare } from "@fanfare/react";
import { useEffect } from "react";
function AnalyticsTracker() {
const fanfare = useFanfare();
useEffect(() => {
const unsubscribeAdmitted = fanfare.on("queue:admitted", (data) => {
// Track in your analytics
analytics.track("Queue Admitted", {
queueId: data.queueId,
});
});
const unsubscribeDrawWon = fanfare.on("draw:won", (data) => {
analytics.track("Draw Won", {
drawId: data.drawId,
});
});
return () => {
unsubscribeAdmitted();
unsubscribeDrawWon();
};
}, [fanfare]);
return null;
}
Health Checks
Monitor Fanfare API availability:
// health-check.ts
async function checkFanfareHealth(): Promise<boolean> {
try {
const response = await fetch("https://api.fanfare.io/health", {
timeout: 5000,
});
return response.ok;
} catch {
return false;
}
}
Vercel
# Set environment variables in Vercel dashboard or CLI
vercel env add NEXT_PUBLIC_FANFARE_ORG_ID
vercel env add NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY
# Deploy
vercel --prod
Netlify
# netlify.toml
[build]
command = "npm run build"
publish = ".next"
[build.environment]
NEXT_PUBLIC_FANFARE_ORG_ID = "org_your_id"
NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY = "pk_live_your_key"
AWS Amplify
# amplify.yml
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: .next
files:
- "**/*"
cache:
paths:
- node_modules/**/*
Set environment variables in the Amplify console.
Docker
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG NEXT_PUBLIC_FANFARE_ORG_ID
ARG NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_FANFARE_ORG_ID=$NEXT_PUBLIC_FANFARE_ORG_ID
ENV NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=$NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["npm", "start"]
Post-Deployment Verification
After deploying, verify your integration:
1. Test the Full Flow
- Visit your production site
- Join a queue/draw/auction
- Verify you are admitted (use a test experience if needed)
- Complete a test transaction
- Verify the admission token was validated
2. Check Logging
Monitor your application logs for any Fanfare-related errors:
# Vercel
vercel logs --follow
# Docker
docker logs -f your-container
3. Verify Webhooks
Trigger a test event and verify your webhook endpoint receives it:
- Go to Fanfare Dashboard > Webhooks
- Click “Send Test Event”
- Verify your endpoint returns 200 OK
4. Monitor Metrics
Check your Fanfare dashboard for:
- Request volume
- Error rates
- Average queue times
- Conversion rates
Rollback Plan
Have a rollback plan ready:
- Feature Flag: Use a feature flag to disable Fanfare integration quickly
- Fallback UI: Show a fallback message if Fanfare is unavailable
- Previous Version: Keep the previous deployment available for quick rollback
function ProductPage() {
const fanfareEnabled = useFeatureFlag("fanfare_enabled");
if (!fanfareEnabled) {
return <FallbackProductPage />;
}
return (
<FanfareProvider {...config}>
<ProductWithQueue />
</FanfareProvider>
);
}
Next Steps