Writing

7 min read

Building HookBridge.app

How I shipped a LinkedIn comment generator SaaS in a weekend — Next.js App Router, Supabase magic-link auth, OpenAI, and a credit system from scratch.

  • Next.js
  • Supabase
  • OpenAI
  • SaaS

I wanted to stay active on LinkedIn without spending an hour crafting comments every day. The problem is specific: most AI-generated comments feel generic and detached from the author's real voice. So I built HookBridge.app — a micro-SaaS that takes any LinkedIn post, analyzes its intent, and returns three high-authority comment drafts, each with a distinct tone. The system detects the language of the post and responds in kind — Spanish, English, Portuguese, or whatever the conversation calls for.

This is a writeup of the decisions that shaped the product, not just the stack choices.

HookBridge dashboard
HookBridge dashboard
The main dashboard — paste a post, pick your tone, generate.

The stack

The core stack is Next.js App Router, Supabase, and OpenAI gpt-4o-mini. No complex infrastructure — a single Vercel deployment, one Supabase project, one environment file.

Next.js 14 · TypeScript · Tailwind CSS · Supabase Auth · Supabase Postgres · OpenAI gpt-4o-mini · Vercel

Auth: magic link OTP

I chose Supabase magic-link OTP over OAuth or password auth for a deliberate reason: zero friction. Users enter their email, receive a 6-digit code, and land on the dashboard. There is no password to forget, no OAuth consent screen, no session token to manage manually. Supabase handles the OTP lifecycle and injects the session into the server-side cookie.

The middleware checks for a valid session on every request to /dashboard and redirects to /login if none exists. The auth flow is two files: middleware.ts and app/auth/callback/route.ts.

HookBridge login
HookBridge login
Magic link login — email + 6-digit code, no passwords.

The credit system

Each user gets a credits balance stored on their profile row in Postgres. Every generation deducts one credit. The deduction happens inside the API route, after auth is confirmed and before the OpenAI response is returned to the client — so a failed generation does not burn a credit.

A Postgres trigger auto-creates the profile row on new user sign-up, seeded with a free credit allowance. Row-level security policies ensure each user can only read and update their own profile. This is one of those cases where pushing logic to the database layer — rather than the application layer — genuinely pays off.

The generation API

The POST /api/generate route does four things in sequence: verify the session, check the credit balance, call OpenAI with a structured prompt, and decrement the credit. The response uses response_format: { type: 'json_object' } to guarantee a parseable JSON structure — three comment objects each with a tone label and body text.

The prompt encodes three distinct personas: a thought leader who builds on the idea, a peer who challenges one assumption, and a practitioner who adds a concrete example. This gives users three tonally different comments to choose from rather than three rewrites of the same thing.

Generated comment cards
Generated comment cards
Three comment cards — each with a distinct voice and a one-click copy.

What I would change

The biggest gap right now is no streaming. The API route waits for the full OpenAI response before returning anything to the client. For a weekend build this is acceptable, but the perceived latency is noticeable. Switching to streaming with the Vercel AI SDK would cut the time-to-first-token from ~3 seconds to under 500ms.

The second thing I would revisit is the credit model. A flat credit count is easy to reason about, but it encourages hoarding rather than consistent use. A rolling weekly allowance or a subscription tier would better reflect the real usage pattern.

Lessons

Supabase Row Level Security eliminates a whole class of authorization bugs that normally live in the application layer. Trusting the database's policy engine — rather than writing guards in every route handler — made the codebase noticeably smaller.

gpt-4o-mini is fast and cheap enough for synchronous comment generation. Using json_object mode removes the need for prompt-level parsing instructions and virtually eliminates malformed output. Native multilingual support came for free — the model handles language detection and response coherence without any extra prompt engineering.

The App Router's server/client boundary is genuinely useful once you stop fighting it. Auth state on the server, interactivity on the client — keeping the split clean makes both sides simpler.