DEV Community

Adhi Nandan J
Adhi Nandan J

Posted on

How I Built a Job Search OS with Aurora PostgreSQL, AWS Bedrock and Vercel

I created this content for the purposes of entering the Vercel x AWS Hackathon. #H0Hackathon

The Problem

Job seekers make some of the most important financial decisions of their lives with nothing but a spreadsheet and gut feel. They leave money on the table by not negotiating. They sign non-competes they don't understand. They fail the same interview type four times in a row and never notice because their notes are scattered across Notion, WhatsApp and memory.

No tool solves this today. Spreadsheets have no intelligence. ChatGPT has no memory of your history. Glassdoor gives you salary data that is not connected to anything else in your workflow.

The core insight I kept coming back to was that this is a database product, not a chatbot. The value comes from accumulation — pattern detection across all your applications, offer clause benchmarking over time, interview performance tracked across every single round. None of that is possible with one-shot AI calls. So I built JobLens.

What It Does

JobLens is a full-stack job search intelligence platform. There are six main parts to it.

The Tracker is a drag and drop Kanban board for all your applications. Every move saves to the database instantly, columns are fully customizable, and rejected applications auto-archive after 15 days.

The JD Analyzer lets you paste any job description and get back required skills, red flags buried in the language, tech stack, and a personal fit score based on your profile.

The Interview Logger is where you log every round, get AI model answers to questions, rate your own answers and get feedback, and compute a confidence score which is your estimated probability of clearing that round.

The Offer Analyzer takes any offer letter PDF or pasted text and flags every clause green for standard, yellow for negotiate, or red for flag before signing. Each clause comes with a plain English explanation and the exact wording to ask them to change.

The War Room compares multiple offers side by side covering base salary, equity, vesting, total comp over four years and equity upside at three exit scenarios, then writes a personalized negotiation playbook from your actual numbers.

The Dashboard shows live metrics including response rate, interview conversion, search health score, application funnel and a top insight that surfaces your single biggest bottleneck.

The Stack

Frontend is Next.js 16 App Router with React, Tailwind CSS and shadcn. Database is Amazon Aurora PostgreSQL on Serverless v2. AI is Amazon Nova Pro through AWS Bedrock. Auth is AWS Cognito with OAuth 2.0 and PKCE. ORM is Drizzle with the postgres.js driver. The whole thing is deployed on Vercel with serverless functions at a 30 second timeout.

Why Aurora and Not DynamoDB

The first real decision was the database. DynamoDB is the obvious serverless choice on AWS. It is fast to set up and scales automatically.

But every meaningful insight in this app is a join. A dashboard response rate needs applications grouped by status. An interview confidence score needs rounds joined to questions joined to ratings. An offer comparison needs offer letters joined to clauses joined to applications. The relational depth is literally the product.

DynamoDB would have meant denormalizing everything into flat documents and rewriting every query as a scan. Aurora gave me full PostgreSQL with serverless scaling — the power of a real database without managing any instances.

The schema ended up with 12 tables. Users, applications, kanban columns, offer letters, offer clauses, interview rounds, interview questions, JD extractions, negotiation logs, follow-ups, resume versions and contacts. Every table has a clear owner and foreign keys that cascade correctly.

The connection from Vercel uses postgres.js with a pool of max 3 connections and SSL required. Aurora sits inside a VPC private subnet so it is never publicly exposed. On serverless you want a small pool because each Vercel function instance runs independently and if every one opens 10 connections you will exhaust Aurora's limit fast.

Authentication with Cognito

Every user gets their own isolated account. The flow is straightforward. The user hits the app, NextAuth detects no session and redirects to the login page. Cognito handles the OAuth 2.0 Authorization Code flow with PKCE. The token comes back, NextAuth wraps it into a JWT session, and on first login a callback upserts the user row in Aurora automatically.

Every API route then verifies the session server side before it touches the database. Nobody sees anyone else's data.

AI with Amazon Nova on Bedrock

All the AI features run on amazon.nova-pro-v1:0 through the Bedrock SDK. We could have used Claude or other stronger models. Nova was chosen because it is free within the hackathon tier and it handles every use case reliably.

IAM credentials scoped specifically to Bedrock are stored as environment variables. The principle is least privilege — the application can invoke Bedrock models and nothing else.

The pattern is the same across every AI feature. Structured prompt in, structured JSON out, saved to Aurora. The offer clause analysis works like this. The user uploads a PDF, the extract-pdf route uses unpdf to pull the text out server side, then the analyze-offer route sends it to Nova with a prompt asking it to act as an employment lawyer and return a specific JSON shape. Nova comes back with an array of clauses each with a risk level, plain English explanation and redline suggestion. Those get saved to the offer clauses table in Aurora, anonymized, and the response also cross-checks the user's existing applications and surfaces smart suggestion banners.

One thing worth noting is that structured prompts are far more reliable than conversational ones when you need parseable output. Every Nova call specifies the exact JSON shape and ends with return only valid JSON, no markdown. It makes the AI layer predictable. The few times Nova wraps the response in a code block, a simple regex catches it.

Deploying on Vercel

The frontend and backend both live in a single Next.js App Router project. Every API route becomes a serverless function automatically. The only config I needed was bumping the function timeout to 30 seconds to handle Aurora cold starts and longer AI inference calls.

Vercel's environment variables hold the Cognito client credentials, Aurora connection string and IAM keys. There is genuinely zero infrastructure to manage.

A Few Things I Learned

Aurora cold starts on Serverless v2 are real. On the first request after the database scales to zero you can see 5 to 10 second delays. The 30 second timeout exists for this reason. In production you would want a scheduled ping to keep it warm.

Keep the connection pool small on serverless. Max 3 per function instance is the right balance.

Drizzle ORM is excellent for this kind of project. Schema as TypeScript, migrations via Drizzle Kit, and a query builder that feels like writing SQL. It does not hide what is happening.

Top comments (0)