Domain Glossary

# Pinterest Case Study

A case study for a Search & Pin Creation System. The deliverable is both a written architectural design document (showing scale reasoning, trade-offs, and failure modes) and a working implementation that validates the design.

# Stack

  • Frontend: Next.js (React) โ€” App Router with React Suspense streaming
  • Backend: Node.js API
  • Primary DB: PostgreSQL
  • Search index: Elasticsearch
  • Object storage: S3 (pin images, presigned upload)
  • CDN: CloudFront / Cloudflare (SSR HTML + image variants)
  • Cache: Redis (60s TTL on search results and suggestions)
  • Image processing: Sharp worker (WebP variants, dominant color extraction)
  • Queues: SQS โ€” processing queue (S3 upload โ†’ Sharp) and index queue (CDC โ†’ Elasticsearch)
  • CDC: Debezium (Postgres WAL โ†’ index queue)

# Scope

The system covers two surfaces: Pin Creation (authenticated users upload image + write description) and Search (public, no auth required). Auth exists as a pre-condition for creation but is not implemented in the case study โ€” routes and API endpoints are annotated as public vs protected; production would use a real session (mock auth is sufficient for interview narrative).

Auth boundaries (documented, not enforced in implementation):

Surface Auth
Search page (/search) Public
Suggestions API (GET /api/suggestions) Public
Search API (GET /api/search) Public
Pin detail (/pin/[id]) Public
Pin creation page (/pin/create) Protected โ€” requires authenticated user
Upload URL API (POST /api/pins/upload-url) Protected
Create pin API (POST /api/pins) Protected

Pin Creation โ€” case study scope: Simple form: image file upload + description textarea. The final Pin is a raw image stored in S3 with description stored in Postgres. On publish, user navigates to a minimal Pin detail page (/pin/[id]) showing the 736w image variant and description โ€” no related pins or save UI.

Pin detail โ€” case study scope: Server-rendered public page. Hero image (736w when available, original as fallback) + description + published confirmation on arrival from create. Closes the create โ†’ view loop after publish.

Pin Creation โ€” production extension: A canvas-based editor (Fabric.js / Konva.js) where users compose text layers over an image. The canvas is exported as a flat composited image at submission time. This is called out as a known next step but is out of scope for the case study implementation.

# Language

Pin: A user-created artifact consisting of an image (stored in S3, served via CDN) and a description text. Has a lifecycle status: uploading โ†’ processing โ†’ ready. Only ready pins are indexed in Elasticsearch and visible in search. Supported upload format: JPEG/PNG, max 20MB. Video (mp4) is out of scope for this case study. Avoid: Post, card, item

Pin Status: The internal lifecycle state of a Pin in Postgres. uploading: presigned URL issued, S3 upload in flight. processing: S3 upload confirmed, Sharp generating variants (backend-only โ€” never surfaced in the UI). ready: variants available, Pin indexable and searchable. Avoid: State, stage

Published: The user-visible outcome: image uploaded to S3 and description submitted via POST /api/pins. From the user's perspective the Pin exists immediately โ€” navigate to /pin/[id] and show a published confirmation. Sharp variant generation and ES indexing happen asynchronously in the background; the user is not shown a "processing" state for either. Avoid: Processing (user-facing), pending, draft (post-submit)

Search: A query-driven lookup of Pins by text, returning results ranked by BM25 relevance + recency decay (function_score). Paginated via Elasticsearch search_after cursor. Avoid: Filter, browse

Cursor: An opaque token returned by the search API representing the sort position of the last result on the current page. The client passes it back verbatim to fetch the next page. Avoid: Page token, offset, bookmark

Suggestions: Autocomplete terms surfaced as the user types in the search bar, before the search is submitted. Powered by Elasticsearch's completion suggester on indexed pin titles and descriptions. Requests are debounced at 200ms with AbortController cancellation of in-flight requests on each new keystroke. Avoid: Autocomplete, hints, typeahead

Masonry Grid: A multi-column layout where items have variable heights and are packed top-to-bottom within each column, with no fixed row height. Positions are JS-calculated using image dimensions stored at upload time โ€” no layout shift. DOM is virtualized from v1: only pins near the viewport are rendered, using pre-calculated absolute positions to determine visibility. Avoid: Grid, card grid, waterfall layout

Dominant Color: The most representative hex color extracted from a Pin's image by Sharp at upload time. Stored in Postgres (dominant_color). Used as the masonry placeholder background before the image loads โ€” fills the pre-calculated slot with the image's own color. Avoid: Placeholder color, background color, blur-up

Image Variant: A pre-generated resized copy of a Pin's original image, produced at upload time (via Sharp) and stored in S3. Three variants: 236w, 474w (served to the search grid via srcset), 736w (served on the Pin detail page). Original is never sent to the browser. Avoid: Thumbnail, resized image, scaled image

S3 Key: The permanent, immutable identifier for a Pin's image files in S3. Generated by the API at presigned URL creation time using the Pin's UUID: pins/{uuid}/original.{ext} for the source file, pins/{uuid}/236w.webp, pins/{uuid}/474w.webp, pins/{uuid}/736w.webp for variants. The UUID is also the Pin's primary key in Postgres. Because the URL is immutable, CDN serves these files with Cache-Control: max-age=31536000, immutable. Avoid: File path, image path, filename