14 KiB
PHASE 1: Foundation + Next.js 15 Setup
Status: 🟡 Partially Complete (Core Foundation Ready)
Estimated Time: 20-25 hours
Priority: CRITICAL
Depends On: None
Blocks: All other phases
Note: Core infrastructure complete (Tasks 1.0, 1.1, 1.3). Types (1.2), Base Service (1.4), and Testing (1.5) deferred to be created as needed in subsequent phases.
🎯 Phase Goal
Set up Next.js 16 with App Router + Turbopack, configure Bun, establish environment variables, and build the core infrastructure that all services will depend on: API client, type system, error handling, and base service class.
This phase includes:
- Next.js 16 installation and configuration
- Bun package manager setup
- Environment variables configuration
- API client infrastructure
- Type system for Django API
- Service layer foundation
📋 Prerequisites
- Django backend is running at
https://api.thrillwiki.com - You have API access token for testing
- Bun is installed (
curl -fsSL https://bun.sh/install | bash) - Node.js 18+ is installed (for Next.js 15)
- Git repository is initialized
🗂️ Files to Create/Modify
# Next.js Configuration
next.config.js (NEW)
.env.local (NEW)
.env.example (NEW)
tsconfig.json (UPDATE)
package.json (UPDATE)
# Next.js App Structure
app/
├── layout.tsx (NEW)
├── page.tsx (NEW)
├── loading.tsx (NEW)
├── error.tsx (NEW)
└── not-found.tsx (NEW)
# API & Services
lib/
├── api/
│ ├── client.ts (NEW)
│ ├── errorHandler.ts (NEW)
│ └── index.ts (NEW)
└── env.ts (NEW - env validation)
types/
├── api/
│ ├── responses.ts (NEW)
│ ├── requests.ts (NEW)
│ ├── errors.ts (NEW)
│ └── index.ts (NEW)
└── entities/
├── park.ts (NEW)
├── ride.ts (NEW)
└── ... (others)
services/
├── base/
│ ├── BaseService.ts (NEW)
│ └── index.ts (NEW)
└── __tests__/
├── setup.ts (NEW)
└── mocks/ (NEW)
✅ Task 1.0: Next.js 15 + Bun Setup (5 hours)
Step 1: Install Next.js 15 with Bun
# Create Next.js 15 project with Bun
bun create next-app@latest . --typescript --tailwind --app --no-src-dir
# Or if starting fresh in a new directory:
bun create next-app@latest thrillwiki --typescript --tailwind --app --no-src-dir
cd thrillwiki
Step 2: Configure next.config.js
Create next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
// Enable Turbopack for development
experimental: {
turbo: {
resolveAlias: {
'@': './',
},
},
},
// Image optimization for CloudFlare
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'imagedelivery.net',
},
],
formats: ['image/avif', 'image/webp'],
},
// Production optimizations
compress: true,
poweredByHeader: false,
reactStrictMode: true,
};
module.exports = nextConfig;
Step 3: Environment Variables Setup
Create .env.local:
# Django API
NEXT_PUBLIC_DJANGO_API_URL=http://localhost:8000/api/v1
# CloudFlare Images
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=your_account_id
# Supabase (temporary - keep during migration)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
Create .env.example:
# Django API (required)
NEXT_PUBLIC_DJANGO_API_URL=
# CloudFlare Images (required)
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=
# Supabase (temporary - will be removed)
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
Step 4: Environment Variable Validation
Create lib/env.ts:
import { z } from 'zod';
const envSchema = z.object({
NEXT_PUBLIC_DJANGO_API_URL: z.string().url(),
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: z.string().min(1),
});
export const env = envSchema.parse({
NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL,
NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: process.env.NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID,
});
Step 5: Update package.json Scripts
{
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint",
"type-check": "tsc --noEmit"
}
}
Checklist
- Next.js 16 installed with Bun (Next.js 16.0.1)
Turbopack enabled in next.config.js(Removed - deprecated experimental key in Next.js 16)- .env.local created with all variables
- .env.example documented
- Environment validation in lib/env.ts
- package.json scripts use Bun
- Dev server runs with
bun run dev- ✅ Running on http://localhost:3000 - No TypeScript errors
✅ Task 1.1: Base API Client (3 hours)
Checklist
- Install dependencies:
bun add axios zod(axios installed, zod already present) - Create
lib/api/client.ts - Configure base URL from environment variable
- Add JWT token management
- Get token from localStorage
- Attach to Authorization header
- Handle token expiry
- Create request interceptor
- Add auth headers
- Add request logging (dev only)
- Create response interceptor
- Handle success responses
- Handle error responses
- Extract data from Django response format
- Implement retry logic
- Retry on network failures
- Exponential backoff
- Max 3 retries
- Add timeout configuration (30s default)
- Export configured axios instance
Acceptance Criteria
- Can make GET request to
/api/v1/health(client configured) - Token automatically attached to requests
- Errors are properly caught and logged
- Retries work on network failures
- TypeScript types are correct
Implementation Example
// lib/api/client.ts
import axios from 'axios';
import { env } from '@/lib/env';
const apiClient = axios.create({
baseURL: env.NEXT_PUBLIC_DJANGO_API_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor
apiClient.interceptors.request.use((config) => {
// Add auth token if available
if (typeof window !== 'undefined') {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
});
// Response interceptor
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// Handle errors
return Promise.reject(error);
}
);
export { apiClient };
Testing
// Test in app/page.tsx or a Server Component
import { env } from '@/lib/env';
export default async function HomePage() {
// Test API connection
const response = await fetch(`${env.NEXT_PUBLIC_DJANGO_API_URL}/health`);
const data = await response.json();
return <div>API Status: {data.status}</div>;
}
✅ Task 1.2: Type Definitions (2 hours)
Checklist
- Install zod:
bun add zod - Create
types/api/responses.tsApiResponse<T>generic typePaginatedResponse<T>typeSuccessResponse<T>type
- Create
src/types/api/errors.tsApiErrorinterfaceValidationErrorinterfaceErrorDetailinterface
- Create
src/types/api/requests.tsPaginationParamsinterfaceFilterParamsinterfaceSortParamsinterface
- Create entity type files
types/entities/park.tstypes/entities/ride.tstypes/entities/company.tstypes/entities/rideModel.tstypes/entities/submission.tstypes/entities/review.ts
- Create
types/api/index.ts- export all types - Document type usage
Acceptance Criteria
- All types match Django API schema
- No
anytypes used - Types are reusable across services
- Generic types work correctly
- TypeScript compiler is happy
Example Types
// ApiResponse<T>
interface ApiResponse<T> {
data: T;
status: 'success' | 'error';
message?: string;
}
// PaginatedResponse<T>
interface PaginatedResponse<T> {
results: T[];
count: number;
next: string | null;
previous: string | null;
}
// ApiError
interface ApiError {
message: string;
code?: string;
details?: ErrorDetail[];
status: number;
}
✅ Task 1.3: Error Handler Integration (2 hours)
Checklist
- Create
lib/api/errorHandler.ts - Map Django error responses to user messages
- Handle HTTP status codes
- 400 - Validation errors
- 401 - Unauthorized
- 403 - Forbidden
- 404 - Not found
- 429 - Rate limiting
- 500+ - Server errors
- Extract validation errors from Django format
- Format error messages for UI display
- Integrate with existing error logging
- Add Sentry integration (if available) - Deferred to later phase
- Create user-friendly error messages
- Handle network errors separately
Acceptance Criteria
- All Django errors are properly translated
- Validation errors show field-specific messages
- Errors are logged to monitoring
- User sees helpful error messages
- No stack traces exposed to users
Testing
// Test error handling
try {
await apiClient.post('/api/v1/submissions/', { invalid: 'data' });
} catch (error) {
console.log('Caught error:', error.message);
console.log('User message:', formatErrorForUser(error));
}
✅ Task 1.4: Service Base Class (2 hours)
Checklist
- Create
services/base/BaseService.ts - Add API client instance
- Implement common CRUD methods
get<T>(url: string): Promise<T>list<T>(url: string, params?: any): Promise<T[]>create<T>(url: string, data: any): Promise<T>update<T>(url: string, data: any): Promise<T>delete(url: string): Promise<void>
- Add pagination helper methods
getPaginated<T>(url: string, params?: any): Promise<PaginatedResponse<T>>
- Add filtering/sorting helpers
- Add error handling wrapper
- Add loading state management (optional)
- Document usage patterns
- Add TypeScript generics
Acceptance Criteria
- Base service can be extended
- All CRUD operations work
- Pagination works correctly
- Errors are properly handled
- TypeScript types are inferred correctly
Example Usage
// Example service extending BaseService
class ParkService extends BaseService {
async getParks(params?: FilterParams) {
return this.getPaginated('/api/v1/parks/', params);
}
async getPark(id: string) {
return this.get(`/api/v1/parks/${id}/`);
}
}
✅ Task 1.5: Testing Infrastructure (3 hours)
Checklist
- Install MSW:
bun add -d msw @testing-library/react @testing-library/jest-dom vitest - Create
services/__tests__/setup.ts- Initialize MSW
- Configure test environment
- Create
src/services/__tests__/mocks/handlers.ts- Mock Django API responses
- Create realistic test data
- Create
services/__tests__/mocks/server.ts- Set up MSW server
- Configure for Node/Next.js environment
- Create
services/__tests__/utils.ts- Test helper functions
- Mock data factories
- Write example service tests
- Test success cases
- Test error cases
- Test pagination
- Document testing patterns
- Set up CI test runs (if applicable)
Acceptance Criteria
- MSW intercepts API calls in tests
- Tests can run without real API
- Test data is realistic
- All test utilities are documented
- CI passes tests
Example Test
import { describe, it, expect } from 'vitest';
import { ParkService } from '../parks/parkService';
describe('ParkService', () => {
it('should fetch parks', async () => {
const service = new ParkService();
const parks = await service.getParks();
expect(parks).toHaveLength(10);
});
});
🎯 Phase Completion Criteria
Code Quality
- All files created and in correct locations
- Zero TypeScript errors
- Zero linter warnings
- Code is well-documented
- Naming conventions followed
Functionality
- Can make requests to Django API
- Authentication works
- Error handling works
- Base service can be extended
- Tests pass
Testing
- Unit tests for API client
- Unit tests for error handler
- Unit tests for base service
- Test coverage >80%
- All tests pass
📊 Progress Tracking
Started: [Date]
Completed: [Date]
Time Spent: [Hours]
Tasks Completed
- Task 1.0: Next.js 15 + Bun Setup
- Task 1.1: Base API Client
- Task 1.2: Type Definitions - DEFERRED to be created as needed in Phases 2-11
- Task 1.3: Error Handler Integration
- Task 1.4: Service Base Class - DEFERRED to Phase 4 (Entity Services)
- Task 1.5: Testing Infrastructure - DEFERRED to later phases
🚨 Blockers & Issues
Document any issues encountered:
- Issue: [Description]
- Impact: [How it blocks progress]
- Resolution: [How it was resolved]
📝 Notes
Document important decisions and learnings:
- [Date] - [Note about implementation decision]
⏭️ Next Phase
Once this phase is complete, proceed to Phase 2: Authentication
🔗 Related Documentation
- Environment Variables Guide
- Bun Setup Guide
- Next.js 15 Migration Guide
- Next.js Documentation: https://nextjs.org/docs
- Bun Documentation: https://bun.sh/docs
Document Version: 2.0
Last Updated: November 9, 2025
Changes: Added Next.js 15 + App Router + Turbopack + Bun setup