Skip to content

About

Fullstack social media app (Bun · Fastify · TypeScript · PostgreSQL) — Chas Academy QA course project. I added likes, comments, search, pagination, rate limiting, a custom frontend & full Docker setup on top of the instructor's base.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

SocialApp

A course-base fork, not a from-scratch build — the instructor's Bun/Fastify/Postgres scaffold got auth and a bare post model; I added likes, comments, search, pagination, rate limiting, a full custom frontend, and the Docker setup.

Bun Fastify TypeScript PostgreSQL React Docker


Note

Course project, clearly attributed. This started as ahmadardal/socialapp — the scaffold for a System-Level Quality Assurance course at Chas Academy. Auth, follow/unfollow, and the base post model are the instructor's. Everything in Status marked "added" is mine.

What it is

A photo-feed social app: register, follow people, post an image with a caption, and see a feed of posts from yourself and who you follow. On top of that base I built the interaction layer that makes it feel like a real app — likes, threaded comments, user search, paginated feeds, API-wide rate limiting — plus a custom React frontend and a Docker Compose setup so the whole thing (API, Postgres, frontend) runs with one command.

How it works

The sequence below is a real request path — register/login, post an image (uploaded to S3), like it, comment on it, then read it back through the paginated feed. Everything left of "Rate limiter" is base scaffold; the like, comment, and pagination steps are the features I added.

sequenceDiagram
    actor User
    participant API as Fastify API
    participant RL as Rate limiter
    participant S3 as AWS S3
    participant DB as PostgreSQL

    User->>API: POST /login
    API->>DB: verify password (Bun.password.verify)
    API-->>User: JWT (7d)

    User->>API: POST /create (multipart: image + caption)
    API->>RL: global check — 100 req/min
    API->>S3: upload image
    S3-->>API: image URL
    API->>DB: INSERT post (status='active')
    API-->>User: 201 created post

    User->>API: POST /posts/:id/like
    API->>DB: toggle row in reactions (insert or delete)
    API-->>User: { liked: true|false }

    User->>API: POST /posts/:id/comments
    API->>DB: INSERT comment (parent_comment_id optional → threaded replies)
    API-->>User: 201 created comment

    User->>API: GET /feed?page=2
    API->>DB: posts by self + followed users, ORDER BY created_at LIMIT 25 OFFSET
    API-->>User: paginated feed
Loading

📊 Status & what's mine

Feature Built by State
Register / login (JWT, Bun.password hashing) base scaffold ✅
Follow / unfollow, feed of followed users base scaffold ✅
Image upload to S3, create post base scaffold + hardening ✅
Likes — toggle endpoint, reactions table, like counts me ✅
Comments — threaded via parent_comment_id me ✅
Delete post, edit profile (incl. avatar upload) me ✅
User search (GET /users?search=) me ✅
Feed pagination (?page=, LIMIT/OFFSET) me ✅
Rate limiting — global 100/min + tighter limits on /register, /login me ✅
Custom React frontend — auth + feed pages, own components me ✅
Docker setup — API, frontend, and Postgres containers via Compose me ✅
Security/bug fixes from a code review pass me ✅

Quick start

Requires Bun and Docker.

git clone https://github.com/devinder-dev/socialapp.git
cd socialapp

# create a .env with JWT_SECRET_KEY and S3_REGION / S3_BUCKET / S3_ACCESS_KEY / S3_SECRET_KEY

docker compose up --build
# api        → http://localhost:3000
# frontend   → http://localhost:3002
# postgres   → localhost:5433 (seeded from init.sql)

Or run the API without Docker:

bun install
bun run dev          # bun --hot --sql-preconnect ./src/index.ts
Full feature list (click)

Backend (Fastify + PostgreSQL)

  • POST /register, POST /login — JWT auth, rate-limited to 10 requests / 10 minutes
  • POST /toggle-follow/:username, GET /users?search= — follow/unfollow and searchable user list
  • PATCH /profile — edit display name, bio, avatar (multipart upload to S3)
  • POST /create, GET /feed?page=, DELETE /posts/:id — create, paginated read, soft-delete (status flips to deleted, never hard-deleted)
  • POST /posts/:id/like, GET /posts/:id/likes — toggle stored as an insert/delete on a (user_id, post_id) primary key, so a like can never double-count
  • POST /posts/:id/comments, GET /posts/:id/comments — comments support an optional parent_comment_id, so a comment can reply to another comment
  • Global rate limiting (@fastify/rate-limit, 100 req/min) plus per-route limits on auth endpoints
  • Media uploads go through an S3 adapter (src/adapters/s3.ts) shared by post creation and profile avatars

Frontend

  • Custom React 19 SPA (frontend/src/App.tsx) — no leftover scaffold UI, split into AuthPage and FeedPage
  • Token stored client-side, feed fetched and re-fetched against the real API, create-post form posts multipart/form-data directly

DevOps

  • Dockerfile (API) and Dockerfile.frontend, both oven/bun:1-alpine, frozen-lockfile installs
  • docker-compose.yml — Postgres 16 (health-checked), API, and frontend as three services, init.sql seeding the schema on first boot
  • Required secrets (JWT_SECRET_KEY, S3 credentials) are pulled from the environment, not hardcoded
Architecture & repository layout (click)
flowchart TD
    FE["React SPA<br/>(AuthPage · FeedPage)"] -->|fetch, Bearer JWT| API

    subgraph API["Fastify API"]
        direction TB
        RL["Rate limiter<br/><i>global + per-route</i>"] --> R["Routes"]
        R --> MW["authenticate (JWT)"]
        MW --> C["Controllers<br/><i>parse request, shape response</i>"]
        C --> Repo["Repositories<br/><i>raw SQL via bun:sql</i>"]
    end

    Repo --> PG[("PostgreSQL<br/>users · posts · reactions · comments")]
    C --> S3[("AWS S3<br/>post images · avatars")]
Loading
src/
├── auth/            JWT plugin + authenticate preHandler
├── controllers/      HTTP-facing: parse request, call repository, shape response
├── repository/        raw SQL queries (bun:sql), one module per entity
├── routes/           route → controller wiring
├── schemas/          request validation (register, login)
├── adapters/          S3 upload adapter
├── db/               db client + schema
└── types/            shared DB / HTTP / auth types
frontend/
└── src/
    ├── App.tsx        auth-state switch → AuthPage or FeedPage
    └── pages/          AuthPage.tsx, FeedPage.tsx

Controllers stay thin — they pull data off the request, call a repository function, and return a status code. There's no separate service layer here (unlike a larger app); at this scale the controller/repository split was enough to keep SQL out of the HTTP layer.

Tech stack (click)
Layer Choice
Runtime Bun 1.3
API Fastify 5, @fastify/jwt, @fastify/rate-limit, @fastify/multipart, @fastify/cors
Language TypeScript
Database PostgreSQL 16, queried directly via Bun's built-in sql client (no ORM)
Media storage AWS S3 (@aws-sdk/client-s3)
Frontend React 19, Bun's own dev server + bundler
DevOps Docker, Docker Compose

Devinder Singh · Fullstack Developer (YH) student at Chas Academy, Stockholm

Base scaffold by Ahmad Ardal. Everything under Status & what's mine marked "me" was built in this fork.

About

Fullstack social media app (Bun · Fastify · TypeScript · PostgreSQL) — Chas Academy QA course project. I added likes, comments, search, pagination, rate limiting, a custom frontend & full Docker setup on top of the instructor's base.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages