Bird
Raised Fist0
GraphQLquery~10 mins

SQL database resolvers in GraphQL - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - SQL database resolvers
GraphQL Query Received
↓
Resolver Function Called
↓
Build SQL Query
↓
Send SQL Query to Database
↓
Database Executes Query
↓
Receive Data from Database
↓
Return Data to GraphQL
↓
GraphQL Response Sent
The flow shows how a GraphQL query triggers a resolver that builds and sends an SQL query to the database, then returns the data back to the client.
Execution Sample
GraphQL
query getUser {
  user(id: 1) {
    id
    name
  }
}
This GraphQL query asks for the user with id 1, requesting their id and name.
Execution Table
StepActionSQL QueryDatabase ResponseGraphQL Response
1Receive GraphQL query
2Call resolver for 'user'SELECT id, name FROM users WHERE id = 1;
3Send SQL query to databaseSELECT id, name FROM users WHERE id = 1;
4Database executes querySELECT id, name FROM users WHERE id = 1;{id: 1, name: "Alice"}
5Receive data from database{id: 1, name: "Alice"}
6Return data to GraphQL{user: {id: 1, name: "Alice"}}
7Send GraphQL response to client{user: {id: 1, name: "Alice"}}
💡 GraphQL response sent to client with requested user data.
Variable Tracker
VariableStartAfter Step 2After Step 4Final
GraphQL Querynullquery getUser { user(id: 1) { id name } }query getUser { user(id: 1) { id name } }query getUser { user(id: 1) { id name } }
SQL QuerynullSELECT id, name FROM users WHERE id = 1;SELECT id, name FROM users WHERE id = 1;SELECT id, name FROM users WHERE id = 1;
Database Responsenullnull{id: 1, name: "Alice"}{id: 1, name: "Alice"}
GraphQL Responsenullnullnull{user: {id: 1, name: "Alice"}}
Key Moments - 3 Insights
Why does the resolver build an SQL query instead of returning data directly?
The resolver translates the GraphQL query into an SQL query to fetch data from the database, as shown in execution_table step 2 where the SQL query is built.
What happens if the database returns no data?
If no data is returned, the resolver returns null or an empty response to GraphQL, stopping at step 5 with an empty database response.
How does the resolver know which fields to request from the database?
The resolver reads the requested fields from the GraphQL query and includes only those in the SQL SELECT statement, as seen in step 2 with 'id' and 'name'.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what SQL query is built at step 2?
ASELECT * FROM users;
BSELECT id, name FROM users WHERE id = 1;
CINSERT INTO users VALUES (1, 'Alice');
DDELETE FROM users WHERE id = 1;
💡 Hint
Check the 'SQL Query' column at step 2 in the execution_table.
At which step does the database send back the user data?
AStep 4
BStep 3
CStep 5
DStep 6
💡 Hint
Look at the 'Database Response' column in the execution_table.
If the GraphQL query requested the user's email too, how would the SQL query change at step 2?
ASELECT id, name FROM users WHERE id = 1;
BSELECT email FROM users WHERE id = 1;
CSELECT id, name, email FROM users WHERE id = 1;
DSELECT * FROM users;
💡 Hint
The SQL query includes all requested fields from the GraphQL query, see step 2.
Concept Snapshot
SQL Database Resolvers in GraphQL:
- Resolver receives GraphQL query
- Builds SQL query with requested fields
- Sends SQL query to database
- Receives data from database
- Returns data to GraphQL response
- Enables dynamic data fetching based on query
Full Transcript
When a GraphQL query is received, the resolver function is called. The resolver builds an SQL query that selects only the requested fields from the database. This SQL query is sent to the database, which executes it and returns the data. The resolver then returns this data back to GraphQL, which sends the response to the client. This process allows GraphQL to fetch exactly the data requested by the client from an SQL database.

Practice

(1/5)
1. What is the main role of SQL database resolvers in a GraphQL server?
easy
A. To format the GraphQL query results into JSON
B. To define the GraphQL schema types and fields
C. To handle user authentication and authorization
D. To fetch or modify data in the SQL database based on GraphQL queries or mutations

Solution

  1. Step 1: Understand the purpose of resolvers

    Resolvers are functions that connect GraphQL queries or mutations to the actual data source, here an SQL database.
  2. Step 2: Identify the role in data fetching and modification

    Resolvers run SQL commands to get or change data as requested by the GraphQL operations.
  3. Final Answer:

    To fetch or modify data in the SQL database based on GraphQL queries or mutations -> Option D
  4. Quick Check:

    Resolvers connect queries to SQL data [OK]
Hint: Resolvers link GraphQL queries to SQL commands [OK]
Common Mistakes:
  • Confusing resolvers with schema definitions
  • Thinking resolvers handle authentication
  • Assuming resolvers format output only
2. Which of the following is the correct way to define a resolver function for a GraphQL query named getUser that fetches user data from an SQL database?
easy
A. const resolvers = { Query: { getUser: () => db.query('SELECT * FROM users') } };
B. const resolvers = { Mutation: { getUser: () => db.query('SELECT * FROM users') } };
C. const resolvers = { Query: { getUser: 'SELECT * FROM users' } };
D. const resolvers = { getUser: () => db.query('SELECT * FROM users') };

