Skip to content

FULL-STACK / OCT 2026 – CURRENT

Network Growth Assistant

A networking and job-search workspace that only counts what actually happened.

Built on my own. All 256 commits in the repository are mine.

MY ROLE
Solo project, design to deployment
KEY RESULT
Built solo over 256 commits, with automated tests that run on every change
DECISION
Return fewer people rather than lower the evidence bar, and count only accepted invitations.
EVIDENCE
A live app, a public repository with CI, and the safe-fetch source shown below.

Built with

  • Next.js
  • React
  • TypeScript
  • PostgreSQL
  • Supabase
  • Vercel
  • Vitest
  • GitHub Actions
  • Claude Code

What it is

Network Growth Assistant is a website that helps you meet more people in your field and run your job search, all in one place. It remembers who you want to contact, which messages are still waiting to be sent, and which jobs you have applied for.

It is careful on purpose. It never logs into LinkedIn for you, never sends a message for you, and never counts something as done before it really happened. You do the human parts, and the app does the remembering and the research.

What it can do

  • Quick capture

    Copy a LinkedIn search page and paste it into the app. It turns the page into a tidy list of people, with their school and program already filled in. It only reads what you paste, and it never visits LinkedIn.

  • A to-do list for each day

    Every day the app builds a short list of people to contact, based on the goals you set. You open each profile, send the invitation yourself, then tick it off.

  • Honest progress

    Your progress bar only moves when someone actually accepts. Invitations that are still waiting are shown in a separate colour, so the numbers never flatter you.

  • Discover

    Type a request like “Find 10 UBC alumni who work at Amazon”. The app searches public websites such as GitHub and ORCID, checks each fact on the person’s own page, and shows the proof next to every result. If it cannot back something up, it leaves it out.

  • Real jobs only

    Jobs come from companies’ own career pages. Every link is checked before the Apply button turns on, and each job shows how well it fits your resume and what is missing.

  • Follow-ups and applications

    When someone accepts, the app suggests a thank-you and later check-ins, with drafts you can edit. A board follows each job from saved to offer, and there is interview preparation for the ones that get that far.

How a request flows

  1. Next.js 16App Router, server actions
  2. proxy.tsSession refresh, sign-in redirect
  3. SupabasePostgres, row-level security, Auth
  4. pg_cron + API routesCrawler and alerts

How it works

  1. 01

    You add people by pasting a search page or importing a file.

  2. 02

    Each day the app sorts the new people and builds your list for the day.

  3. 03

    You open each profile and send the invitation yourself, then mark it as sent.

  4. 04

    When someone accepts, you record it, and the app creates the follow-up reminders for that person.

  5. 05

    The overview shows how close you are to your goals. Only accepted invitations count.

  6. 06

    You search for jobs, save the ones you like, track your applications, and prepare for interviews.

Screenshots

Overview (example data)
Quick capture (example data)
Discover (example data)
Jobs (example data)

Why

Networking tools tend to flatter you with vanity numbers or automate the part that should stay personal. I wanted one that is honest about progress and leaves every invitation and message to me.

What I built

  1. 01

    Built the Next.js 16 App Router front end with React 19, Tailwind CSS 4, and shadcn/ui, including light and dark themes and lists that turn into cards on a phone.

  2. 02

    Designed the Supabase Postgres schema with row-level security on every table, scoped to the owner, plus Auth (Google, password, emailed link), Storage, and pg_cron jobs.

  3. 03

    Wrote safe-fetch, the only path for fetching outside URLs: it refuses private and reserved addresses, pins the connection to the addresses it just checked, and follows redirects by hand.

  4. 04

    Built Discover, a research assistant over public sources that verifies each fact against the source page and drops any quote it cannot find, with scores computed by visible rules rather than a model.

  5. 05

    Built the Jobs search over employers’ own boards, where a posting gets an Apply button only after its link is verified.

  6. 06

    Wrote 1,523 unit tests and 305 pgTAP tests (checks that run inside the database itself), and ran axe-core accessibility scans in the Playwright end-to-end suite, with CI on GitHub Actions.

The code

src/lib/follow-ups/templates.ts
/**
 * The follow-up touches after someone accepts an invitation, and a draft message for each. Drafts are built
 * from a template using only what the record holds (the person's name and, if known, their school and field),
 * never claim a conversation that did not happen, and are copied by the user, who sends them. Nothing here
 * calls an AI service, so a draft cannot contain an invented fact.
 */

export const FOLLOW_UP_KINDS = ["thank_you", "coffee_chat", "check_in_30", "check_in_90"] as const;
export type FollowUpKind = (typeof FOLLOW_UP_KINDS)[number];

export const FOLLOW_UP_LABELS: Record<FollowUpKind, { title: string; when: string }> = {
  thank_you: { title: "Thank them for connecting", when: "the day they accept" },
  coffee_chat: { title: "Ask for a short chat", when: "a week after" },
  check_in_30: { title: "Check in", when: "30 days after" },
  check_in_90: { title: "Catch up", when: "90 days after" },
};
The four follow-up messages the app suggests after someone accepts an invitation, and when each is due. The messages come from fixed templates, not from an AI, so they can never include a made-up fact.

Outcome

A deployed app on Vercel and Supabase that never scrapes LinkedIn, never sends anything for you, and shows progress that cannot be inflated by pending invitations.

The trade-off

Discover returns fewer people rather than weaker matches, and an AI model only writes text; scores, rankings, and fit are rules. That makes results explainable and checkable, but a short list can feel less impressive than a long, loosely matched one.

What I learned

Security and honesty rules are easiest to keep when they live in one place: one function for outside fetches, one place for counting rules, and row-level security in the database instead of checks scattered through the app.

What I'd fix next

Run the screen-reader checklist (not done yet, and the README says so) and re-measure Lighthouse on production, which has not been repeated since the newest features landed.

Working on something like Network Growth Assistant?

Tell me what you're building and I'll reply within two business days.

Open to co-op, internship and software roles in Vancouver, on-site or hybrid. Graduating May 2027.