python

What Rollercoaster of Code Awaits You in Building a Full-Stack Web App from Scratch?

A Journey Through FastAPI, React, and PostgreSQL: Building Your Dream Web Application

What Rollercoaster of Code Awaits You in Building a Full-Stack Web App from Scratch?

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!

Keywords: Full-stack development, FastAPI, React frontend, PostgreSQL database, building web applications, API Endpoints, Docker containerization, authentication and authorization, testing with Pytest and Jest, deploying full-stack apps



Similar Posts
Blog Image
What Magical Trick Makes FastAPI Lightning-Fast?

Turbo-Charge Your FastAPI with Asynchronous Routes for Blazing Performance

Blog Image
Breaking Down Marshmallow’s Field Metadata for Better API Documentation

Marshmallow's field metadata enhances API documentation, providing rich context for developers. It allows for detailed field descriptions, example values, and nested schemas, making APIs more user-friendly and easier to integrate.

Blog Image
Is Your FastAPI Ready to Zoom with Asynchronous Database Drivers?

FastAPI and `asyncpg`: Turbocharging Your App with Asynchronous Database Drivers

Blog Image
How Can You Build an Eye-Catching Portfolio Website with Flask in No Time?

Creatively Showcase Your Talents with This Beginner-Friendly Flask Portfolio Guide

Blog Image
What Makes FastAPI and WebSockets a Real-Time Powerhouse?

Instant Feedback Marvels: Uniting FastAPI and WebSockets for Live Data Wonderment

Blog Image
What’s the Secret to Building a Slick CRUD App with FastAPI, SQLAlchemy, and Pydantic?

Mastering the Art of CRUD with FastAPI, SQLAlchemy, and Pydantic