6.3: FastAPI Backend Setup
Understand domain names and DNS to connect custom domains to your deployed websites. Learn how DNS works, how to register domains, and configure DNS records to point your domain to your hosting platform.
1. Introduction to Backend Development
What is a Backend API?
A backend API (Application Programming Interface) is server-side code that:
- Processes data and business logic
- Manages databases
- Handles authentication and authorization
- Serves data to frontend applications
Frontend + Backend Architecture:
Vue.js Frontend (Browser)
↓ HTTP requests
FastAPI Backend (Server)
↓
Database (PostgreSQL, MongoDB, etc.)
Why FastAPI?
FastAPI is a modern Python web framework that offers:
- Fast: High performance (comparable to Node.js)
- Easy: Simple syntax, built on Python
- Type safety: Uses Python type hints
- Automatic docs: Interactive API documentation
- Async support: Handle many requests efficiently
Perfect for Python developers learning web development!
Comparison with other frameworks:
| Framework | Language | Speed | Learning Curve |
|---|---|---|---|
| FastAPI | Python | Fast | Easy |
| Django | Python | Medium | Moderate |
| Express | Node.js | Fast | Easy |
| Flask | Python | Slow | Easy |
2. Setting Up FastAPI
Prerequisites
- Python 3.8+ installed
- Basic Python knowledge (you have this!)
- pip (Python package manager)
Check Python version:
python --version
# or
python3 --version
Create a Project
Step 1: Create project directory
mkdir my-api
cd my-api
Step 2: Create virtual environment
# Create virtual environment
python -m venv venv
# Activate it
# On macOS/Linux:
source venv/bin/activate
# On Windows:
venv\Scripts\activate
You should see (venv) in your terminal prompt.
Step 3: Install FastAPI and Uvicorn
pip install fastapi uvicorn[standard]
Explanation:
fastapi: The web frameworkuvicorn: ASGI server to run FastAPI
Step 4: Create requirements.txt
pip freeze > requirements.txt
This file lists all dependencies for deployment.
3. Your First FastAPI Application
Create main.py
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "query": q}
Explanation:
app = FastAPI(): Creates FastAPI application@app.get("/"): Defines GET endpoint at root- Type hints:
item_id: intprovides automatic validation q: str = None: Optional query parameter
Run the Server
uvicorn main:app --reload
Explanation:
main: Filename (main.py)app: FastAPI instance variable--reload: Auto-restart on code changes (development only)
Output:
INFO: Uvicorn running on http://127.0.0.1:8000
INFO: Application startup complete.
Test Your API
Open browser and visit:
- Root endpoint: http://127.0.0.1:8000
- Returns:
{"message": "Hello, FastAPI!"}
- Returns:
- Item endpoint: http://127.0.0.1:8000/items/42?q=test
- Returns:
{"item_id": 42, "query": "test"}
- Returns:
- Interactive docs: http://127.0.0.1:8000/docs
- Automatic API documentation (Swagger UI)
- Test endpoints directly in browser!
- Alternative docs: http://127.0.0.1:8000/redoc
- ReDoc documentation
4. Building a RESTful API
REST Principles
REST (Representational State Transfer) uses HTTP methods:
| Method | Purpose | Example |
|---|---|---|
| GET | Retrieve data | Get user list |
| POST | Create data | Create new user |
| PUT | Update/replace | Update user |
| PATCH | Partial update | Update user email |
| DELETE | Delete data | Delete user |
Example: Todo API
Create a simple todo API:
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
app = FastAPI(title="Todo API", version="1.0.0")
# Data model
class Todo(BaseModel):
id: Optional[int] = None
title: str
description: Optional[str] = None
completed: bool = False
# In-memory database (for learning)
todos: List[Todo] = []
todo_id_counter = 1
# Endpoints
@app.get("/")
def root():
return {"message": "Todo API is running"}
@app.get("/todos", response_model=List[Todo])
def get_todos():
"""Get all todos"""
return todos
@app.get("/todos/{todo_id}", response_model=Todo)
def get_todo(todo_id: int):
"""Get a specific todo"""
for todo in todos:
if todo.id == todo_id:
return todo
raise HTTPException(status_code=404, detail="Todo not found")
@app.post("/todos", response_model=Todo)
def create_todo(todo: Todo):
"""Create a new todo"""
global todo_id_counter
todo.id = todo_id_counter
todo_id_counter += 1
todos.append(todo)
return todo
@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: int, updated_todo: Todo):
"""Update a todo"""
for i, todo in enumerate(todos):
if todo.id == todo_id:
updated_todo.id = todo_id
todos[i] = updated_todo
return updated_todo
raise HTTPException(status_code=404, detail="Todo not found")
@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):
"""Delete a todo"""
for i, todo in enumerate(todos):
if todo.id == todo_id:
todos.pop(i)
return {"message": "Todo deleted"}
raise HTTPException(status_code=404, detail="Todo not found")
Key concepts:
1. Pydantic Models:
class Todo(BaseModel):
id: Optional[int] = None
title: str
completed: bool = False
- Automatic validation
- Type checking
- JSON serialization
2. Path Parameters:
@app.get("/todos/{todo_id}")
def get_todo(todo_id: int):
3. Request Body:
@app.post("/todos")
def create_todo(todo: Todo):
4. Response Models:
@app.get("/todos", response_model=List[Todo])
5. Error Handling:
raise HTTPException(status_code=404, detail="Todo not found")
Test the API
Using the interactive docs (http://127.0.0.1:8000/docs):
- Try POST /todos:
{
"title": "Learn FastAPI",
"description": "Complete the FastAPI tutorial",
"completed": false
}
- Try GET /todos - see your created todo
- Try PUT /todos/1 to update
- Try DELETE /todos/1 to delete
5. Handling CORS
What is CORS?
CORS (Cross-Origin Resource Sharing) is a security feature that restricts:
Frontend (http://localhost:3000)
↓ Request
Backend (http://localhost:8000)
↓ Blocked by browser!
Why? Browsers block requests from different origins (domain/port) for security.
Enable CORS in FastAPI
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:3000", # Vue dev server
"http://localhost:5173", # Vite dev server
"https://myapp.netlify.app", # Production frontend
],
allow_credentials=True,
allow_methods=["*"], # Allow all methods
allow_headers=["*"], # Allow all headers
)
# Your routes...
For development (less secure):
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Security note: In production, specify exact origins!
6. Connecting Vue.js to FastAPI
Vue.js Setup
Install Axios (HTTP client):
npm install axios
Create API service (src/services/api.js):
// src/services/api.js
import axios from 'axios'
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000',
headers: {
'Content-Type': 'application/json'
}
})
export default {
// Get all todos
getTodos() {
return apiClient.get('/todos')
},
// Get single todo
getTodo(id) {
return apiClient.get(`/todos/${id}`)
},
// Create todo
createTodo(todo) {
return apiClient.post('/todos', todo)
},
// Update todo
updateTodo(id, todo) {
return apiClient.put(`/todos/${id}`, todo)
},
// Delete todo
deleteTodo(id) {
return apiClient.delete(`/todos/${id}`)
}
}
Vue Component Example
<!-- src/components/TodoList.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import api from '@/services/api'
const todos = ref([])
const newTodo = ref('')
const loading = ref(false)
const error = ref(null)
async function fetchTodos() {
loading.value = true
try {
const response = await api.getTodos()
todos.value = response.data
} catch (err) {
error.value = 'Failed to fetch todos'
console.error(err)
} finally {
loading.value = false
}
}
async function addTodo() {
if (!newTodo.value.trim()) return
try {
const response = await api.createTodo({
title: newTodo.value,
completed: false
})
todos.value.push(response.data)
newTodo.value = ''
} catch (err) {
error.value = 'Failed to create todo'
console.error(err)
}
}
async function deleteTodo(id) {
try {
await api.deleteTodo(id)
todos.value = todos.value.filter(t => t.id !== id)
} catch (err) {
error.value = 'Failed to delete todo'
console.error(err)
}
}
onMounted(() => {
fetchTodos()
})
</script>
<template>
<div class="todo-list">
<h1>Todo List</h1>
<div v-if="error" class="error">{{ error }}</div>
<div class="add-todo">
<input
v-model="newTodo"
@keyup.enter="addTodo"
placeholder="Add a new todo"
>
<button @click="addTodo">Add</button>
</div>
<div v-if="loading">Loading...</div>
<ul v-else>
<li v-for="todo in todos" :key="todo.id">
<span>{{ todo.title }}</span>
<button @click="deleteTodo(todo.id)">Delete</button>
</li>
</ul>
</div>
</template>
<style scoped>
.todo-list {
max-width: 600px;
margin: 2rem auto;
}
.error {
background: #ffebee;
color: #c62828;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.add-todo {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
input {
flex: 1;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 0.5rem 1rem;
background: #42b883;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #35a372;
}
li {
display: flex;
justify-content: space-between;
padding: 0.75rem;
border-bottom: 1px solid #eee;
}
</style>
Environment Variables
Create .env file:
# .env
VITE_API_URL=http://localhost:8000
Production .env:
# .env.production
VITE_API_URL=https://my-api.railway.app
7. Deploying FastAPI
Deployment Options
Free hosting platforms:
- Railway - Easiest, generous free tier
- Render - Simple, free tier available
- Fly.io - Good free tier
- PythonAnywhere - Free tier for simple APIs
Deploy to Railway
Step 1: Prepare for deployment
Create Procfile:
# Procfile
web: uvicorn main:app --host 0.0.0.0 --port $PORT
Update requirements.txt:
pip freeze > requirements.txt
Create runtime.txt (specify Python version):
python-3.11.0
Step 2: Push to GitHub
git init
git add .
git commit -m "Initial FastAPI app"
git remote add origin https://github.com/yourusername/my-api.git
git push -u origin main
Step 3: Deploy on Railway
- Go to https://railway.app
- Sign up with GitHub
- Click "New Project" → "Deploy from GitHub repo"
- Select your repository
- Railway auto-detects Python and deploys!
Step 4: Get your URL
Railway provides a URL like: https://my-api.railway.app
Step 5: Test deployment
Visit: https://my-api.railway.app/docs
Deploy to Render
Step 1: Create render.yaml
# render.yaml
services:
- type: web
name: my-api
env: python
buildCommand: pip install -r requirements.txt
startCommand: uvicorn main:app --host 0.0.0.0 --port $PORT
Step 2: Push to GitHub
Step 3: Connect to Render
- Go to https://render.com
- Sign up with GitHub
- New → Web Service
- Connect your repository
- Render auto-deploys!
8. Database Integration
SQLite (Simple)
Install SQLAlchemy:
pip install sqlalchemy
Database setup:
# database.py
from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./todos.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# Todo model
class TodoDB(Base):
__tablename__ = "todos"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
description = Column(String, nullable=True)
completed = Column(Boolean, default=False)
# Create tables
Base.metadata.create_all(bind=engine)
Update main.py:
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from database import SessionLocal, TodoDB
from pydantic import BaseModel
app = FastAPI()
# Pydantic model
class Todo(BaseModel):
title: str
description: str = None
completed: bool = False
class Config:
orm_mode = True
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/todos")
def create_todo(todo: Todo, db: Session = Depends(get_db)):
db_todo = TodoDB(**todo.dict())
db.add(db_todo)
db.commit()
db.refresh(db_todo)
return db_todo
@app.get("/todos")
def get_todos(db: Session = Depends(get_db)):
return db.query(TodoDB).all()
PostgreSQL (Production)
For production, use PostgreSQL:
pip install psycopg2-binary
Database URL:
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@host:5432/dbname"
Or use environment variable:
import os
SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL")
Railway provides free PostgreSQL:
- Add PostgreSQL plugin in Railway
- Copy DATABASE_URL
- Use in your app
9. Authentication Basics
Simple API Key Authentication
# main.py
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
API_KEY = "your-secret-api-key"
async def verify_api_key(x_api_key: str = Header()):
if x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API Key")
return x_api_key
@app.get("/protected")
async def protected_route(api_key: str = Depends(verify_api_key)):
return {"message": "You have access!"}
Usage from frontend:
axios.get('/protected', {
headers: {
'X-API-Key': 'your-secret-api-key'
}
})
JWT Authentication (Advanced)
Install dependencies:
pip install python-jose[cryptography] passlib[bcrypt] python-multipart
Basic JWT setup:
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
SECRET_KEY = "your-secret-key-change-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
@app.post("/login")
def login(username: str, password: str):
# Verify credentials (simplified)
if username == "admin" and password == "password":
access_token = create_access_token(data={"sub": username})
return {"access_token": access_token, "token_type": "bearer"}
raise HTTPException(status_code=401, detail="Invalid credentials")
10. Knowledge Check
Question 1: What is the purpose of CORS?
Show answer
CORS (Cross-Origin Resource Sharing) controls which domains can make requests to your API. It's a security feature to prevent unauthorized websites from accessing your backend.Question 2: Why use Pydantic models in FastAPI?
Show answer
Pydantic models provide automatic data validation, type checking, and JSON serialization/deserialization, making APIs more robust and reducing errors.Question 3: What's the difference between GET and POST?
Show answer
GET retrieves data from the server, POST sends data to create new resources. GET is idempotent (safe to repeat), POST is not.Question 4: How do you connect a Vue app to FastAPI?
Show answer
Use Axios to make HTTP requests to FastAPI endpoints, configure CORS on the backend, and use environment variables to store the API URL.Question 5: Where should you store sensitive API keys?
Show answer
In environment variables, never commit them to Git. Use .env files locally and platform environment variables in production.11. Practical Exercises
Exercise 6.3.1: Build a Simple API
- Create a FastAPI app with GET and POST endpoints
- Implement a simple in-memory data store
- Test with the interactive docs
- Deploy to Railway
Exercise 6.3.2: Connect Vue to FastAPI
- Create a Vue.js frontend
- Set up Axios API service
- Connect to your FastAPI backend
- Implement full CRUD operations
Exercise 6.3.3: Add Database
- Integrate SQLite with SQLAlchemy
- Create database models
- Update endpoints to use database
- Test persistence
Exercise 6.3.4: Deploy Full Stack
- Deploy FastAPI to Railway
- Deploy Vue to Vercel/Netlify
- Configure CORS for production
- Test the full application
12. Key Takeaways
- FastAPI is a modern, fast Python framework perfect for APIs
- RESTful APIs use HTTP methods (GET, POST, PUT, DELETE) consistently
- Pydantic models provide automatic validation and type safety
- CORS must be configured to allow frontend-backend communication
- Deployment is easy with Railway, Render, or other platforms
- Environment variables keep configuration flexible and secure
- Database integration enables data persistence
- Authentication protects sensitive endpoints
- Interactive docs at
/docsmake testing easy - Type hints enable automatic validation and better developer experience
13. Next Steps
Excellent work! You now know how to build and deploy backend APIs with FastAPI.
In Lesson 6.4: Full-Stack Deployment & Production Best Practices, you'll learn how to deploy complete full-stack applications, optimize performance, implement monitoring, and follow production best practices.
Ready to master production deployment? Let's finish strong!
Further Resources
Official Documentation:
Tutorials:
Deployment Platforms:
Database Tutorials: