A hands-on tutorial that teaches you the production-grade patterns of building full-stack applications with FastAPI (Python), React (TypeScript), and React Native (Expo). Every file is heavily commented to explain not just what the code does, but why it's done that way.
One API, Multiple Clients — the same backend powers both web and mobile apps.
- Backend: Build a REST API with FastAPI, Pydantic validation, and proper error handling
- Web Frontend: Create a React app with TypeScript, controlled components, and API integration
- Mobile App: Build a React Native app with Expo that consumes the same API
- Full-Stack Patterns: Connect frontend to backend with CORS, environment variables, and type safety
- Production Practices: Error handling, loading states, separation of concerns, and clean architecture
- CRUD Operations: Implement Create, Read, Update, Delete — the foundation of most apps
| Layer | Technology | Purpose |
|---|---|---|
| Backend | FastAPI | Modern Python web framework with automatic OpenAPI docs |
| Validation | Pydantic | Data validation and serialization |
| Web Frontend | React + TypeScript | Type-safe UI components |
| Web Styling | React-Bootstrap | Pre-built Bootstrap 5 components |
| Web Build Tool | Vite | Fast frontend development server |
| Mobile App | Expo + React Native | Cross-platform mobile development |
| Mobile Navigation | Expo Router | File-based routing for React Native |
| Package Manager | uv (Python) / npm (Node) | Dependency management |
Before you start, make sure you have these installed:
| Tool | Version | Installation |
|---|---|---|
| Python | 3.11+ | python.org |
| uv | Latest | docs.astral.sh/uv |
| Node.js | 18+ | nodejs.org |
| npm | 9+ | Comes with Node.js |
| Expo Go | Latest | App Store / Play Store (for mobile) |
git clone https://github.com/YOUR_USERNAME/learn-fastapi-react.git
cd learn-fastapi-reactcd backend
uv sync # Install Python dependencies
uv run fastapi dev main.py # Start FastAPI dev serverThe API will be running at http://localhost:8000
Interactive docs at http://localhost:8000/docs
cd frontend
cp .env.example .env # Create environment file
npm install # Install Node dependencies
npm run dev # Start Vite dev serverThe web app will be running at http://localhost:5173
Mobile devices can't access localhost — you need a tunnel or local IP.
Option A: Using ngrok (works anywhere)
# Terminal 1: Start ngrok tunnel
ngrok http 8000
# Copy the https URL (e.g., https://abc123.ngrok-free.app)# Terminal 2: Start mobile app
cd mobile
cp .env.example .env
# Edit .env and set EXPO_PUBLIC_API_BASE_URL to your ngrok URL
npm install
npx expo startOption B: Using local IP (same WiFi only)
cd mobile
cp .env.example .env
# Edit .env and set EXPO_PUBLIC_API_BASE_URL=http://YOUR_LOCAL_IP:8000
npm install
npx expo startScan the QR code with Expo Go app on your phone.
- Open http://localhost:5173 in your browser (web)
- Scan QR code with Expo Go (mobile)
- Create a user — it appears on BOTH web and mobile!
- Check http://localhost:8000/docs to see the API documentation
learn-fastapi-react/
├── backend/ # FastAPI backend
│ ├── main.py # App entry point, CORS config
│ ├── models.py # Pydantic models (data shapes)
│ ├── database.py # Data storage layer
│ └── routers/
│ └── users.py # CRUD endpoints
│
├── frontend/ # React web frontend
│ ├── src/
│ │ ├── main.tsx # React entry point
│ │ ├── App.tsx # Main component (state + logic)
│ │ ├── types.ts # TypeScript interfaces
│ │ ├── api/
│ │ │ └── users.ts # API client functions
│ │ └── components/
│ │ ├── UserForm.tsx # Form component
│ │ └── UserList.tsx # List component
│ ├── .env.example # Environment template
│ └── .env # Your local config (git-ignored)
│
├── mobile/ # React Native mobile app (Expo)
│ ├── app/
│ │ ├── _layout.tsx # Root layout (navigation)
│ │ └── index.tsx # Home screen (state + logic)
│ ├── src/
│ │ ├── types.ts # TypeScript interfaces (same as web!)
│ │ ├── api/
│ │ │ └── users.ts # API client (same pattern as web!)
│ │ └── components/
│ │ ├── UserForm.tsx # Native form component
│ │ └── UserList.tsx # Native list component
│ ├── .env.example # Environment template
│ └── .env # Your local config (git-ignored)
│
└── README.md # You are here!
This isn't a "copy-paste and hope it works" tutorial. It's designed for you to read and understand the code.
- Every file is heavily commented — explaining patterns, decisions, and connections between frontend and backend
- Follow the reading order below — it's structured to build understanding layer by layer
- Open files side by side — when a frontend file references a backend endpoint, open both and trace the flow
- Experiment — break things, add console.logs, change values, see what happens
- "Why is this code here and not somewhere else?"
- "What would break if I removed this?"
- "How does this frontend code connect to the backend?"
backend/models.py— Data shapes (Pydantic)backend/database.py— Storage layerbackend/routers/users.py— CRUD endpointsbackend/main.py— App setup & CORS
frontend/src/types.ts— TypeScript interfaces (mirrors backend models)frontend/src/api/users.ts— API client (calls backend)frontend/src/main.tsx— Entry pointfrontend/src/App.tsx— Main component (state + logic)frontend/src/components/UserForm.tsx— Form componentfrontend/src/components/UserList.tsx— List component
mobile/src/types.ts— Same interfaces as web!mobile/src/api/users.ts— Same API client patternmobile/app/_layout.tsx— Expo Router layout (like React Router)mobile/app/index.tsx— Main screen (compare to frontend/src/App.tsx)mobile/src/components/UserForm.tsx— Native form (TextInput vs input)mobile/src/components/UserList.tsx— Native list (View + map vs ul/li)
| Pattern | Where |
|---|---|
| Pydantic validation | backend/models.py |
| REST CRUD endpoints | backend/routers/users.py |
| CORS configuration | backend/main.py |
| API client layer | frontend/src/api/users.ts, mobile/src/api/users.ts |
| Controlled components | frontend/src/components/UserForm.tsx |
| List rendering with keys | frontend/src/components/UserList.tsx |
| Smart/dumb component split | App.tsx vs components/ |
| try/catch/finally | frontend/src/App.tsx, mobile/app/index.tsx |
| React Native primitives | mobile/src/components/*.tsx (View, Text, TextInput) |
| StyleSheet.create() | mobile/src/components/*.tsx |
| Expo Router file-based routing | mobile/app/_layout.tsx, mobile/app/index.tsx |
| Environment variables | .env files (VITE* prefix vs EXPO_PUBLIC* prefix) |
This tutorial goes beyond "it works" to "it works correctly":
| Practice | Implementation |
|---|---|
| Error Handling | try/catch/finally on all async operations, user-friendly error messages |
| Loading States | Loading indicators, disabled buttons during operations (prevents double-submits) |
| Environment Variables | API URL configured via .env, not hardcoded |
| Type Safety | TypeScript interfaces mirror Pydantic models — errors caught at compile time |
| Separation of Concerns | API layer separate from components, smart/dumb component pattern |
| Input Validation | Pydantic validates on backend, HTML5 + required attributes on frontend |
Once you understand this codebase, here's where to go next:
| Topic | What to Add | Resources |
|---|---|---|
| Real Database | Replace in-memory dict with PostgreSQL + SQLAlchemy | FastAPI SQL Databases |
| Authentication | Add JWT tokens, protected routes | FastAPI Security |
| React Router | Multiple pages, navigation | React Router |
| State Management | For complex apps: Zustand, Redux, or TanStack Query | TanStack Query |
| Testing | pytest (backend), Vitest (frontend) | FastAPI Testing |
| Deployment | Docker, Railway, Vercel | FastAPI Deployment |
MIT License — do whatever you want with this code. Learn from it, modify it, use it in your projects. No attribution required (but appreciated!).
Happy coding! If this helped you, consider starring the repo ⭐