React 19 + TypeScript + Vite application with OpenAPI-driven development and BFF authentication.
- Prerequisites
- Getting Started
- Development Modes
- Authentication
- Testing
- API Client
- Project Structure
- Scripts Reference
- FAQ
- Troubleshooting
- Node.js 25+
- npm 10+
Install dependencies:
npm installGenerate API client from OpenAPI specification:
npm run api:generateDevelop with a running backend server:
npm run devRequirements: Backend (or Gateway) must be running on http://localhost:8080
How it works:
- Vite proxies
/api/*requests to the backend at port 8080 - Vite also proxies auth routes (
/login-options,/oauth2,/login,/logout) to the Gateway - Full CRUD operations with real database
Use when:
- Developing features that require backend logic
- Testing full-stack integration
- Working with real data and OAuth2 authentication
Develop frontend independently using Prism mock server:
npm run dev:mockRequirements: None! No backend needed.
How it works:
- Starts Prism mock server on port 4010 (serves OpenAPI examples)
- Starts Vite dev server with hot reload
- Sets
VITE_AUTH_MODE=mockto bypass OAuth2 and use a hardcoded mock user - Sets
VITE_USE_PRISM=trueto proxy/apirequests to Prism on port 4010 - Returns realistic data from OpenAPI specification
Use when:
- Backend is unavailable or not started
- Developing UI components independently
- Rapid prototyping without backend or Keycloak setup
- Backend developers testing frontend requirements
Example response (from OpenAPI examples):
{
"data": [
{
"id": 1001,
"reference": "GRE-2025-000001",
"message": "Hello, World!",
"recipient": "World",
"createdAt": "2025-01-15T10:30:00Z"
}
],
"meta": {
"pageNumber": 0,
"pageSize": 20,
"totalElements": 3,
"totalPages": 1
}
}npm run dev:mock-realLike dev:mock but with VITE_AUTH_MODE=real -- uses Prism for API data while still integrating with real OAuth2 flows via the Gateway. Used by Playwright mock E2E tests.
Run only the mock API server (useful for backend developers):
npm run mock-apiAccess: http://localhost:4010/v1/greetings
Use when:
- Backend developers need to test API contract compliance
- Testing API client libraries
- Generating sample requests/responses
The frontend uses the BFF (Backend-for-Frontend) pattern for authentication. Tokens are never stored in the browser.
- The frontend calls
GET /api/v1/meto check the current session - If not authenticated, it redirects to
/login-options(served by the Gateway) - The Gateway handles the OAuth2 Authorization Code Flow with Keycloak
- After login, the session is maintained via an
HttpOnlycookie - The API client automatically includes
credentials: "include"for cookie-based auth
The API client reads the XSRF-TOKEN cookie and attaches it as the X-XSRF-TOKEN header on all state-changing requests (POST, PUT, PATCH, DELETE).
On 401 responses, the API client dispatches a auth:session-expired custom event on globalThis, which the AuthProvider listens to for triggering re-authentication.
In dev:mock mode (VITE_AUTH_MODE=mock), all authentication is bypassed with a hardcoded mock user. This allows full UI development without Keycloak or the Gateway.
npm run test # Run tests in watch mode
npm run test:coverage # Run tests with V8 coverage reportTest Stack:
- Vitest (test runner)
- React Testing Library
- MSW (Mock Service Worker) for API mocking
How API mocking works in tests:
- MSW intercepts network requests at the application level
- Uses same request handlers for all tests (
src/test/mocks/handlers.ts) - Stateful in-memory store for CRUD testing with auth guards
- No need to run mock server during tests
npm run test:e2eStarts a Vite preview server on port 4173 and runs Playwright against it. API calls can be mocked via page.route() in the test files.
npm run test:e2e:mockUses dev:mock-real (Prism + real auth mode) as the webserver. Prism starts automatically.
npm run test:e2e:ciSame as test:e2e but with --reporter=line for CI-friendly output.
E2E test coverage:
e2e/hello.spec.ts-- Greetings CRUD flow and paginatione2e/auth.spec.ts-- Authentication states (anonymous vs. authenticated)e2e/bff-integration.spec.ts-- BFF patterns: cookie auth, CSRF, session expiry, OAuth2 login/logout
API client is auto-generated from OpenAPI specification:
npm run api:generateSource: ../api/specification/openapi.yaml
Output: src/api/generated/
Generated files:
sdk.gen.ts- API functions (listGreetings, createGreeting, etc.)types.gen.ts- TypeScript interfacesclient.gen.ts- HTTP client configuration
The API client is configured with:
- Base URL from
VITE_API_URLenv var (defaults to/api) - Credentials:
includefor cookie-based BFF authentication - CSRF: Automatically reads
XSRF-TOKENcookie and sendsX-XSRF-TOKENheader - Session expiry: Dispatches
auth:session-expiredevent on 401 responses - Error handling: RFC 7807
ProblemDetailparsing viasrc/api/errors.ts
import { listGreetings, createGreeting } from './api/generated';
// List greetings with pagination
const response = await listGreetings({
query: { page: 0, size: 20 }
});
// Create a new greeting
const newGreeting = await createGreeting({
body: {
message: "Hello, World!",
recipient: "World"
}
});All requests and responses are fully typed:
import type {
GreetingResponse,
CreateGreetingRequest,
GreetingPage
} from './api/generated';frontend/
├── src/
│ ├── api/
│ │ ├── generated/ # Auto-generated API client (don't edit!)
│ │ ├── config.ts # Client init, CSRF, session expiry, re-exports
│ │ ├── config.test.ts # Client configuration tests
│ │ ├── errors.ts # RFC 7807 ProblemDetail parsing
│ │ └── errors.test.ts # Error handling tests
│ ├── features/
│ │ ├── greetings/ # Greeting feature module
│ │ │ ├── components/ # React components + tests
│ │ │ └── hooks/ # Custom React hooks + tests
│ │ └── auth/ # Authentication feature module
│ │ ├── AuthProvider.tsx # Context provider (session, login, logout)
│ │ ├── hooks.ts # useAuth() hook
│ │ ├── types.ts # AuthStatus, AuthMode, AuthContextValue
│ │ └── utils.ts # resolveAuthMode(), resolveLoginUri()
│ ├── test/
│ │ └── mocks/
│ │ ├── handlers.ts # MSW request handlers (CRUD + auth guards)
│ │ ├── data.ts # Mock data factories
│ │ └── server.ts # MSW server setup
│ ├── App.tsx # Root component
│ └── main.tsx # Entry: initApiClient + AuthProvider
├── e2e/ # Playwright E2E tests
│ ├── hello.spec.ts # Greetings CRUD flow
│ ├── auth.spec.ts # Authentication states
│ └── bff-integration.spec.ts # BFF patterns (CSRF, session, OAuth2)
├── playwright.config.ts # E2E config (Vite preview, port 4173)
├── playwright.config.mock.ts # E2E config (Prism, port 5173)
├── openapi-ts.config.ts # API client generator config
├── vite.config.ts # Vite bundler + proxy config
├── vitest.config.ts # Vitest test runner config
├── Dockerfile # Multi-stage build (Node + nginx)
├── nginx.conf # Production nginx configuration
└── package.json # Dependencies & scripts
| Script | Description |
|---|---|
npm run dev |
Start development server (requires backend) |
npm run dev:mock |
Start development with Prism mock + mock auth (no backend) |
npm run dev:mock-real |
Start development with Prism mock + real OAuth2 auth |
npm run mock-api |
Start Prism mock server only (port 4010) |
npm run build |
Build for production |
npm run preview |
Preview production build (port 4173) |
npm run test |
Run unit tests in watch mode |
npm run test:coverage |
Run tests with V8 coverage |
npm run test:e2e |
Run E2E tests (Vite preview) |
npm run test:e2e:mock |
Run E2E tests against Prism |
npm run test:e2e:ci |
Run E2E tests with CI reporter |
npm run api:generate |
Generate API client from OpenAPI spec |
npm run lint |
Lint code with ESLint |
npm run lint:report |
Lint and output JSON report (for SonarQube) |
npm run typecheck |
Type-check with TypeScript |
npm run format |
Format code with Prettier |
npm run format:check |
Check formatting without writing |
A: Check which mode you're using:
- Real backend: Ensure backend/Gateway is running on port 8080
- Mock mode: Use
npm run dev:mockinstead ofnpm run dev
A: Mock data comes from OpenAPI examples. Update the specification:
- File:
../api/specification/openapi.yaml - Add/modify
examplesin response definitions - Restart mock server
A: Yes! Edit test mock handlers:
- File:
src/test/mocks/handlers.ts - Add custom logic for specific test scenarios
- Use
mockErrorsutilities for error responses
A:
- Backend:
npm run dev(default, proxies to backend on :8080) - Mock:
npm run dev:mock(setsVITE_USE_PRISM=true+VITE_AUTH_MODE=mock) - No restart needed - just kill one and start the other
A:
- MSW: In-process mocking for unit/integration tests (Node.js)
- Prism: Standalone HTTP server for development (separate process on port 4010)
- Both: Serve OpenAPI-compliant responses
# Find process using port 4010
netstat -ano | findstr :4010 # Windows
lsof -i :4010 # macOS/Linux# Ensure OpenAPI spec exists
ls ../api/specification/openapi.yaml
# Regenerate client
npm run api:generate
# Check for errors in openapi-ts.config.ts- Check Vite dev server is running (default port: 5173)
- Verify proxy configuration in
vite.config.ts - In mock mode, ensure Prism is running on port 4010
- In real mode, ensure backend/Gateway is running on port 8080
- Check browser DevTools Network tab for proxy errors