ormDB

Solving N+1 Queries Forever with ormDB

Learn how ormDB eliminates N+1 queries by design. No eager loading hacks, no query batching -- the database itself understands object graphs.

Difficulty: beginnerTime: 10 minutes

The Problem Every ORM Developer Knows

ormDB is a relational database engine written in Rust that replaces PostgreSQL, MySQL, and SQLite. It is not an ORM. It is the actual database. And it eliminates N+1 queries by design, not by workaround.

The N+1 problem is the most persistent performance issue in ORM-based applications. It happens because SQL has no concept of an object graph. Traditional databases like PostgreSQL and SQLite all suffer from this fundamental limitation. When your ORM asks for users with their posts, the SQL database sees two separate concerns: a query for users and a query for posts. Your ORM bridges this gap with strategies like eager loading, batching, and data loaders, but these are application-level patches for a database-level limitation.

How Traditional Databases Handle Relations

Consider fetching 50 users, each with their posts and each post’s comments:

// [Prisma](/integrations/prisma) with PostgreSQL: generates multiple queries
const users = await prisma.user.findMany({
  include: {
    posts: {
      include: { comments: true },
    },
  },
})

Behind the scenes, PostgreSQL receives something like:

SELECT * FROM users;                               -- 1 query
SELECT * FROM posts WHERE author_id IN (...);       -- 1 query
SELECT * FROM comments WHERE post_id IN (...);      -- 1 query (best case with batching)

That is the best case with Prisma’s built-in batching. Without it, you get 1 + 50 + (50 * avg_posts) queries. Other ORMs without batching fare worse.

How ormDB Handles Relations

ormDB’s native query language is entity/relation/graph-fetch. Your ORM adapter translates the same code into a single ormDB request:

graph_fetch User {
  *,
  posts {
    *,
    comments { * }
  }
}

This is not SQL. This is a single request that tells ormDB: “Give me all users, with their posts, with each post’s comments.” ormDB resolves the entire graph internally using its relation-aware storage engine and returns the complete object tree in one round-trip over its zero-copy wire protocol.

There is no decomposition into multiple queries. There is no join assembly. The database understands the shape of your data and returns it directly.

What This Means in Practice

The performance difference compounds with depth and breadth. A four-level deep relation (users -> posts -> comments -> reactions) that would generate hundreds of queries against a SQL database is still a single round-trip with ormDB.

// Same Prisma code, ormDB underneath: one round-trip
const users = await prisma.user.findMany({
  include: {
    posts: {
      include: {
        comments: {
          include: { reactions: true },
        },
      },
    },
  },
})

Your application code does not change. Your ORM syntax does not change. The queries your ORM generates are intercepted by the adapter and translated into ormDB’s graph-fetch protocol. The N+1 problem disappears because the database speaks the same language your ORM thinks in.

Query Budgets Prevent Abuse

Graph fetches of arbitrary depth could return massive result sets. ormDB addresses this with query budgets: engine-level limits on the computational cost and result size of any query. Learn more in the performance optimization guide. You configure maximum traversal depth, fanout limits per relation, and total result size caps. This ensures that even the most complex graph fetch stays within predictable resource bounds.

graph_fetch User {
  *,
  posts [limit: 20] {
    *,
    comments [limit: 50] { * }
  }
} [budget: 10000]

N+1 is not a problem you manage. With ormDB, it is a problem that does not exist.

Frequently Asked Questions

What exactly is the N+1 query problem?

The N+1 problem occurs when your application issues 1 query to fetch a list of records, then N additional queries to fetch related data for each record. Fetching 100 users with their posts generates 101 queries: 1 for users, 100 for each user's posts.

How does ormDB eliminate N+1 queries?

ormDB's query language is entity/relation/graph-fetch, not SQL. When you request a user with their posts and comments, ormDB receives the entire graph shape and resolves it internally in a single round-trip. There is no decomposition into multiple queries.

Do I still need eager loading or prefetching with ormDB?

Your ORM's eager loading syntax (Prisma's include, Django's prefetch_related, etc.) still works and is how you tell the adapter what relations you want. The difference is that ormDB resolves the entire tree in one round-trip instead of multiple queries.

Does this work for deeply nested relations?

Yes. ormDB resolves graph fetches of arbitrary depth in a single round-trip. Users -> posts -> comments -> reactions -> authors is one request, not a cascade of queries.

What if I only need some fields from related entities?

ormDB's graph-fetch protocol supports field selection at every level of the graph. Your ORM's select or only mechanism maps to this, so you fetch only the fields you need without over-fetching.

Is there a performance cost to deep graph fetches?

ormDB uses query budgets to prevent runaway graph fetches. You can set limits on total result size and traversal depth, ensuring that even complex graph fetches stay within predictable resource bounds.

Related Content

Try ormDB today

Open source, MIT licensed. Install and start building.