2026Featured Project

PulseBoard

An end-to-end data pipeline that ingests Hacker News and NewsAPI headlines, scores them for sentiment, transforms them through dbt staging and marts, orchestrates runs in Airflow, and serves a live FastAPI + Next.js dashboard

Tech Stack

Python 3.13PostgreSQL 14dbt 1.11Apache AirflowConfluent KafkaFastAPIpsycopg2TextBlobNext.js 14Tailwind CSSSWRDocker Compose

Context

The Problem

Most data-engineering portfolios show one piece in isolation — a dbt project, an Airflow DAG, or a dashboard. PulseBoard was built to show the whole pipeline working together: ingestion, storage, transformation, orchestration, streaming, sentiment, API, and frontend, all wired into one system.

Constraints

  • Pipeline must be idempotent — re-running must not produce duplicate rows
  • Both batch (Airflow) and streaming (Kafka) ingestion paths must write to the same PostgreSQL tables
  • Every transformation must be covered by dbt schema tests (unique, not_null)
  • Sentiment scoring must be applied at ingest, not as a downstream lookup
  • Dashboard must reflect new data without requiring a manual refresh

Stakes

Designed as a coherent data-engineering portfolio demonstrating fluency across the full modern data stack rather than competence in any single tool

My Role

Title

Data Engineer & Full-Stack Developer

Team

Personal Project

Ownership

End-to-end ownership: Python ingestion, PostgreSQL schema, dbt models, Airflow DAGs, Kafka streaming path, FastAPI REST layer, and Next.js dashboard

Approach & Key Decisions

Python fetchers pull Hacker News stories and NewsAPI headlines into a PostgreSQL raw schema, applying TextBlob sentiment polarity at ingest. dbt 1.11 transforms raw data through staging models (cleaning) into marts (business-ready analytics) with schema tests enforcing uniqueness and not-null. Airflow orchestrates hourly runs; a Confluent Kafka path (via Docker) offers a real-time alternative writing to the same tables. FastAPI exposes three endpoints with auto-generated Swagger docs, and a Next.js 14 + Tailwind dashboard uses SWR to auto-refresh every 60 seconds.

Idempotent upserts on PostgreSQL using source-provided IDs

Lets the pipeline be re-run safely — backfills, retries, and replays never produce duplicates, which is critical for any production-grade ingestion.

Layered dbt models: raw → staging → marts

Keeps raw data untouched for auditability, isolates cleaning logic in staging, and presents only business-ready models to the API — the standard modern-data-stack pattern.

Both Airflow batch and Kafka streaming writing to the same tables

Demonstrates that the storage layer is the integration point, not the ingestion mechanism — either path can feed the same downstream consumers.

Sentiment scoring at ingest with TextBlob

Stores polarity alongside the raw row so downstream models and APIs never need to recompute it; sentiment becomes a first-class column from the start.

SWR auto-refresh every 60 seconds on the dashboard

Trades a tiny amount of API load for a perceptibly live UI — users see fresh data without needing to reload.

Challenges & Solutions

Challenge

Re-running ingestion typically risks duplicate rows

Solution

Upserts on source-provided IDs make the pipeline safely re-runnable end to end

Challenge

Maintaining two ingestion paths (batch and streaming) without divergence

Solution

Both Airflow and Kafka write to the same raw tables; transformations and APIs are unaware of which path produced a row

Challenge

Guaranteeing transformations don't silently degrade

Solution

Every dbt model is covered by schema tests (unique, not_null) that run on every build — 6/6 tests passing across 3 models

Challenge

Re-computing sentiment for every API response would be wasteful

Solution

Polarity is computed once at ingest and stored on the row, so the API and dashboard read it for free

Outcomes & Impact

dbt Models

3 models building successfully with 6/6 schema tests passing (unique, not_null)

Orchestration

Airflow DAG running hourly with 100% task success rate

Sentiment Coverage

Polarity score and label (positive/negative/neutral) applied to 100% of ingested headlines

Streaming Path

Kafka producer/consumer streaming 10+ stories per run into the same PostgreSQL tables as the batch path

Dashboard

Next.js + Tailwind dashboard auto-refreshing every 60 seconds across trending topics, HN, and news feeds

Phases Delivered

8 complete phases: ingestion, storage, transformations, orchestration, API, dashboard, streaming, sentiment

Project Links