USD ($)
$
United States Dollar
Euro Member Countries
India Rupee

Building a Full-Stack CRUD App with User Auth and File Handling

Lesson 28/30 | Study Time: 28 Min

Building a full-stack CRUD application with user authentication and file handling involves developing both frontend and backend components that work together to manage data securely and efficiently.

The backend provides APIs for creating, reading, updating, and deleting records, handles user authentication and authorization, and manages file uploads and storage. The frontend interacts with these APIs to present data, manage user sessions, and allow users to upload and access files through an intuitive interface.

Project Overview and Tech Stack

This section outlines the app we'll build: a file-sharing dashboard where authenticated users can create, view, edit, and delete notes, each attached to an uploaded image file. It ties together API design, security, and frontend integration, reflecting industry standards like those from OWASP for auth and REST API guidelines.

We'll use a modern, efficient stack that's beginner-friendly yet scalable.


1. Backend: FastAPI (Python) for APIs, SQLAlchemy for ORM, JWT for auth, and Alembic for migrations.

2. Database: PostgreSQL for relational data with file metadata.

3. Frontend: React with Axios for API calls, React Router for navigation, and Tailwind CSS for styling.

4. File Handling: Secure uploads to a local directory (extendable to AWS S3).

5. Other Tools: Docker for containerization, environment variables for secrets.

This stack ensures your app handles 100+ concurrent users efficiently, per benchmarks from FastAPI docs.

Setting Up the Backend with FastAPI

Start by creating a robust backend that exposes CRUD endpoints secured by authentication. FastAPI's dependency injection makes auth seamless, while Pydantic models enforce data validation.

First, install dependencies: pip install fastapi uvicorn sqlalchemy alembic python-jose[cryptography] python-multipart passlib[bcrypt] psycopg2-binary.


Database Models and Migrations

Define your models for users, notes, and files. Use SQLAlchemy for ORM to map Python classes to tables.

python
# models.py
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_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)

class Note(Base):
__tablename__ = "notes"
id = Column(Integer, primary_key=True, index=True)
title = Column(String)
content = Column(String)
file_path = Column(String) # Stores relative path to uploaded file
owner_id = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime, default=datetime.utcnow)

Run Alembic migrations: alembic init migrations, configure alembic.ini, then alembic revision --autogenerate -m "initial" and alembic upgrade head.


Implementing User Authentication

Secure your API with JWT tokens. FastAPI's OAuth2PasswordBearer handles token extraction effortlessly.


1. Create a password hasher utility using passlib.

2. Define auth schemas in Pydantic.

3. Implement /auth/register and /auth/login endpoints.


Example login endpoint

python
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import timedelta

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")
SECRET_KEY = "your-secret-key" # Use env vars in prod
ALGORITHM = "HS256"

@app.post("/auth/login")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = get_user_by_email(form_data.username) # Implement this
if not verify_password(form_data.password, user.hashed_password):
raise HTTPException(status_code=400, detail="Incorrect credentials")
access_token = jwt.encode({"sub": user.email}, SECRET_KEY, algorithm=ALGORITHM)
return {"access_token": access_token, "token_type": "bearer"}

Protect routes with a dependency: @app.get("/notes/", dependencies=[Depends(get_current_user)]).

CRUD Operations for Notes

With auth in place, build CRUD endpoints for notes. Each note links to a user and stores file metadata—ensuring atomic operations via database transactions.


Create and Read Endpoints


POST /notes/: Create a note (requires auth).


1. Validate input with Pydantic.

2. Associate with current_user.id.


Example

