BACKEND & APIS//5 MIN READ

Fixing N+1 Chat Waterfalls and Building Multi-Image Review Proof on the Backend

How I refactored backend API serializers to eliminate N+1 chat profile waterfalls, added multi-image photo proof to artisan ratings, and cleaned up OpenAPI schemas.

if the frontend has to make 4 requests to render 1 chat header, fix the serializer.

A lot of frontend sluggishness isn't actually a frontend problem—it is the symptom of an under-designed backend serializer forcing the client to stitch relational data together over the network.

While working across the backend API and React/TanStack frontend of our artisan marketplace, I ran into four classic API friction points: chat threads flashing generic placeholder names, text-only reviews that couldn't prove physical craftsmanship, redundant schema fields, and missing job category associations. Here is how I fixed them at the API layer.

1. Direct Counterparty Info on Chats (/api/v1/chats/)

Originally, our chat endpoints only returned raw foreign-key IDs for the participants (artisan and customer). To render a simple inbox list or chat header with names and avatars, the frontend had to fire secondary lookup requests for every thread. On slow mobile connections, users saw fallback labels like 'Artisan' and 'Customer' before the real profiles resolved.

  • Updated the chat serializers for both the collection endpoint (GET /api/v1/chats/) and single thread retrieve (GET /api/v1/chats/{id}/).
  • Joined the respective artisan and customer profile tables directly at the query level.
  • Injected four enriched counterparty fields directly into the response payload: customer_name, customer_image_url, artisan_name, and artisan_image_url.
  • Result: Every chat list item and message header renders participant names and avatars on the first paint with zero extra network round trips.

2. Multi-Image Attachments for Artisan Reviews

When hiring an electrician, plumber, or solar installer, a 5-star icon and a one-line comment aren't enough. Customers want to see visual proof of the finished work, and good artisans want their craftsmanship documented.

Previously, the rating endpoint only accepted a numeric rating and a text string. I expanded both the write and read paths to support multi-image review galleries:

  • Rate Endpoint (POST /api/v1/profiles/artisans/{id}/rate/): Updated the request schema to accept an images array of uploaded media URLs alongside rating and comment, persisting them via a dedicated review-media relational table.
  • Ratings Listing (GET /api/v1/profiles/artisans/{id}/ratings/): Updated the serializer to return an image_data array containing { id, url } objects for every attached photo, powering full-screen review lightboxes on artisan profiles.

3. Schema Hygiene & Job Service Category Association

As a product evolves, old fields linger in serializers and confuse frontend consumers. The ArtisanProfile serializer still carried a legacy skills array alongside our formalized service_categories relation. At the same time, GET /api/v1/jobs/{id}/ was omitting consistent service_category data needed during chat and job matching.

  • Stripped the deprecated skills field from ArtisanProfile serializers and OpenAPI specs, keeping the contract focused on canonical fields (business_name, service_categories, address_string, working_hours, portfolio_photos, and pricing).
  • Exposed service_category directly on the job detail response model (GET /api/v1/jobs/{id}/) so trade identification and specialty matching stay consistent from job creation through to in-app chat.
my take ↓

Anyway, that's what I've been thinking about lately. Back to building.

From the archive

Read something else.