About Skills Work Journey Blog Contact
Frontend · 8 min read · January 2026

The Full Stack Behind Mapleins — Canada's AI Job Platform

The Canadian job market has a specific problem. Most job boards are designed for the US market and surface irrelevant roles, or they're government portals that haven't been updated since 2011. Job seekers are left uploading their resume to five different sites and hearing nothing back.

Mapleins is my attempt to fix that — a platform that combines ATS resume scoring, AI-powered job matching, and interview prep, built specifically for the Canadian market. Here's how it's built.

The Stack

The core technologies:

  • Next.js 14 (App Router) — for the frontend and API routes
  • Supabase — PostgreSQL database, auth, realtime, and storage
  • OpenAI API — for resume analysis and job match scoring
  • Vercel — deployment and edge functions
  • Tailwind CSS — styling

I chose Next.js App Router because the platform needed both server-side rendering for SEO and client-side interactivity for the dashboard. Supabase was the obvious choice for the database layer — the Postgres extension ecosystem and built-in auth saved weeks of work.

ATS Resume Scoring

The ATS (Applicant Tracking System) scorer is the core feature. Most large companies don't read your resume — a system does first. If your resume doesn't match the job description keywords, you're filtered out before a human sees it.

The Mapleins scorer works like this:

  1. User uploads their resume (PDF or DOCX)
  2. We extract the text using a serverless function
  3. We send the resume text + job description to GPT-4 with a structured prompt
  4. GPT returns a JSON score with category breakdowns and specific improvement suggestions
  5. We store the score in Supabase and display it in the dashboard

The prompt engineering here matters a lot. Generic "rate this resume" prompts return garbage. I spent time crafting a prompt that evaluates: keyword match percentage, skills gap analysis, formatting quality, and impact language (did they use numbers and outcomes?). The response schema is typed and validated before it hits the database.

AI Job Matching

The matching system generates 15+ curated job recommendations per user based on their resume profile. This isn't just keyword matching — the AI reads the resume holistically and matches it against job descriptions we've indexed from Canadian job boards.

The matching pipeline:

// Simplified matching logic
const userProfile = await extractProfile(resumeText)
const jobs = await supabase
  .from('jobs')
  .select('*')
  .eq('country', 'CA')
  .limit(200)

const scored = await Promise.all(
  jobs.map(job => scoreMatch(userProfile, job))
)

return scored
  .sort((a, b) => b.score - a.score)
  .slice(0, 15)

We run the matching in parallel using Promise.all, which keeps latency manageable even with 200 candidate jobs. The match score factors in role fit, location preference, experience level, and salary range when available.

Auth & Data Architecture

Supabase Auth handles the entire authentication flow. I'm using Row Level Security (RLS) policies so users can only ever read and write their own data — no custom auth middleware needed.

The database schema is straightforward:

  • users — extended profile from Supabase auth
  • resumes — stored file refs + extracted text
  • ats_scores — score history per resume
  • job_matches — cached match results
  • jobs — indexed job listings

I cache match results rather than rerunning the AI on every page load. The cache invalidates when the user uploads a new resume.

Performance Considerations

The biggest performance challenge was the AI calls — GPT-4 can take 5–10 seconds for complex analysis. I solved this with a few patterns:

  • Optimistic UI — show loading states immediately, update when the result arrives
  • Background processing — trigger the ATS score on upload, not on view. By the time the user navigates to the results page, it's usually ready.
  • Edge caching — job listings are static enough to cache at the edge for 1 hour

What I Learned

A few things that surprised me building this:

  • Prompt engineering is real engineering. The difference between a useful AI feature and a party trick is in the prompt. I went through 20+ iterations before the ATS scorer was consistently useful.
  • Supabase RLS is worth the learning curve. It felt complex at first but once it clicks, you get security guarantees that would take weeks to implement manually.
  • Users don't read — they scan. I rewrote the dashboard UI three times before I accepted this and switched to a card-based layout with big numbers and clear CTAs.
Mapleins is live at mapleins.com. It's free to try — upload your resume and see your ATS score in under a minute.
Share

More Writing