Skip to main content

GraphQL

Introduction

This project is an example implementation of Clean Architecture principles using GraphQL. The goal is to demonstrate how Clean Architecture can be used to create a scalable and maintainable application with a flexible and efficient GraphQL API.

Clone Solution

  1. Clone via GitHub CLI

    mkdir GraphQL
    cd GraphQL
    gh repo clone danielmackay/dotnet-clean-architecture-graphql
    cd dotnet-clean-architecture-graphql

Explore Solution

  1. Open in Rider

    rider .
  2. Explore the solution structure

    • GraphQL/program.cs: The entry point for the GraphQL application.
      • AddTypes() - source generated
    • GraphQL/Types
      • Explicit vs implicit binding
      • Ignoring fields
      • Additional fields
    • GraphQL/Properties/ModuleInfo.cs: Contains the module information for the GraphQL application.
    • GraphQL/Queries: Contains the GraphQL queries.
      • Filters
    • GraphQL/Mutations: Contains the infrastructure code for the GraphQL application.
    • GraphQL/Subscriptions: Contains the Web API project that serves the GraphQL API.

Nitro Playground

  1. Run the solution

    aspire run
  2. Browse Schema and explore:

    • Root Types
    • Objects
    • Input Objects

Queries

  1. Navigate to 'Operation'

  2. Get all todo items

    query getAll {
    todoItems {
    nodes {
    id
    title
    }
    }
    }
    info

    The use of nodes and edges comes from the Relay specification, which is a collection of common patterns in GraphQL APIs. The Relay specification was originally created by Facebook / Meta.

  3. Wildcard search

    query wildCardSearch {
    todoItems(where: { title: { contains: "list" } }) {
    nodes {
    id
    title
    }
    }
    }
  4. Ordered list

    query orderedList {
    todoItems(order: [{ title: ASC }]) {
    nodes {
    id
    title
    }
    }
    }
  5. Projections

    query projections {
    todoLists {
    nodes {
    id
    title
    }
    }
    }

    Show underlying SQL Query - pulling only what we need

    query projections {
    todoLists {
    nodes {
    id
    title
    items {
    title
    }
    }
    }
    }

    Show underlying SQL Query - now joining the todo items table

Mutations

GraphQL's query types are very powerful, but they are not the only way to interact with the API. Mutations allow you to create, update, and delete data in the API.

  1. Create a new todo item

    mutation createTodoItem {
    createTodoItem (input: {
    listId: 1
    title: "hello from .NET Superpowers"
    }){
    todoItem {
    id
    title
    note
    priority
    reminder
    done
    }
    }
    }
    info

    Notice how we are issuing a mutation and then immediately querying the newly created item. This is a common pattern in GraphQL APIs, allowing you to get the latest state of the data after a mutation.

Subscriptions

Subscriptions allow you to receive real-time updates from the API. This is useful for scenarios where you want to be notified of changes to the data without having to poll the API.

  1. Subscribe to todo item updates

    subscription todoItemCreatedSubscription{
    onTodoItemCreated{
    todoItemId
    }
    }
  2. Duplicate Nitro Playground

  3. Trigger subscription by creating a new todo item

    mutation createTodoItem {
    createTodoItem (input: {
    listId: 1
    title: "subscription test from .NET Superpowers"
    }){
    todoItem {
    id
    title
    note
    priority
    reminder
    done
    }
    }
    }
  4. Observe the subscription in the first Nitro Playground

note

In the demo solution there is also a Blazor UI showing how to use Strawberry Shake to generate a client for the GraphQL API. This allows you to easily interact with the API from a Blazor application.

I won't have time to cover this in the session, but you can explore it in the solution.