Imagine you’re about to embark on an exciting journey of building a full-stack web application. It’s a rollercoaster ride but in the best way possible. Combining FastAPI for the backend, React for the frontend, and PostgreSQL as your trusty database, you’re all set for turning your ideas into reality. Here’s a laid-back yet thorough walkthrough of how to build this tech marvel from scratch.
First things first, let’s get organized. Think of it as setting up your workbench before you start building something cool. Create a tidy structure that separates your frontend and backend duties. You’ll have two main directories - one for FastAPI and another for React.
mkdir fastapi-react-app
cd fastapi-react-app
mkdir fastapi
mkdir react
Backend Fun with FastAPI
Alright, time to dive into the backend. Start off by setting up a virtual environment for your FastAPI project. This ensures all the magic happens in its own bubble, without messing up your computer.
python3 -m venv fastapi/env
source fastapi/env/bin/activate
Next, you’ll need some key ingredients to make your backend delicious. Install these dependencies:
pip install fastapi uvicorn sqlalchemy psycopg2-binary
Cooking Up the Database
With PostgreSQL as your database, it’s like choosing the right kind of flour for baking the perfect bread. Ensure the PostgreSQL client library is installed, and create your database. Here’s how to handle database connections neatly in a database.py
file inside your FastAPI directory:
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Designing Models and Schemas
Ever tried assembling a piece of IKEA furniture? Defining database models using SQLAlchemy is similar. Create a models.py
file that lays the groundwork for your app’s data structure:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from .database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
Schemas are like blueprints. They ensure the data is shaped correctly as it flows through your app. Check out this example:
from pydantic import BaseModel
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
orm_mode = True
Crafting API Endpoints
Here’s where the magic happens. Create a main.py
file to set up your FastAPI app and define routes. It’s like mapping out your treasure hunt:
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from . import models, schemas, database
app = FastAPI()
def get_db():
db = database.SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = models.User(email=user.email)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
Frontend Adventures with React
Swinging over to the frontend, create your React app using a handy tool called Create React App. Think of it as pulling out a pre-organized tool kit for your project.
npx create-react-app .
Building React Components
Craft your login and registration forms as React components. You’ll love working with state and event handlers, which keep your forms alive. Here’s an example for a login component:
import React, { useState } from 'react';
import axios from 'axios';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:8000/token', {
grant_type: 'password',
username: email,
password: password,
});
localStorage.setItem('token', response.data.access_token);
window.location.href = '/';
} catch (error) {
setError(error.response.data.detail);
}
};
return (
<div>
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" />
<button type="submit">Login</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
</div>
);
};
export default Login;
Nailing Authentication and Authorization
Authentication is like having a VIP pass. When users log in, generate JWT tokens and keep them safe in local storage. Authenticate subsequent requests using these tokens.
Backend Authentication
Protect routes using OAuth2PasswordBearer
in your FastAPI app:
from fastapi.security import OAuth2PasswordBearer
from fastapi import Depends, HTTPException
from sqlalchemy.orm import Session
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/users/me", response_model=schemas.User)
def read_users_me(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
user = get_user(db, token=token)
if not user:
raise HTTPException(status_code=401, detail="Invalid authentication credentials")
return user
Frontend Authentication
Use the power of Axios interceptors to add the JWT token to each request automatically. No need to repeat yourself every time you send a request.
import axios from 'axios';
axios.interceptors.push({
request: config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
});
Deploying Like a Pro
Let’s get this beast running live. Use Docker to containerize both frontend and backend parts of your project. Think of Docker as packing your entire app into a neat and portable box.
Docker Compose Setup
Create a docker-compose.yml
file to outline your services. This file defines how to piece together your app’s services using Docker:
version: '3'
services:
db:
image: postgres
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=dbname
volumes:
- db-data:/var/lib/postgresql/data
backend:
build: ./fastapi
environment:
- DATABASE_URL=postgresql://user:password@db:5432/dbname
ports:
- "8000:8000"
depends_on:
- db
frontend:
build: ./react
ports:
- "3000:3000"
depends_on:
- backend
volumes:
db-data:
Run everything with a simple command. It’s like hitting the start button on your favorite game console:
docker-compose up
Head over to http://localhost:3000
to see your app in action.
Testing, Testing, 1-2-3
Ensuring your application behaves is a big deal. Use Pytest for backend and Jest for frontend tests to keep everything under control.
Backend
Write your tests to make sure everything is functioning behind the scenes:
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_create_user():
response = client.post("/users/", json={"email": "[email protected]", "password": "password"})
assert response.status_code == 201
assert response.json()["email"] == "[email protected]"
Frontend
For React components, use Jest to ensure interactions happen just the way you intend:
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import axios from 'axios';
import Login from './Login';
jest.mock('axios');
describe('Login', () => {
it('should call the login API', async () => {
axios.post.mockResolvedValue({ data: { access_token: 'token' } });
const { getByPlaceholderText, getByText } = render(<Login />);
const emailInput = getByPlaceholderText('Email');
const passwordInput = getByPlaceholderText('Password');
const submitButton = getByText('Login');
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
fireEvent.click(submitButton);
await waitFor(() => expect(axios.post).toHaveBeenCalledTimes(1));
expect(axios.post).toHaveBeenCalledWith('http://localhost:8000/token', expect.anything());
});
});
Wrapping Things Up
Building a full-stack application with FastAPI, React, and PostgreSQL is like assembling the coolest LEGO set ever. Each step, from setting up project structure to deploying with Docker, adds a piece to your masterpiece. Following this guide, you’ll end up with a robust, scalable, and maintainable web application that’s ready to take on the world.
Just remember, the journey is just as important as the destination. Happy coding!