Notes
Profiling TypeORM queries in NestJS
The problem
NestJS makes it easy to stack repositories, services, and interceptors until no single place in your code issues a query, and no single place sees them all. The endpoint is slow, the code reads fine, and the forty queries hiding behind one innocent relations option stay invisible.
TypeORM's built-in answer is logging: true, which prints every statement to stdout. In development that is a firehose you learn to scroll past; in production you turn it off. Neither mode answers the questions that matter: which queries ran for this request, how long did each take, and which ones ran more times than they should have?
What you actually want to measure
- Per-query duration, not just the slow-query threshold log.
- Query count per request: the N+1 signal. Ninety near-identical selects behind one endpoint show up in a count and hide in a log stream.
- Which entity and which call path produced the query, so the fix lands in the right repository.
TypeORM exposes the hook for all of this: a custom logger receives every query with its parameters and timing. The work is aggregation: grouping by statement shape, attaching counts and durations, and putting the result somewhere you look, instead of a log you don't.
The tool
I packaged that aggregation as nestjs-query-profiler, a NestJS module that hooks into TypeORM and reports database query performance per request. You register the module, and instead of a stdout firehose you get grouped, timed query profiles you can act on.
Setup and options are in the package README.
What it changed for me
I stopped profiling only after an endpoint got slow enough for someone to complain. Now I glance at query counts while building the feature, when the fix is still a one-line relations change instead of an incident.