Skip to case study

PixShare

Keeping photographs and their data together.

A simple online photo album with a Next.js interface, FastAPI backend, PostgreSQL persistence and genuine Clerk authentication.

Image sharing / independent engineering project

Next.jsFastAPIPostgreSQLClerkPillowS3-compatible storage

Who it is for

People browsing public photographs and signed-in owners managing their own images and captions.

The problem it solves

An image upload needs more than a filename: the application must verify content, enforce ownership, select appropriate image sizes and keep storage references safe during replacement or failure.

What I personally built

Built and verified the album interface, media validation and variants, authenticated ownership boundaries, and safe local/S3-compatible storage lifecycle.

A focused photo album

Applied Hallmark Atelier with Sentient, Hanken Grotesk, warm paper and muted slate. Photograph composition, responsive rows, caption limits, visible focus and real recovery states lead the interface.

Authenticated media changes

Used genuine Clerk sessions and API owner checks. PostgreSQL row locks serialize replacement/deletion; old media is removed after commit, while a refresh failure cannot delete an already committed replacement.

Responsive and replaceable storage

Generated bounded WebP variants with original fallback. Local development needs no cloud account; an S3-compatible adapter uses immutable namespaces, private manifests and bounded SDK calls, verified against local MinIO.

How the product works

5 inspectable steps

Fresh current standalone frontend captures with bundled demo photographs, a cached release API and isolated PostgreSQL. The retained authenticated upload screenshot comes from the earlier real Clerk release suite and masks the avatar. The public frontend returned HTTP 200 on 27 October 2026, but its gallery could not reach the API.

Captured from the applicationVerified local app; demo photographs
PixShare Atelier homepage showing real demo photos and the upload action.
Actual local production build; bundled demonstration photos.Inspect full image (opens in a new tab)

Choose a detail step to use zoom and dimming.

The photographs come from repository demo assets. Counts and photos are real backend data; no user-growth or production claims are implied.

Guided inspection

Step 1 of 5

Open the photo album

The landing screen opens directly onto the working album, using the selected Atelier typography and restrained surfaces.

Authentic application captures. These controls explore screenshot evidence; they do not operate the app. Use Zoom to evidence, dim the surroundings, or open the full-resolution image. The text transcript below works without JavaScript.

Read every walkthrough step as text
  1. 1. Open the photo album

    The landing screen opens directly onto the working album, using the selected Atelier typography and restrained surfaces.

    The photographs come from repository demo assets. Counts and photos are real backend data; no user-growth or production claims are implied.

    Inspect corresponding screenshot (opens in a new tab)
  2. 2. Browse full compositions

    Dimension-aware rows preserve the source order and whole photograph. Filtering, sorting and pagination keep their existing API behavior.

    Images are public. Mutation ownership is a backend authorization boundary, separate from public image URLs.

    Inspect corresponding screenshot (opens in a new tab)
  3. 3. Inspect an image and its metadata

    Open a real image from the local gallery and inspect its caption, format and original file metadata.

    The photograph comes from the repository demo fixtures and is served by the real isolated backend.

    Inspect corresponding screenshot (opens in a new tab)
  4. 4. Add a photograph and caption

    The real picker and caption form retain validation, progress, error focus and public-visibility guidance.

    The full browser suite separately verifies signup, sign-in, account switching, upload, caption edit, replacement, ownership denial and deletion with real Clerk sessions.

    Inspect corresponding screenshot (opens in a new tab)
  5. 5. Read the album on a phone

    Mobile uses one source-ordered column, bounded captions, comfortable controls and the same light/dark system.

    Captured in Chromium at a mobile viewport. Physical-device and complete assistive-technology verification are not claimed.

    Inspect corresponding screenshot (opens in a new tab)

Browse a real local photo collection

Current local standalone frontend, cached release API and disposable PostgreSQL with bundled demo photographs. Real frontend interactions and API responses. Fresh authenticated upload/management is blocked; prior release evidence is retained and labeled. The public frontend responds, but its gallery was unavailable on 27 October 2026.

