Search Results for

    Show / Hide Table of Contents

    GraphQL Schema

    Your GraphQL server uses a schema to describe the shape of your available data. This schema defines a hierarchy of types with fields that are populated from your back-end data stores. The schema also specifies exactly which queries and mutations are available for clients to execute.

    This article describes the fundamental building blocks of a schema and how to create a schema using GraphQL for Delphi.

    The schema definition language

    The GraphQL specification defines a human-readable schema definition language (or SDL) that you use to define your schema and store it as a string.

    Here's a short example schema that defines two object types: Book and Author:

        type Book {
          id: ID
          name: String
          author: Author
        }
    
        type Author {
          id: ID
          name: String
          books: [Book]
        }
    

    A schema defines a collection of types and the relationships between those types. In the example schema above, a Book can have an associated author, and an Author can have a list of books.

    Because these relationships are defined in a unified schema, client developers can see exactly what data is available and then request a specific subset of that data with a single optimized query.

    Note that the schema is not responsible for defining where data comes from or how it's stored. It is entirely implementation-agnostic.

    Field definitions

    Most of the schema types you define have one or more fields:

        # This Book type has two fields: name and author    
        type Book {    
          id: ID         # record ID    
          name: String   # returns a String    
          author: Author # returns an Author    
        }
    

    A field can return a list containing items of a particular type. You indicate list fields with square brackets [], like so:

        type Author {
          id: ID
          name: String
          books: [Book] # A list of Books
        }
    

    By default, it's valid for any field in your schema to return null instead of its specified type. You can require that a particular field doesn't return null with an exclamation mark !, like so:

        type Author {
          id: ID!       # Can't return null
          name: String! # Can't return null
          books: [Book]
        }
    

    These fields are non-nullable. If your server attempts to return null for a non-nullable field, an error is thrown.

    With a list field, an exclamation mark ! can appear in any combination of two locations:

        type Author {
          id: ID!         # Can't return null
          name: String!   # Can't return null
          books: [Book!]! # This list can't be null AND its list *items* can't be null
        }
    
    • If ! appears inside the square brackets, the returned list can't include items that are null.
    • If ! appears outside the square brackets, the list itself can't be null.
    • In any case, it's valid for a list field to return an empty list.

    The Query type

    The Query type is a special object type that defines all of the top-level entry points for queries that clients execute against your server.

    Each field of the Query type defines the name and return type of a different entry point. The Query type for our example schema might resemble the following:

        type Query {
          author(id: ID!): Author 
          book(id: ID!): Book
          books: [Book!]!
          authors: [Author!]!
        }
    

    This Query type defines four fields. author and book fields return a record of the corresponding type by its id, books and authors fields return a list of the corresponding type.

    With a REST-based API, this would probably be returned by different endpoints (e.g., /api/books, /api/authors, /api/books/:id, /api/authors/:id). The flexibility of GraphQL enables clients to query both resources with a single request.

    Structuring a query

    Based on our example schema so far, a client could execute the following query, which requests both a list of all book names and a list of all author names:

        query GetBooksAndAuthors {
          books {
            name
          }
    
          authors {
            name
          }
        }
    

    Our server would then respond to the query with results that match the query's structure, like so:

        {
          "data": {
            "books": [
              {
                "name": "War and Peace"
              },
              ...
            ],
            "authors": [
              {
                "name": "Leo Tolstoy"
              },
              ...
            ]
          }
        }
    

    The Mutation type

    The Mutation type is similar in structure and purpose to the Query type. Whereas the Query type defines entry points for read operations, the Mutation type defines entry points for write operations.

    Each field of the Mutation type defines the signature and return type of a different entry point. The Mutation type for our example schema might resemble the following:

        type Mutation {
          createAuthor(name: String!): Author!
          createBook(name: String!, authorId: ID!): Book!
        }  
    

    This Mutation type defines available mutations, createAuthor and createBook. createBook mutation accepts a single argument (name) and returns a newly created Author object. createBook mutation accepts two arguments (name and authorId) and returns a newly created Book object.

    Structuring a mutation

    Like queries, mutations match the structure of your schema's type definitions. The following mutation creates a new Author and requests certain fields of the created object as a return value:

        mutation CreateBook {
          createAuthor(name: "Leo Tolstoy") {
            id
            name
          }
        }
    

    As with queries, our server would respond to this mutation with a result that matches the mutation's structure, like so:

        {
           "data": {
             "createAuthor": {
               "id": 1,
               "name": "Leo Tolstoy",
             }
           }
         }
     

    Resolvers

    GraphQL Server needs to know how to populate data for every field in your schema so that it can respond to requests for that data. To accomplish this, it uses resolvers.

    A resolver is a function that's responsible for populating the data for a single field in your schema. It can populate that data in any way you define, such as by fetching data from a back-end database or a third-party API.

    Defining Resolvers

    Let's say our server defines the schema described above. We want to define resolvers for Query, Mutation, Author, Book and their fields.

    GraphQL for Delphi library can map GraphQL schema definition onto Delphi class structure. Those resolvers definitions look like this:

    type
      TBook = class;
    
      TAuthor = class
      private
        FId: Integer;
        FName: string;
        function GetBooks: TArray<TBook>;
      public
        constructor Create(AName: string);
        property Books: TArray<TBook> read GetBooks;
    
        property Id: Integer read FId;
        property Name: string read FName;
      end;
    
      TBook = class
      private
        FId: Integer;
        FName: string;
        FAuthorId: Integer;
        function GetAuthor: TAuthor;
      public
        constructor Create(AName: string; AAuthorID: Integer);
        property Id: Integer read FId;
        property Name: string read FName;
        property Author: TAuthor read GetAuthor;
      end;
    
      TMutation = class
      public
        function CreateAuthor(Name: string): TAuthor;
        function CreateBook(Name: string; AuthorID: Integer): TBook;
      end;
    
      TQuery = class
      public
        function Books: TArray<TBook>;
        function Authors: TArray<TAuthor>;
        function Book(id: Integer): TBook;
        function Author(id: Integer): TAuthor;
      end;
    
    In This Article
    Back to top GraphQL for Delphi v1.4
    © 2021 - 2024 tmssoftware.com