Part 8.2 — GraphQL Mental Model: Schemas, Types & Resolvers (Conceptual)
GraphQL is often misunderstood as “a different API protocol.”
In reality, it’s a mental model for designing APIs.
Once you understand that model—schemas, types, and resolvers—you can apply the same clarity to REST APIs built with NestJS, Prisma, and PostgreSQL.
This post is about thinking like GraphQL, not using it.
1. Start with the Schema: A Contract, Not an Implementation
In GraphQL, everything starts with a schema.
The schema answers:
what data exists
how data relates
what clients are allowed to ask for
Crucially, it does not describe:
databases
tables
HTTP routes
implementation details
It’s a capability contract.
2. REST Often Skips the Schema Step
In many REST APIs, the “schema” is implicit:
inferred from controllers
scattered across DTOs
discovered by reading code
This leads to:
inconsistent responses
accidental breaking changes
unclear client expectations
GraphQL forces you to define the contract first.
You can—and should—do the same with REST.
3. GraphQL Types = Response Shapes
A GraphQL type describes a shape of data.
Conceptually:
User {
id
name
email
}This is not a database table.
It’s a response shape.
In REST, this maps directly to DTOs.
export class UserDto {
id: number;
name: string;
email: string;
}Key idea:
Types describe what clients receive, not how data is stored.
4. Think in Public Types, Not Entities
A common backend mistake is returning database entities directly.
GraphQL forbids this by design.
REST should too.
Good practice:
Prisma models → internal
DTOs → public contract
This separation lets you:
change databases safely
evolve APIs without breaking clients
hide internal fields
GraphQL’s type system enforces discipline REST developers must choose consciously.
5. Resolvers: The Most Important Concept
Resolvers are where GraphQL shines conceptually.
A resolver answers one question:
“How do I get the data for this field?”
Resolvers are:
small
focused
composable
They don’t care about HTTP.
They care about data access and composition.
6. REST Controllers Are Already Resolvers
Here’s the key insight:
A NestJS controller method is already a resolver.
@Get(':id')
getUser(@Param('id') id: number) {
return this.usersService.findById(id);
}This resolves:
a specific request
to a specific data shape
The difference is scope:
GraphQL resolves fields
REST resolves endpoints
The mental model is the same.
7. Services Are Resolver Logic
In clean NestJS architecture:
controllers map requests → intent
services resolve data → logic
Prisma resolves data → persistence
This maps cleanly to GraphQL thinking:
GraphQL | REST (NestJS) |
|---|---|
Schema | DTOs + API docs |
Type | DTO |
Resolver | Controller + Service |
Data source | Prisma + PostgreSQL |
Same roles. Different transport.
8. Field-Level Thinking Improves REST Design
GraphQL encourages you to think:
which fields are expensive?
which relations are optional?
which data is frequently needed together?
Apply this to REST by:
designing focused DTOs
avoiding “everything” responses
exposing optional includes explicitly
Good REST feels intentional—never accidental.
9. Nested Data Without Losing Control
GraphQL allows nested queries:
user {
posts {
comments
}
}REST can support this intentionally, not implicitly.
Example:
GET /users/1?include=posts
GET /users/1?include=posts.commentsThis is GraphQL-style thinking using REST semantics.
10. Avoid the “One Endpoint = One Page” Trap
GraphQL breaks the idea that:
“Every screen needs a new endpoint.”
REST APIs often grow this way.
Instead:
design endpoints around resources
let clients compose UI from responses
avoid UI-driven endpoints
This reduces API sprawl dramatically.
11. Resolver Complexity Still Exists
Important reality check:
Resolvers don’t remove complexity.
They relocate it.
In REST, complexity shows up as:
bloated endpoints
duplicated logic
inconsistent shapes
In GraphQL, it shows up as:
resolver orchestration
query planning
performance tuning
Either way, you must design carefully.
12. The Transferable Lesson
The most valuable GraphQL idea is not the query language.
It’s this:
Separate what clients can ask for from how data is retrieved.
REST developers who internalize this build:
clearer APIs
safer contracts
happier frontend teams
13. What We’ll Do Next
Now that you understand:
schemas as contracts
types as response shapes
resolvers as data logic
Related
Part 8.1 — Why GraphQL Exists: Problems It Tries to Solve
GraphQL didn’t appear because REST failed. It appeared because many REST APIs were designed without client needs in mind.
Part 4.6 — Building a Maintainable Testing Architecture
Writing tests is easy. Maintaining hundreds of them is hard. This post shows how to structure your NestJS backend tests so they scale without pain.
Part 4.4 — Testing Auth Guards, JWT Strategy & Protected Routes
Authentication logic is critical—and fragile if untested. In this post, you’ll learn how to test JWT auth guards and protected REST routes with confidence.
Comments