python
@app.post("/notes/")
async def create_note(note: NoteCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
db_note = Note(**note.dict(), owner_id=current_user.id)
db.add(db_note)
db.commit()
db.refresh(db_note)
return db_note


GET /notes/: List user's notes (paginated for scalability).


1. Filter by owner_id.

2. Use limit and offset query params.

Bullet key features: Pagination prevents overload; SQLAlchemy's order_by ensures latest-first sorting.


Update and Delete Endpoints

PUT /notes/{note_id} and DELETE /notes/{note_id} follow similar patterns, with ownership checks.

python
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
note = db.query(Note).filter(Note.id == note_id, Note.owner_id == current_user.id).first()
if not note:
raise HTTPException(status_code=404, detail="Note not found")
db.delete(note)
db.commit()
return {"msg": "Note deleted"}


These align with REST conventions (RFC 7231), ensuring intuitive API consumption.

Secure File Upload and Handling

File handling introduces security risks like arbitrary code execution—mitigate with validation and isolation. FastAPI's UploadFile makes this straightforward.


Upload Endpoint Integration

Extend the create/update flow:


1. In POST /notes/, accept file: UploadFile.

2. Validate: Check MIME type (e.g., images only), size (<5MB).

3. Save to ./uploads/{user_id}/{filename} with unique names (UUID).

python
import shutil
from fastapi import File, UploadFile
import os
from uuid import uuid4

UPLOAD_DIR = "uploads"

@app.post("/notes/")
async def create_note_with_file(
title: str, content: str, file: UploadFile = File(...),
current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
if not file.content_type.startswith("image/"):
raise HTTPException(400, "Only images allowed")
filename = f"{uuid4()}.{file.filename.split('.')[-1]}"
os.makedirs(f"{UPLOAD_DIR}/{current_user.id}", exist_ok=True)
path = f"{UPLOAD_DIR}/{current_user.id}/{filename}"
with open(path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)

note = Note(title=title, content=content, file_path=path, owner_id=current_user.id)
db.add(note)
db.commit()
return note


1. Serve files statically: Use FastAPI's StaticFiles mount at /uploads.

2. Best practices: Scan with ClamAV in prod; delete files on note deletion to free storage.

For downloads: GET /notes/{id}/file streams the file securely.

Building the React Frontend

Shift to the frontend: A responsive dashboard with login, note list, forms, and file previews. Use React Query (TanStack Query) for optimistic updates and caching.


Setup and Key Components

npx create-react-app frontend --template typescript, then npm i axios react-router-dom @tanstack/react-query tailwindcss.


Core components


1. AuthContext: Manages JWT token in localStorage.

2. NoteList: Fetches and displays notes with image previews.

3. NoteForm: Handles create/edit with file input.


Example API hook

jsx
import { useMutation, useQuery } from '@tanstack/react-query';
import axios from 'axios';

const api = axios.create({ baseURL: 'http://localhost:8000' });

export const useNotes = () => {
return useQuery(['notes'], () => api.get('/notes/', {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
}).then(res => res.data));
};


UI Flows: Login to CRUD


1. Login Page: Form submits to /auth/login, stores token.

2. Dashboard: Protected route shows note cards with file preview (<img src={/uploads/${note.file_path}} />).

3. Create/Edit Modal: Drag-and-drop file upload, form validation with React Hook Form.

4. Delete Confirmation: Optimistic UI update via React Query mutations.

Responsive design: Tailwind classes ensure mobile-first.

Error handling: Toast notifications for 401/404.

Deployment and Best Practices

Package for production: Dockerize backend (Dockerfile with multi-stage build), deploy frontend to Vercel/Netlify, backend to Render/Heroku. Use Cloudinary or S3 for files.


Key practices

This app scales to enterprise levels, as seen in FastAPI's production case studies (e.g., Uber).

himanshu singh

himanshu singh

Product Designer
Profile

Class Sessions

1- HTTP Methods and REST Principles 2- Status Codes, Headers, and Request/Response cycles 3- JSON and XML Data Formats for API Payloads 4- Resource Naming Conventions and URI Design Best Practices 5- Statelessness, HATEOAS, and API Versioning Strategies 6- Rate Limiting, Caching, and Idempotency for Scalability 7- FastAPI Setup, Pydantic Models, and Async Endpoint Creation 8- Path/Query Parameters, Request/Response Validation 9- Dependency Injection and Middleware for Authentication/Authorization 10- SQLAlchemy ORM with Async Support for PostgreSQL/MySQL 11- CRUD Operations via API Endpoints with Relationships 12- Database Migrations Using Alembic and Connection Pooling 13- JWT/OAuth2 Implementation with FastAPI Security Utilities 14- File Uploads, Pagination, and Real-Time WebSockets 15- Input Sanitization, CORS, and OWASP Top 10 Defenses 16- Unit/integration testing with Pytest and FastAPI TestClient 17- API Documentation Generation with OpenAPI/Swagger 18- Mocking External Services and Load Testing with Locust 19- Containerization with Docker and Orchestration via Docker Compose 20- Deployment to Cloud Platforms 21- CI/CD Pipelines Using GitHub Actions and Monitoring with Prometheus 22- Consuming APIs in React/Vue.js with Axios/Fetch 23- State Management (Redux/Zustand) for API Data Flows 24- Error Handling, Optimistic Updates, and Frontend Caching Strategies 25- Async Processing with Celery/Redis for Background Tasks 26- Caching Layers (Redis) and Database Query Optimization 27- Microservices Patterns and API Gateways 28- Building a Full-Stack CRUD App with User Auth and File Handling 29- API Analytics, Logging (Structlog), and Error Tracking 30- Code Reviews, Maintainability, and Evolving APIs in Production