polycratia

A slow API on top of a fast database is not a contradiction. That is the normal outcome of measuring execution and ignoring assembly.

The database answers exactly the question you asked, in milliseconds. Then the response still has to be built. Every row becomes an object. Every object walks a serializer. Nested relations expand, computed fields run, permission checks fire per field, per row. None of that shows up in a query log, because none of it is a query.

What usually fails is the shape of the response, not its speed. An endpoint that returns a whole catalog entry with its variants, media, and pricing history is cheap to query and expensive to emit. That payload grows with your data, not with your traffic, so the endpoint gets slower month over month while every single query stays fast. Put it behind a list view and the cost multiplies by page size.

The part that keeps this hidden is instrumentation asymmetry. Databases ship with timing tools and slow-query logs. Serialization ships with nothing.

So I log two numbers per response next to query time: rows hydrated and serialized bytes. The moment you can see those, the slow endpoints stop being mysterious. They are the ones emitting a megabyte so a client can render twelve fields.

And the fix is a contract rather than an optimization: explicit field selection, server-enforced page caps, no implicit nesting. A list endpoint and a detail endpoint are different endpoints, even when they describe the same object.

I write up more production trade-offs like this one here: https://polycratia.com/c/3f00b94c

Your heaviest endpoint spends its time somewhere: in the query, or after it...

react

$ new-project --brief

or email hey@polycratia.com