< Back to projects

Web app · case study

Notecom

A social study-notes platform: share folders, follow classmates, and search lectures in a code-editor reader.

Notecom web preview
My role

Sole developer

In use

Used almost every day by me and four classmates

Type

Web application

Stack

Next.js, TypeScript, PostgreSQL, Tailwind CSS

Runs on

Vercel

Edge

Vercel edge

Why I built it

I built Notecom for myself and my classmates.Like most students, we pasted lecture slides into ChatGPT, Kimi or DeepSeek for explanations, and the notes came back uneven, unformatted, and lost in chat history.Notecom writes notes in one consistent study format, files them into folders, and lets classmates share them.I'm the sole developer, and five of us use it almost every day.

The problem

  • General-purpose chatbots gave explanation notes of uneven quality and no consistent format.

  • A good answer was hard to find again once it scrolled away in a chat.

  • Nothing organized notes by course, or let classmates share them.

Features

  • Follow classmates to get invited into their folders
  • Public folders are discoverable, but joining needs approval
  • New accounts start in featured course folders; topics help find the rest
  • Role-based access per folder: viewer, editor, owner
  • Full-text search across notes, plus starred favourites

Architecture

Architecture diagramRequests hit Vercel's edge middleware first. It sets a per-request CSP nonce, rate-limits, and verifies the Supabase session. The Next.js app then queries Postgres with the anon key and the caller's JWT. Row Level Security decides every read and write. Avatars live in a private Storage bucket.VERCELSUPABASEBrowserReact UIcode-editor readerEdge middlewareCSP nonce per requestsession gate, rate limitsNext.js 15RSC + route handlersstore.ts, collab/*Supabase Authpassword + emailed codePostgres + RLS31 SQL migrationsfull-text search, GINStorageprivate avatar bucketHTTPSgetUser()verified usersign-in, codeanon key + JWTsigned URLs
Dev · applicationSec · security controlExternal · third partyScroll sideways to see all of it.
  • Browser to Edge middleware: HTTPS
  • Edge middleware to Supabase Auth: getUser()
  • Edge middleware to Next.js 15: verified user
  • Next.js 15 to Supabase Auth: sign-in, code
  • Next.js 15 to Postgres + RLS: anon key + JWT
  • Next.js 15 to Storage: signed URLs

In words

Requests hit Vercel's edge middleware first.It sets a per-request CSP nonce, rate-limits, and verifies the Supabase session.The Next.js app then queries Postgres with the anon key and the caller's JWT.Row Level Security decides every read and write.Avatars live in a private Storage bucket.

Pipeline

push → main

  1. GitHub Actions

    npm ci, tsc --noEmit

  2. Vercel build

    next build

  3. Atomic deploy

    instant rollback

schema change

  1. SQL migration

    versioned in repo

  2. Applied to Supabase

    tables, functions, RLS

Decisions

Authorization lives only in Postgres Row Level Security, with no service-role key.

Any client can call any endpoint, so the database must enforce access.A second copy of the rules in the app would be weaker.

Supabase became the only data store.

It replaced three Go services syncing a local SQLite copy.Devices could fall behind, and every deploy ran all three.

Tags stopped granting access.

Tag access was precise but hard to explain.A new account could read nothing until two people acted.Invitations, requests and featured folders replaced it; tags became topics.

Joining never grants writing.

Requests and onboarding give viewer access.Editing needs the owner's invitation.

Search splits notes at headings.

Fixed-size chunks cut explanations mid-thought.Headings mark real topic changes.

Challenges

A policy on the members table that queries it recurses.

SECURITY DEFINER helper functions break the cycle.Each checks the caller first, since RLS is off inside them.

An invitation inbox leaked to the sender.

Fixed in the folder data layer in August 2026.

Changing the access model on a live app.

Tag access was retired only once no one read through a tag.The old functions were revoked so it can't return.

Testing

  • CI type-checks every push and pull request.

  • RLS changes are verified in SQL as each role, from a checklist in the repo.

  • A script validates generated notes against the HTML format contract.

  • Not yet: automated tests.Policy tests are next.

Outcome

  • Live, and used almost every day by me and four classmates.

  • Desktop build released as v0.1.0 for Windows and Linux.

Next

  • Automated tests for the access policies

  • A global rate limit if traffic grows

  • A working macOS desktop build

Stack

Next.js

Renders the sidebar, folder browser, and lesson reader.

TypeScript

Types the shared folder and permission models.

PostgreSQL

Stores notes, folders and per-folder roles, on Supabase.

Tailwind CSS

Styles the dark, code-editor reading view.

Security

Authentication

Supabase Auth: email and password, plus an emailed verification code.

Input validation

RLS policies and Postgres constraints on every notes_* table.

Protections

No service-role key anywhere.Every query carries the caller's JWT and is scoped by RLS.

Operations

Runs on

Vercel

Edge

Vercel edge

Deploy

Push to main deploys on Vercel.Schema and policies ship as versioned SQL migrations.

Health check

A 307 to the sign-in page is the healthy answer here.