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

- 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
- 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
GitHub Actions
npm ci, tsc --noEmit
Vercel build
next build
Atomic deploy
instant rollback
schema change
SQL migration
versioned in repo
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.