Series: GraphQL with .NET & React, From Zero to Production
Welcome to this comprehensive GraphQL tutorial series! By the end of these 5 articles, you'll have a solid understanding of GraphQL and hands-on experience building a full-stack application with React and .NET.
Get your code here: Github
This is the series that takes you from GraphQL basics to advanced patterns.
Part 1: Foundations & Library Backend
Part 3: Mutations (Create, Update & Delete)
Part 4: Real-Time Data with web sockets
Part 5: Advanced Patterns (Pagination, Errors, JWT & Testing)
Part 2: Query (Arguments, Aliases & Fragments)
Welcome back! In Part 1, we set up our Library Management System, learned about GraphQL, and wrote our first query. Now it's time to master the full power of GraphQL queries.
Where We Left Off
We have:
A working .NET backend with HotChocolate serving GraphQL
A React frontend with Apollo Client consuming it
Basic queries fetching books with nested author data
Now let's learn the advanced query features that make GraphQL truly powerful.
Understanding Query Structure
Before diving into advanced features, let's fully understand query anatomy:
query GetBooksQuery {
books {
nodes {
title
}}}| Part | What it is | Required? |
|---|---|---|
| query | Operation type | Optional for queries (default) |
| GetBooksQuery | Operation name | Optional but recommended |
| books | Root field | Required |
| { nodes { title } } | Selection set | Required |
Query Arguments
Arguments allow you to pass parameters to your queries - like function parameters in programming.
What Are Arguments?
Without arguments, queries return ALL data. Arguments let you:
Filter: "Give me book #5"
Limit: "Give me only 10 books"
Search: "Give me books containing 'war'"
Basic Argument Syntax
query {
bookById(id: 1) {
title
description
}
}Syntax breakdown:
bookById- The field name(id: 1)- Argument in parenthesesid- Argument name1- Argument value
How Arguments Work (Backend)
In HotChocolate, arguments become method parameters:
// The 'id' parameter becomes a GraphQL argument automatically
public async Task<Book?> GetBookById(LibraryDbContext context, int id)
{
return await context.Books.FirstOrDefaultAsync(b => b.Id == id);
}Method name
GetBookById→ GraphQL fieldbookByIdParameter
int id→ GraphQL argumentid: Int!Return type
Book?→ GraphQL return typeBook
Multiple Arguments
query {
filteredBooks(
filter: { genre: "Fantasy", minYear: 1950 }
sort: { field: PUBLISHED_YEAR, direction: DESC }
skip: 0
take: 10
) {
title
publishedYear
}
}Variables - Making Queries Dynamic
Hardcoding values like id: 1 isn't practical. What if the ID comes from user input?
The Problem
# Hardcoded - not reusable
query {
bookById(id: 1) { title }
}The Solution: Variables
Variables separate the query structure from the values:
# Dynamic - pass any ID
query GetBook($bookId: Int!) {
bookById(id: $bookId) {
title
description
}
}Syntax breakdown:
$bookId- Variable name (starts with $)Int!- Variable type (must match argument type): $bookId- Using the variable as argument value
Variables Are Passed Separately
When executing the query, variables are sent as JSON:
{
"bookId": 1
}Why this separation?
Same query can be reused with different values
Better security (prevents injection attacks)
Query can be cached regardless of variables
React Implementation with Variables
const GET_BOOK = gql`
query GetBook($bookId: Int!) {
bookById(id: $bookId) {
title
description
}
}
`;
function BookDetail({ bookId }: { bookId: number }) {
// Pass variables as an option to useQuery
const { loading, data } = useQuery(GET_BOOK, {
variables: { bookId }, // This becomes the JSON above
});
if (loading) return <p>Loading...</p>;
return <h1>{data.bookById.title}</h1>;
}Default Values for Variables
Make variables optional with defaults:
query GetBooks($first: Int = 10, $genre: String) {
filteredBooks(take: $first, filter: { genre: $genre }) {
title
}
}Now you can call:
{ }- Uses defaults (first: 10, genre: null){ "first": 5 }- Override first, genre still null{ "genre": "Fantasy" }- Use default first, filter by genre
Aliases - Query Same Field Multiple Times
The Problem
What if you need two different books?
# ERROR: Can't have duplicate field names
query {
bookById(id: 1) { title }
bookById(id: 2) { title }
}GraphQL doesn't allow duplicate field names in the same selection set!
The Solution: Aliases
Aliases give fields custom names in the response:
# Works! Each field has a unique name
query {
firstBook: bookById(id: 1) { title }
secondBook: bookById(id: 2) { title }
}Syntax: aliasName: fieldName(args)
Response
{
"data": {
"firstBook": { "title": "1984" },
"secondBook": { "title": "Animal Farm" }
}
}Practical Use Case: Comparison
Compare two authors' statistics:
query CompareAuthors {
orwell: authorById(id: 1) {
name
bookCount
averageBookRating
}
tolkien: authorById(id: 5) {
name
bookCount
averageBookRating
}
}You Can Alias Any Field
Even rename nested fields:
query {
bookById(id: 1) {
bookTitle: title # Rename 'title' to 'bookTitle'
writer: author { # Rename 'author' to 'writer'
authorName: name # Rename 'name' to 'authorName'
}
}
}Fragments - Reusable Field Sets
The Problem: Repetition
When you need the same fields in multiple places:
query {
firstBook: bookById(id: 1) {
id
title
description
publishedYear
price
author { name }
}
secondBook: bookById(id: 2) {
id
title
description
publishedYear
price
author { name }
}
}
That's a lot of copy-paste! What if you need to add a field? You'd have to change it everywhere.
The Solution: Fragments
Fragments are reusable pieces of a query:
# Define the fragment once
fragment BookFields on Book {
id
title
description
publishedYear
price
author { name }
}
# Use it with the spread operator (...)
query {
firstBook: bookById(id: 1) {
...BookFields
}
secondBook: bookById(id: 2) {
...BookFields
}
}
Syntax breakdown:
fragment- Keyword to define a fragmentBookFields- Fragment name (you choose this)on Book- The type this fragment applies to...BookFields- Spread the fragment's fields here
Fragment Rules
Must specify a type (
on Book) - Fragment fields must exist on that typeCan be nested - Fragments can use other fragments
Can add extra fields - Mix fragments with additional fields
query {
bookById(id: 1) {
...BookFields
reviews { # Extra field not in fragment
rating
}
}
}Using Fragments in React
// Define fragments as constants for reuse
export const BOOK_FRAGMENT = gql`
fragment BookDetails on Book {
id
title
description
publishedYear
price
}
`;
export const AUTHOR_FRAGMENT = gql`
fragment AuthorDetails on Author {
id
name
biography
country
}
`;
// Use fragments in queries
export const GET_BOOK = gql`
${BOOK_FRAGMENT}
${AUTHOR_FRAGMENT}
query GetBook($id: Int!) {
bookById(id: $id) {
...BookDetails
author {
...AuthorDetails
}
}
}
`;

Join the conversation! Your thoughts help the community grow.