API Design
GraphQL
The Schema, Resolvers, the N+1 Problem, and Security risks.
GraphQL
REST over-fetches (gets too much data) or under-fetches (needs too many requests). GraphQL solves this by letting the client ask for exactly what it needs.
The Schema First Approach
GraphQL is typed. You define a schema (schema.graphql) before writing code.
type User {
id: ID!
name: String
posts: [Post] # Relationship
}
type Query {
user(id: ID!): User
}The Resolver
A function that fetches the data for a specific field.
Query.user-> Fetches User from DB.User.posts-> Fetches Posts whereauthor_id = user.id.
The N+1 Problem
The most common performance killer in GraphQL.
Scenario: You query for 50 Users, and for each user, you ask for their posts.
- 1 Query:
SELECT * FROM Users LIMIT 50 - 50 Queries:
SELECT * FROM Posts WHERE user_id = ?(Runs once per user).
The Fix: DataLoader A batching utility.
- Resolvers call
loader.load(id). - DataLoader waits a "tick" (loop of the event loop).
- It collects all 50 IDs.
- It runs one query:
SELECT * FROM Posts WHERE user_id IN (1, 2, ... 50). - It distributes the results back to the resolvers.
Security Risks
GraphQL gives clients immense power. You must guard it.
1. Query Depth Limit
A malicious client can ask for: User -> Posts -> Comments -> Author -> Posts -> Comments...
- Fix: Limit query depth to e.g., 5 levels.
2. Complexity Analysis
Some fields are harder to calculate than others.
- Fix: Assign "points" to fields. Reject queries that cost > 1000 points.
When to use GraphQL?
- Yes: Complex frontends with deep relational data (e.g., Facebook, Dashboard).
- No: Simple CRUD apps, Microservice-to-Microservice comms (Use gRPC), or public APIs where you want strict caching (REST is better at HTTP caching).