Skip to content
All projects

Personal career operations system

CareerTrack

Deployed Release Candidate2026

I built CareerTrack to keep opportunities, hiring-stage history, interviews, contacts, offers, private documents, and employment records in one personal career workspace.

My contribution
Solo Developer / Software Engineer
Primary users
People organizing their job search and career records.
Project evidence
  • Recorded hiring history
  • Server-side sessions
  • Private document storage

Technology stack

  • Next.js
  • React
  • TypeScript
  • Tailwind CSS
  • Node.js
  • Express
  • PostgreSQL
  • Prisma
  • Zod

Problem & Solution

Scattered career information

I kept finding opportunities, recruiter conversations, interview notes, and documents across job boards, emails, spreadsheets, and bookmarks. An application status by itself did not keep that context together.

One structured workspace

I built the frontend, REST API, relational model, Authentication, and private document handling. Each application dossier connects hiring history, people, interviews, offers, notes, and documents. A career brief carries that journey into employment history.

Application Screens

Use the previous and next buttons, left and right arrow keys, or swipe or drag horizontally to browse. Home and End jump to the first and last screen. Select the image to enlarge it.

0108
Screen 1 of 8.

Opportunity Desk

Career direction, active opportunities, and follow-ups in one daily view.

The Career Workflow

  1. 01Opportunity
  2. 02Application
  3. 03Hiring journey
  4. 04Offer
  5. 05Employment
  6. 06Career history

This is the lifecycle the workspace supports. Users record actual stage changes, offers, and employment. Applications can skip stages, be rejected, or be withdrawn.

Production Architecture

  1. User's browser

  2. Vercel

    Next.js frontend

    Same-origin /api/* rewrite
  3. Railway

    Express API / Node.js

  • Neon PostgreSQL

    Relational data / Prisma ORM

  • Cloudflare R2

    Private document storage

Google OAuth / OIDCIdentity verification by the Express API
The browser calls CareerTrack's own origin. The API checks the session and resource ownership before accessing PostgreSQL or private files. It also verifies Google identity during sign-in and account linking.
Frontend
Next.js App Router, React, TypeScript, Tailwind CSS
Backend & data
Node.js, Express, TypeScript, Zod, PostgreSQL, Prisma

Engineering Decisions

One browser origin across separate hostsVercel forwards /api requests to the Railway API.

I kept the frontend and API as separate applications in a pnpm monorepo. A same-origin rewrite handles the generated hosting domains while preserving host-only, Secure, HttpOnly, SameSite=Lax session cookies and exact-origin CSRF checks. Browser traffic stays on the CareerTrack domain.

Revocable sessions and explicit identity linkingThe server controls sessions; users choose when to connect Google.

I used an opaque session token in an HttpOnly cookie, while PostgreSQL stores its SHA-256 hash. Logout revokes the session. Google sign-in uses OIDC with state, nonce, and PKCE. A matching email does not silently merge accounts; an authenticated user must explicitly connect Google. The initial API deployment uses one replica because OAuth replay state and some rate limits are process-local.

Private files with an API authorization boundaryDocument metadata lives in PostgreSQL; file bytes stay in private R2 storage.

I made the API verify authentication and ownership before returning a document. A storage interface supports local development and S3-compatible production storage. Users can upload, replace, and link documents; PDFs support preview, while DOC/DOCX use authorized download. Database updates and object cleanup are separate operations, so orphan-file reconciliation remains an operational follow-up.

Recorded workflow history and honest insightsStage changes preserve history, and insights use events that actually exist.

I used a transaction to update the current application status and add a history entry, with conflict checks for stale changes. Career Intelligence counts explicitly recorded milestones without inferring skipped stages. Response timing describes recorded status movement, and small source groups show limited-data labels. Offers and accepted applications can be managed alongside linked employment records without inventing future career events.

Testing & Release Readiness

My release-candidate verification passed 60 focused API tests and 26 frontend tests. I also ran type checking, linting, production builds, and verification of six Prisma migrations. Production acceptance covered sessions, Google sign-in and linking, persistent records, and private document handling.

Deployed Release Candidate / beta

v1.0 remains pending. Before a broader release, I still need to add email ownership verification and explicit career defaults for new accounts. New accounts currently default to “Actively Job Hunting”; the planned correction will let users choose their own career direction.

What I Learned

Building CareerTrack showed me that interface design is only one part of a full application. I also had to think about Authentication boundaries, record ownership, workflow integrity, migrations, private storage, and deployment across providers. Testing production behavior and describing the release honestly taught me as much as building the screens.