Skip to content

toruiwasa/roller-dashboard

Repository files navigation

Blue Mountains Adventure Park — Operations Dashboard

A portfolio project demonstrating production-grade API integration with ROLLER, a booking and ticketing platform used by tourism attractions. Built around a fictional Blue Mountains venue.

Built as a portfolio project to demonstrate real-world API integration patterns. Runs fully on mock data out of the box; switching to the live ROLLER API requires only a single environment variable change.


Tech stack

Layer Technology
Framework Next.js 16 (App Router)
Language TypeScript
Data fetching TanStack Query
Charts Recharts
Timezone date-fns-tz
Validation Zod
Mock data @faker-js/faker
Testing Vitest
Deployment Vercel

Architecture

Browser
  └── React components ('use client')
        └── useRoller.ts (TanStack Query hooks)
              └── rollerFetch('/v1/...')
                    │
                    ├── USE_MOCK=true  → Next.js API Route (/api/v1/...)
                    │                       └── generators.ts (faker data)
                    │
                    └── USE_MOCK=false → auth.ts (OAuth2 token)
                                          └── https://api.roller.app/v1/...

Security note: ROLLER_CLIENT_SECRET lives only in server-side environment variables and is never exposed to the browser. Next.js API Routes act as a proxy between the client and the ROLLER API.


Getting started

git clone https://github.com/your-username/roller-dashboard
cd roller-dashboard
npm install
npm run dev
# → http://localhost:3000

The app starts in mock mode (NEXT_PUBLIC_USE_MOCK_API=true) and requires no API keys.


Environment variables

# .env.local

# Toggle mock / production mode
NEXT_PUBLIC_USE_MOCK_API=true
NEXT_PUBLIC_BASE_URL=http://localhost:3000

# Production only — from ROLLER Dashboard → Settings → Integrations → API Keys
# ROLLER_CLIENT_ID=your_client_id
# ROLLER_CLIENT_SECRET=your_client_secret

# CORS allowed origin (set to your Vercel deployment URL)
# NEXT_PUBLIC_APP_URL=https://your-app.vercel.app

# Rate limiting via Upstash Redis (optional — falls back to in-memory)
# UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
# UPSTASH_REDIS_REST_TOKEN=your_token

Switching to live ROLLER API

  1. Set NEXT_PUBLIC_USE_MOCK_API=false
  2. Add ROLLER_CLIENT_ID and ROLLER_CLIENT_SECRET
  3. Restart the dev server (or redeploy)

No code changes required.


Key implementation details

OAuth2 client_credentials flow (src/lib/roller/auth.ts)

  • Fetches a Bearer token from https://auth.roller.app/oauth/token
  • Caches the token in module-level memory; reuses it until 30 seconds before expiry
  • clearTokenCache() is called on 401 responses to force re-authentication

API client with retry logic (src/lib/roller/client.ts)

  • 401 — clears token cache and retries (up to 2 times)
  • 429 — reads Retry-After header, waits, then retries
  • 5xx — throws immediately (no retry)

Zod schema validation (src/types/roller.ts)

Types are derived from Zod schemas via z.infer<>, eliminating dual maintenance of interfaces and runtime validators. Both query parameters and API responses are validated at the route handler level.

Timezone conversion

ROLLER returns all timestamps in UTC. date-fns-tz converts them to Australia/Sydney, which automatically handles the AEST (UTC+10) / AEDT (UTC+11) daylight saving transition.

Security headers (next.config.ts)

Production builds include X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Strict-Transport-Security, and Content-Security-Policy.

CORS and rate limiting (src/middleware.ts)

CORS is restricted to the configured app origin. Rate limiting is applied to all /api/* routes: 100 requests per minute per IP, backed by Upstash Redis in production with an in-memory fallback for local development.


Running tests

npm test              # run all tests once
npm run test:watch    # watch mode
npm run test:coverage # with coverage report

129 tests across 6 files covering (1 skipped):

  • OAuth2 token cache boundary values (30-second pre-expiry window)
  • 401 / 429 retry logic and edge cases
  • Zod schema equivalence partitions and boundary values
  • Rate limit window reset (including now === resetAt boundary)
  • Middleware CORS and IP resolution logic
  • Mock data generators

Deploy to Vercel

vercel deploy

Set the following in Vercel → Settings → Environment Variables:

Variable Value
NEXT_PUBLIC_USE_MOCK_API false
ROLLER_CLIENT_ID from ROLLER dashboard
ROLLER_CLIENT_SECRET from ROLLER dashboard
NEXT_PUBLIC_APP_URL your Vercel URL
UPSTASH_REDIS_REST_URL from Upstash (optional)
UPSTASH_REDIS_REST_TOKEN from Upstash (optional)

Known limitations

  • Authentication — the dashboard has no user authentication. For production use, add NextAuth.js with Google Workspace or Microsoft 365 SSO and protect all routes via middleware.ts.
  • Rate limit persistence — without Upstash, the in-memory rate limiter is not shared across serverless instances.
  • ROLLER API access — the ROLLER API requires a paid subscription add-on. This project uses faker-generated mock data that matches the documented response schema.

About

A portfolio project demonstrating production-grade API integration with ROLLER, a booking and ticketing platform used by tourism attractions. Built around a fictional Blue Mountains venue.

Topics

Resources

Stars

Watchers

Forks

Contributors