|
Hey HN, I built LazyQL, a small TypeScript library, because I was tired of GraphQL resolvers doing unnecessary work. The problem: In a typical GraphQL API, your resolver computes all fields for an object, even if the client only asked for 2 out of 15. This means unnecessary database calls, API requests, and CPU time — all thrown away. The solution: LazyQL uses JavaScript Proxy to intercept field access. You write a class with getter methods (getStatus(), getCustomerEmail(), etc.), and LazyQL ensures each getter only runs when GraphQL actually reads that field. @LazyQL(OrderDTO)
class Order {
constructor(private id: number, private db: Database) {}
getEntityId() { return this.id; }
getStatus() { return this.db.getOrderStatus(this.id); }
async getCustomerEmail() { return this.db.getCustomerEmail(this.id); }
async getShippingAddress() { return this.db.getShippingAddress(this.id); }
}
// Query { entity_id, status } → only getEntityId() and getStatus() run
// getCustomerEmail() and getShippingAddress() never execute
In my benchmarks, a query requesting 3 out of 10 fields made 6 calls instead of 35 with the traditional approach.Key design decisions:
- Works transparently with Apollo, Mercurius, or any GraphQL server — they don't know LazyQL exists
- Naming convention maps snake_case DTO fields to getCamelCase methods automatically
- @Shared() decorator caches expensive operations that multiple getters depend on
- Validates at startup that all required DTO fields have matching getters (fail fast)
- Zero runtime dependencies beyond reflect-metadata It's been running in production for a few weeks now with zero issues. The whole point was to build something invisible — it sits there, does its job, and doesn't interfere with anything. ~400 lines of code, MIT licensed. Would love to hear your thoughts. |
Or is this doing more?