Actual Playwright recording · silent · native playback controls

Recording transcript

  1. Browse the local album and open the public gallery.
  2. Open a photograph and inspect its image details.
  3. Filter the persisted collection to PNG, restore all formats and change its sort order.
  4. Inspect the upload and personal-gallery sign-in prerequisites without bypassing authentication.

Read the Phase 5D capture and verification report

Inspect the mobile captures (390 × 844)
PixShare local public gallery on mobile with bundled photographs.
PixShare local public gallery on mobile with bundled photographs. Same capture environment as the recording.Inspect full image (opens in a new tab)
PixShare image details on mobile.
PixShare image details on mobile. Same capture environment as the recording.Inspect full image (opens in a new tab)

Public deployment check: PixShare frontend (opens in a new tab) responded with HTTP 200; the gallery could not reach its API. The card retains its case-study destination while this remains unresolved.

Difficult engineering decisions

Database commit versus object cleanup

PostgreSQL and media storage cannot commit atomically. New media is cleaned on commit failure, old media after success, and explicit reconciliation handles possible orphans.

Bounded image work

Decode limits protect the API, while WebP variants reduce selected payload size. Fixture-scoped byte savings support an engineering decision without claiming production latency or scale.

Architecture behind the interface

Validate bytes before storage

The API verifies MIME/format agreement and bounded Pillow decoding, with independent 10 MiB, 16-megapixel, side-length and request-envelope limits.

app/core/upload.py (opens in a new tab)

Keep the commit boundary explicit

Owner-only mutations lock the row. Failed commits clean new uploads; successful commits precede old-media cleanup. Database and storage cannot share an atomic transaction, so orphan limitations remain documented.

app/api/routes/posts.py (opens in a new tab)

Preserve originals and select variants

The browser chooses actual-width WebP sources through the existing picture component, while originals remain available. Storage routing preserves old local references when new writes use object storage.

web/components/posts/PostImage.tsx (opens in a new tab)

Evidence, with boundaries

Read Phase 5 verification report

Upstream results below are attributed to their recorded checks. This portfolio change does not rerun every application suite.

Backend regression

141 tests passed with a separate disposable PostgreSQL database, including 14 real local MinIO integration cases and demo-seed preservation/idempotence. Ruff and basedpyright passed.

Full browser regression

30 cases passed on the final Atelier production build, including setup, 24 public/device checks and five genuine Clerk journeys. Frontend lint, TypeScript and production build passed.

Accessibility and payload evidence

53 final workflow/state captures reported no axe violations or horizontal overflow. For three normalized demo photos at DPR 1, selected bodies totaled 91,058 bytes versus 3,333,388 original bytes, a 97.27% reduction. This is fixture-scoped payload evidence, not a latency or capacity benchmark.

API contract evidence

Earlier API capture, retained as secondary evidence. Frontend and authenticated lifecycle checks come from the later release report.

Actual PixShare FastAPI OpenAPI documentation.
Earlier local API capture; not the featured product image.Inspect full image (opens in a new tab)

What remains unverified

Working implementation and release readiness have different evidence requirements.

  • This task made no deployment. The public frontend URL responds, but its gallery was unavailable during browser verification even when API health returned 200. Local MinIO compatibility does not establish AWS IAM, CDN or recovery behavior.
  • Media is public and originals retain metadata. Private albums, moderation and account-deletion workflows are outside scope.
  • PostgreSQL and storage are not one atomic transaction. Crashes or failed cleanup can leave orphans; reconciliation is operator-invoked.
  • Axe and keyboard checks do not establish complete WCAG/screen-reader conformance. Chromium viewport captures are not physical-device certification.
  • Sentient loads through the official Fontshare service with a fallback. Authenticated local verification is distinct from the keyless public CI job.

Source links are pinned to the inspected revision. The Phase 5 report distinguishes retained captures, current source, prior test results and checks executed for this portfolio change.