Back to browse
GitHub Repository

A lightweight TypeScript library that makes GraphQL resolvers truly lazy by default. Developers define models using getter methods, and the library ensures only the requested fields are computed at runtime.

3 starsTypeScript

LazyQL: Only compute the GraphQL fields requested (TypeScript)

by matutetandil·Mar 9, 2026·1 point·3 comments

AI Analysis

●●SolidBig Brain

Proxy-based lazy GraphQL field resolution saves unnecessary database calls.

Strengths
  • JavaScript Proxy intercepts field access for true lazy evaluation at runtime.
  • Decorator validates DTO-to-getter mappings automatically, catching errors early.
Weaknesses
  • GraphQL already has DataLoader and field-level resolvers for similar optimization.
  • Niche problem that most GraphQL frameworks already address with existing patterns.
Target Audience

Backend developers building GraphQL APIs with TypeScript

Similar To

Apollo GraphQL · GraphQL-JS · DataLoader

Post Description

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.

Similar Projects