Role-based access control in GraphQL - Time & Space Complexity
When checking user permissions in role-based access control, we want to know how the time to verify access changes as the number of roles or users grows.
We ask: How does the system's work increase when more roles or users are involved?
Analyze the time complexity of the following code snippet.
query GetUserPermissions($userId: ID!) {
user(id: $userId) {
roles {
permissions {
name
}
}
}
}
This query fetches a user's roles and the permissions each role grants.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Traversing the list of roles and then the list of permissions for each role.
- How many times: Once for each role, and inside that, once for each permission of that role.
As the number of roles (r) and permissions per role (p) increase, the total checks grow by multiplying these counts.
| Input Size (roles x permissions) | Approx. Operations |
|---|---|
| 10 roles x 5 permissions | 50 |
| 100 roles x 5 permissions | 500 |
| 100 roles x 20 permissions | 2000 |
Pattern observation: The work grows roughly by multiplying the number of roles by the number of permissions per role.
Time Complexity: O(r × p)
This means the time to check permissions grows in proportion to the number of roles times the permissions each role has.
[X] Wrong: "Checking permissions is always fast and constant time regardless of roles or permissions."
[OK] Correct: The system must look through each role and its permissions, so more roles or permissions mean more work.
Understanding how permission checks scale helps you design systems that stay quick even as users and roles grow. This skill shows you can think about real-world system performance.
"What if we cached permissions per user instead of checking roles each time? How would the time complexity change?"
Practice
Solution
Step 1: Understand RBAC concept
RBAC limits what data users can access depending on their assigned roles.Step 2: Identify RBAC purpose in GraphQL
In GraphQL, RBAC ensures users only see or modify data allowed by their role.Final Answer:
To restrict data access based on user roles -> Option CQuick Check:
RBAC = restrict access by role [OK]
- Confusing RBAC with performance optimization
- Thinking RBAC formats data output
- Assuming RBAC caches data
Solution
Step 1: Identify correct directive syntax
The standard directive for role checks is @hasRole with a role argument as a string.Step 2: Match syntax to options
type Query { users: [User] @hasRole(role: "admin") } uses @hasRole(role: "admin"), which is the correct syntax.Final Answer:
type Query { users: [User] @hasRole(role: "admin") } -> Option AQuick Check:
@hasRole(role: "admin") syntax correct [OK]
- Using incorrect directive names like @auth or @roleCheck
- Missing quotes around role name
- Using = instead of : in directive arguments
query { editArticle(id: "123") { title content } }Solution
Step 1: Understand role restriction effect
@hasRole(role: "editor") restricts access to users with the editor role only.Step 2: Check user role against required role
User has role "viewer", which does not match "editor", so access is denied.Final Answer:
The query returns an authorization error -> Option AQuick Check:
Role mismatch = authorization error [OK]
- Assuming query returns data anyway
- Thinking query returns empty data silently
- Believing server crashes on role denial
type Mutation { deleteUser(id: ID!): Boolean @hasRole(role: admin) }Why does this cause a syntax error?
Solution
Step 1: Check directive argument syntax
Directive arguments require string values in quotes, e.g., role: "admin".Step 2: Identify missing quotes error
Here, role: admin lacks quotes, causing a syntax error.Final Answer:
The role value admin is missing quotes -> Option DQuick Check:
Directive string args need quotes [OK]
- Thinking directive names are wrong
- Believing directives can't be on mutations
- Misunderstanding ID! type syntax
Solution
Step 1: Understand multi-role directive syntax
@hasRole accepts an array of roles to allow multiple roles access.Step 2: Evaluate options for correct syntax
type Mutation { updatePrice(id: ID!, price: Float): Product @hasRole(role: ["admin", "manager"]) } uses @hasRole(role: ["admin", "manager"]), which correctly passes an array of roles.Step 3: Identify incorrect options
type Mutation { updatePrice(id: ID!, price: Float): Product @hasRole(roles: "admin,manager") } passes a string, not an array; C uses multiple directives which may not combine logically; A uses a non-standard directive @hasAnyRole.Final Answer:
type Mutation { updatePrice(id: ID!, price: Float): Product @hasRole(role: ["admin", "manager"]) } -> Option BQuick Check:
Array of roles in @hasRole = correct [OK]
- Passing roles as comma string instead of array
- Using multiple @hasRole directives expecting OR logic
- Using non-standard directive names
Ready to prove it?
Pass the test and this pattern moves unknown
