GraphQL

Queries in GraphQL

18 min Lesson 3 of 35

Understanding GraphQL Queries

Queries are how you fetch data in GraphQL. They allow clients to specify exactly what data they need, and the server responds with only that data. This is one of GraphQL's most powerful features - no more over-fetching or under-fetching.

Key Concept: A GraphQL query is a string that describes the data you want to retrieve. It looks similar to the shape of the data you get back.

Basic Query Syntax

Here's a simple query to fetch a user's name and email:

# Query query { user(id: "123") { name email } } # Response { "data": { "user": { "name": "Ahmed Hassan", "email": "ahmed@example.com" } } }

The response matches the structure of the query - this predictability makes GraphQL easy to work with.

Nested Queries

One of GraphQL's strengths is the ability to fetch related data in a single query:

query { user(id: "123") { name email posts { title publishedAt comments { text author { name } } } } }

This single query retrieves the user, their posts, comments on those posts, and comment authors - all in one request!

Best Practice: Fetch all the data you need in a single query rather than making multiple sequential requests. This reduces network overhead and improves performance.

Query Arguments

You can pass arguments to fields to filter, sort, or paginate results:

query { users( limit: 10 offset: 0 orderBy: "createdAt" role: ADMIN ) { id name email } posts( status: PUBLISHED tags: ["graphql", "api"] minViews: 100 ) { title views tags } }

Field Aliases

Aliases allow you to rename the result of a field to avoid naming conflicts when querying the same field multiple times:

query { # Query the same field with different arguments recentPosts: posts(limit: 5, orderBy: "createdAt") { title createdAt } popularPosts: posts(limit: 5, orderBy: "views") { title views } # Rename fields for clarity authorName: name authorEmail: email }
# Response { "data": { "recentPosts": [...], "popularPosts": [...], "authorName": "Ahmed Hassan", "authorEmail": "ahmed@example.com" } }

Fragments

Fragments let you reuse common pieces of queries. They're like functions for your queries:

# Define a fragment fragment UserInfo on User { id name email avatar } # Use the fragment query { user(id: "123") { ...UserInfo posts { title author { ...UserInfo } } } allUsers { ...UserInfo } }

Inline Fragments are useful for querying fields on specific types when dealing with interfaces or unions:

query { search(query: "graphql") { ... on User { name email } ... on Post { title content } ... on Comment { text createdAt } } }
Tip: Fragments make your queries more maintainable and reduce duplication. They're especially valuable in large applications with complex data requirements.

Query Variables

Instead of hardcoding values in queries, use variables for dynamic values. This is essential for real-world applications:

# Define the query with variables query GetUser($userId: ID!, $includeEmail: Boolean!) { user(id: $userId) { name email @include(if: $includeEmail) posts { title } } } # Pass variables separately { "userId": "123", "includeEmail": true }

Variable syntax:

  • $variableName - Variable declaration starts with $
  • ID! - Type annotation (non-nullable ID)
  • Variables are passed in a separate JSON object
Security Benefit: Variables prevent query injection attacks and allow GraphQL servers to cache parsed queries more effectively.

Default Variable Values

You can provide default values for variables:

query GetPosts( $limit: Int = 10 $offset: Int = 0 $status: PostStatus = PUBLISHED ) { posts(limit: $limit, offset: $offset, status: $status) { title status createdAt } } # If you don't pass variables, defaults are used {}

Directives

Directives modify the execution of queries. GraphQL includes two built-in directives:

@include(if: Boolean) - Include field if the condition is true:

query GetUser($userId: ID!, $includeEmail: Boolean!) { user(id: $userId) { name email @include(if: $includeEmail) posts { title } } } # Variables { "userId": "123", "includeEmail": false # email field will not be included }

@skip(if: Boolean) - Skip field if the condition is true:

query GetUser($userId: ID!, $skipPosts: Boolean!) { user(id: $userId) { name email posts @skip(if: $skipPosts) { title content } } } # Variables { "userId": "123", "skipPosts": true # posts field will be skipped }
Note: @include and @skip cannot be used on the same field. Choose the one that makes your logic clearer.

Operation Names

It's a best practice to name your queries for debugging and logging:

# Named query query GetUserProfile($userId: ID!) { user(id: $userId) { name email bio } } # Anonymous query (not recommended for production) query { user(id: "123") { name } }

Introspection

GraphQL's introspection system allows you to query the schema itself. This powers tools like GraphiQL and Apollo Studio:

# Get all types in the schema { __schema { types { name kind } } } # Get details about a specific type { __type(name: "User") { name fields { name type { name kind } } } } # Get all query operations { __schema { queryType { fields { name description args { name type { name } } } } } }
Security Tip: In production, you may want to disable introspection to prevent exposing your complete schema to potential attackers.

Query Best Practices

  • Always name your queries - Makes debugging and logging easier
  • Use fragments - Reduce duplication and improve maintainability
  • Use variables - Never interpolate values into query strings
  • Request only needed fields - Don't request data you won't use
  • Paginate large lists - Use limit/offset or cursor-based pagination
  • Consider query depth - Deeply nested queries can impact performance
Exercise: Write a GraphQL query for an e-commerce application with the following requirements:
  • Fetch 20 products with pagination support
  • Filter by category and price range
  • Include product name, price, description, and images
  • For each product, include the seller's name and rating
  • Use query variables for dynamic filtering
  • Add an option to conditionally include product reviews using a directive
  • Use a fragment for common product fields

Summary

In this lesson, you've learned how to write GraphQL queries, from basic field selection to advanced features like fragments, variables, and directives. Queries are the primary way clients interact with your GraphQL API, and mastering them is essential for building efficient applications.