Solution

  1. Step 1: Identify the correct resolver structure for a query

    Resolvers for queries are defined inside the Query object with function values.
  2. Step 2: Check the syntax for calling the database query

    The resolver should be a function returning the result of db.query with the SQL command string.
  3. Final Answer:

    const resolvers = { Query: { getUser: () => db.query('SELECT * FROM users') } }; -> Option A
  4. Quick Check:

    Query resolvers are functions inside Query object [OK]
Hint: Query resolvers go inside Query object as functions [OK]
Common Mistakes:
  • Placing query resolver inside Mutation
  • Assigning SQL string directly instead of a function
  • Defining resolver outside Query or Mutation objects
3. Given the resolver below, what will be the output when the GraphQL query { getUserById(id: 2) { name } } is executed?
const resolvers = {
  Query: {
    getUserById: (_, args) => {
      const users = [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' },
        { id: 3, name: 'Carol' }
      ];
      return users.find(user => user.id === args.id);
    }
  }
};
medium
A. { "data": { "getUserById": { "name": "Alice" } } }
B. { "data": { "getUserById": { "name": "Bob" } } }
C. { "data": { "getUserById": null } }
D. Error: args.id is undefined

Solution

  1. Step 1: Understand the resolver logic

    The resolver searches the users array for an object with id equal to args.id.
  2. Step 2: Match the query argument with the array data

    The query requests id: 2, so the resolver returns the user with id: 2, which is { id: 2, name: 'Bob' }.
  3. Final Answer:

    { "data": { "getUserById": { "name": "Bob" } } } -> Option B
  4. Quick Check:

    Find user with id 2 = Bob [OK]
Hint: Find user by id in array using args.id [OK]
Common Mistakes:
  • Confusing id 2 with id 1 or 3
  • Forgetting to access args.id
  • Assuming resolver returns entire user object without filtering
4. Identify the error in this resolver code snippet for a mutation that inserts a new user into the SQL database:
const resolvers = {
  Mutation: {
    addUser: (parent, args) => {
      db.query(`INSERT INTO users (name, email) VALUES (${args.name}, ${args.email})`);
      return { success: true };
    }
  }
};
medium
A. SQL injection risk due to string interpolation without parameterization
B. Missing return statement for the inserted user data
C. Incorrect resolver placement inside Mutation instead of Query
D. Using backticks instead of single quotes for SQL string

Solution

  1. Step 1: Analyze the SQL query construction

    The SQL command uses string interpolation to insert args.name and args.email directly into the query string.
  2. Step 2: Identify security risk

    This approach allows SQL injection attacks if user input contains malicious SQL code. Proper parameterization or prepared statements are needed.
  3. Final Answer:

    SQL injection risk due to string interpolation without parameterization -> Option A
  4. Quick Check:

    Unsafe string interpolation causes SQL injection [OK]
Hint: Never insert variables directly into SQL strings [OK]
Common Mistakes:
  • Ignoring SQL injection vulnerability
  • Thinking backticks cause errors
  • Expecting mutation in Query object
5. You want to write a resolver that fetches all orders for a user, but only if the user is active. The SQL tables are users(id, active) and orders(id, user_id, total). Which resolver implementation correctly enforces this condition efficiently?
hard
A. const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM orders WHERE user_id = ${args.userId}`) } };
B. const resolvers = { Query: { userOrders: (_, args) => { const user = db.query(`SELECT * FROM users WHERE id = ${args.userId}`); if (user.active) { return db.query(`SELECT * FROM orders WHERE user_id = ${args.userId}`); } else { return []; } } } };
C. const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM orders WHERE user_id = ${args.userId} AND EXISTS (SELECT 1 FROM users WHERE id = ${args.userId} AND active = TRUE)`) } };
D. const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM users WHERE id = ${args.userId} AND active = TRUE`) } };

Solution

  1. Step 1: Understand the requirement

    Fetch orders only if the user is active, combining the check and data fetch efficiently.
  2. Step 2: Evaluate each option

    const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM orders WHERE user_id = ${args.userId} AND EXISTS (SELECT 1 FROM users WHERE id = ${args.userId} AND active = TRUE)`) } }; uses a single SQL query with EXISTS to check user active status and fetch orders in one call, which is efficient and correct.
    const resolvers = { Query: { userOrders: (_, args) => { const user = db.query(`SELECT * FROM users WHERE id = ${args.userId}`); if (user.active) { return db.query(`SELECT * FROM orders WHERE user_id = ${args.userId}`); } else { return []; } } } }; fetches user first then orders separately, causing multiple queries and possible async issues.
    const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM orders WHERE user_id = ${args.userId}`) } }; fetches orders without checking user active status.
    const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM users WHERE id = ${args.userId} AND active = TRUE`) } }; fetches user data only, not orders.
  3. Final Answer:

    const resolvers = { Query: { userOrders: (_, args) => db.query(`SELECT * FROM orders WHERE user_id = ${args.userId} AND EXISTS (SELECT 1 FROM users WHERE id = ${args.userId} AND active = TRUE)`) } }; -> Option C
  4. Quick Check:

    Single query with EXISTS enforces condition efficiently [OK]
Hint: Use EXISTS in SQL to combine condition and fetch in one query [OK]
Common Mistakes:
  • Running multiple queries instead of one
  • Not checking user active status before fetching orders
  • Returning user data instead of orders
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard