Skip to content

Gatsby GraphQL — Querying Data with GraphQL in Gatsby

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Gatsby Graphql. We cover key concepts, practical examples, and best practices to help you master this topic.

Gatsby GraphQL provides a unified query layer for sourcing, transforming, and querying data from all connected plugins and sources.

What You'll Learn

By the end of this tutorial, you'll understand Gatsby's GraphQL data layer, how to write page queries and static queries, use GraphQL fragments, filter and sort data, and optimize query performance.

Why It Matters

Gatsby's GraphQL layer is the central nervous system of your site. Every piece of content — from blog posts to images to CMS data — flows through it. Efficient querying directly impacts build speed and site performance.

Real-World Use

A multi-language documentation site queries content from 2000 markdown files, filters by language and category, orders by version, and retrieves featured images. A single GraphQL query replaces what would require multiple API calls.

Query Architecture

graph TD
    A[Gatsby GraphQL
Schema] --> B[Node Types] A --> C[Connections] B --> D[MarkdownRemark] B --> E[ContentfulProduct] B --> F[WordPressPost] B --> G[ImageSharp] C --> H[allMarkdownRemark] C --> I[allContentfulProduct] C --> J[allWordPressPost] D --> K[Page Queries] E --> K F --> K G --> L[Static Queries] K --> M[Component Props] L --> N[useStaticQuery Hook] style A fill:#4a90d9,color:#fff style K fill:#e67e22,color:#fff style L fill:#27ae60,color:#fff

Page Queries

// src/templates/blog-post.js
import React from 'react';
import { graphql } from 'gatsby';

export default function BlogPost({ data }) {
    const { markdownRemark } = data;
    const { frontmatter, html, fields } = markdownRemark;

    return (
        <article>
            <h1>{frontmatter.title}</h1>
            <div className="meta">
                <span>{frontmatter.date}</span>
                <span>{fields.readingTime.readingTime} min read</span>
                <div className="tags">
                    {frontmatter.tags?.map(tag => (
                        <span key={tag} className="tag">{tag}</span>
                    ))}
                </div>
            </div>
            <div dangerouslySetInnerHTML={{ __html: html }} />
        </article>
    );
}

// Page query (runs at build time, tied to page)
export const query = graphql`
    query BlogPostBySlug($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            html
            frontmatter {
                title
                date(formatString: "MMMM DD, YYYY")
                tags
            }
            fields {
                readingTime {
                    readingTime
                }
            }
        }
    }
`;

Static Queries

// src/components/header.js
import React from 'react';
import { useStaticQuery, graphql, Link } from 'gatsby';

export default function Header() {
    // Static query (available in any component, no context needed)
    const data = useStaticQuery(graphql`
        query SiteMetadata {
            site {
                siteMetadata {
                    title
                    description
                }
            }
            allNavigationYaml {
                nodes {
                    name
                    path
                }
            }
        }
    `);

    const { title } = data.site.siteMetadata;
    const navItems = data.allNavigationYaml.nodes;

    return (
        <header>
            <Link to="/" className="logo">{title}</Link>
            <nav>
                {navItems.map(item => (
                    <Link key={item.path} to={item.path}>
                        {item.name}
                    </Link>
                ))}
            </nav>
        </header>
    );
}

Advanced Filtering and Sorting

// src/pages/blog.js — Advanced query patterns
export const query = graphql`
    query BlogList(
        $category: String = "all"
        $tag: String = ""
        $sortField: String = "date"
        $sortOrder: SortOrderEnum = DESC
        $limit: Int = 10
        $skip: Int = 0
    ) {
        # Filter by category
        featuredPost: markdownRemark(
            frontmatter: {
                featured: { eq: true }
                category: { eq: $category }
            }
        ) {
            frontmatter {
                title
                excerpt
                slug
            }
        }

        # Paginated, sorted posts with tag filter
        allMarkdownRemark(
            filter: {
                frontmatter: {
                    category: { eq: $category }
                    tags: { in: [$tag] }
                    draft: { ne: true }
                }
            }
            sort: { frontmatter: { date: $sortOrder } }
            limit: $limit
            skip: $skip
        ) {
            totalCount
            nodes {
                id
                frontmatter {
                    title
                    date(formatString: "MMMM DD, YYYY")
                    slug
                    tags
                }
                excerpt(pruneLength: 200)
                fields {
                    slug
                }
            }
        }
    }
`;

GraphQL Fragments

// fragments/post-fields.js
// Reusable fragment for post metadata
import { graphql } from 'gatsby';

export const PostFields = graphql`
    fragment PostFields on MarkdownRemark {
        id
        frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
            slug
            tags
            author
        }
        fields {
            slug
            readingTime {
                readingTime
            }
        }
        excerpt(pruneLength: 200)
    }
`;

// Usage in a page query
export const query = graphql`
    query BlogList {
        allMarkdownRemark(
            sort: { frontmatter: { date: DESC } }
            limit: 10
        ) {
            nodes {
                ...PostFields
                # Fragment includes all fields above
                # Add page-specific fields here
                frontmatter {
                    featuredImage {
                        publicURL
                    }
                }
            }
        }
    }
`;

Common Mistakes

  1. Not using aliases for ambiguous queries. When querying the same type twice, use aliases (featuredPost, latestPost) to avoid conflicts.
  2. Over-fetching nested data. Request only the fields you render. Each extra field increases build time and memory usage.
  3. Forgetting to use formatString for dates. Raw date strings are unformatted. Always specify the output format you need.
  4. Using page queries in non-page components. Page queries only work in top-level page files. Use useStaticQuery for components.
  5. Not filtering out draft content. Drafts should be filtered with draft: { ne: true }. Otherwise they're included in production builds.

Practice Questions

  1. What is the difference between a page query and a static query in Gatsby?
  2. How do you pass variables to a Gatsby page query?
  3. What are GraphQL fragments and why are they useful?
  4. How do you filter and sort data in Gatsby GraphQL queries?
  5. Can you use the same GraphQL query in multiple components?

Challenge: Build a blog with advanced GraphQL queries: implement pagination using skip/limit, filter by category and tags, use fragments for consistent post fields, and display reading time from computed fields.

FAQ

Can I use Gatsby without GraphQL?

No. Gatsby's data layer is built on GraphQL. You can use createPages with raw data in gatsby-node.js, but GraphQL is the standard approach.

How does Gatsby generate its GraphQL schema?

Gatsby infers the schema from sourced data. Source plugins create node types with fields. You can also define explicit schema with createSchemaCustomization.

Can I query external APIs directly from pages?

It's not recommended. All data should go through Gatsby's GraphQL layer for caching, transformation, and consistency. Use source plugins instead.

How do I debug GraphQL queries in Gatsby?

Use the GraphiQL explorer at http://localhost:8000/___graphql during development. It auto-completes and documents the entire schema.

Does GraphQL query complexity affect build time?

Yes. Complex queries with deep nesting, large result sets, and many transformations slow the build. Optimize by requesting only needed fields and using pagination.

Mini Project

Create a Gatsby site with advanced GraphQL queries: 10+ markdown posts with categories and tags, a filtered blog listing page, pagination with skip/limit, reusable GraphQL fragments, and a featured post section using query aliases.

What's Next

You've mastered Gatsby GraphQL. Now explore an alternative SSG approach with Hugo — the world's fastest static site generator built in Go.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro