Home

Awesome

GraphQL for Delphi

License

Simple implementation for GraphQL, a query language for APIs created by Facebook.

GraphQL is a query language for your API and a server-side runtime for executing queries using a type system you define for your data. GraphQL isn't tied to any specific database or storage engine and is instead backed by your existing code and data.

See more complete documentation at https://graphql.org/.

Table of Contents

<!-- /code_chunk_output -->

Features

GraphQL for Delphi supports only a basic part of the GraphQL specifications:

Other parts like variables, schema and validation are under development.

GraphQL tree navigation

The more basic feature of GraphQL for Delphi is the possibility to explore the GraphQL query.

With a code like this you can build the GraphQL tree:

  LBuilder := TGraphQLBuilder.Create(SourceMemo.Text);
  try
    // This will create the tree
    LGraphQL := LBuilder.Build;
  finally
    LBuilder.Free;
  end;

Then you will have a struture like this:

IGraphQL
├── Name
└── Fields
    ├── IGraphQLField (first entity)
    │   ├── Name
    │   ├── Alias
    │   ├── Arguments / Parameters
    │   │   ├─ IGraphQLArgument 
    │   │   └─ IGraphQLArgument 
    │   │
    │   └── IGraphQLValue (IGraphQLNull | IGraphQLObject)
    │       └─ Fields
    │          ├─ ...
    │          ├─ ...
    │
    └── IGraphQLField (second entity)
        ├── Name
        ├── Alias
        ├── ...

You can see the demo to have an idea of the capabilities of this library.

Query your API with GraphQL

First of all you need an API to query. At this moment GraphQL for Delphi supports classes or simple procedures and functions. In either case you have to tell the library how to call your API.

Basic API

If you have a simple API made of classic functions like this:

function RollDice(NumDices, NumSides: Integer): Integer;

function ReverseString(const Value: string): string;

function StarWarsHero(const Id: string): TStarWarsHero;

Then you need to register your API in this way:

  FQuery := TGraphQLQuery.Create;

  FQuery.RegisterFunction('rollDice',
    function (AParams: TGraphQLParams) :TValue
    begin
      Result := RollDice(AParams.Get('numDice').AsInteger, AParams.Get('numSides').AsInteger);
    end
  );

  FQuery.RegisterFunction('reverseString',
    function (AParams: TGraphQLParams) :TValue
    begin
      Result := ReverseString(AParams.Get('value').AsString);
    end
  );

  FQuery.RegisterFunction('hero',
    function (AParams: TGraphQLParams) :TValue
    begin
      Result := StarWarsHero(AParams.Get('id').AsString);
    end
  );

Eventually you can query your API:


json := FQuery.Run(MyQuery);

Run methods from a class using RTTI

If you have a class you need to tell the library:

For example if you have a class like this:

  TTestApi = class(TObject)
  private
    FCounter: Integer;
  public
    [GraphQLEntity]
    function Sum(a, b: Integer): Integer;

    [GraphQLEntity('mainHero')]
    function MainHero: TStarWarsHero;

  end;

You need to add the GraphQLEntity to every method queryable by GraphQL and register the class:

  FQuery := TGraphQLQuery.Create;
  FQuery.RegisterResolver(TGraphQLRttiResolver.Create(TTestApi, True));

The RegisterResolver method can add a resolver (any class that implements IGraphQLResolver) to the GraphQL engine. A resolver is a simple object that explains to GraphQL how to get the data from the API. You can build your own resolvers or use the resolvers build-in with the library.

The TGraphQLRttiResolver is capable of running methods from a class using the RTTI.

Then you can query your API:


json := FQuery.Run(MyQuery);

A simple query:

How to use GraphQL aliases:

How to call simple functions:

A more complex example:

Use API from a ReST server

If you need to use GraphQL to queries a ReST API you can see the ProxyDemo. This simple project creates a basic HTTP server that responds to GraphQL query and uses a remote ReST API (https://jsonplaceholder.typicode.com/) as a data source.

The project uses a TGraphQLReSTResolver to map the GraphQL fields to the ReST API in this way:

  FQuery := TGraphQLQuery.Create;

  LResolver := TGraphQLReSTResolver.Create;

  // Basic entities
  LResolver.MapEntity('posts', 'https://jsonplaceholder.typicode.com/posts/{id}');
  LResolver.MapEntity('comments', 'https://jsonplaceholder.typicode.com/comments/{id}');
  LResolver.MapEntity('albums', 'https://jsonplaceholder.typicode.com/albums/{id}');
  LResolver.MapEntity('todos', 'https://jsonplaceholder.typicode.com/todos/{id}');
  LResolver.MapEntity('users', 'https://jsonplaceholder.typicode.com/users/{id}');

  // Entity details
  LResolver.MapEntity('users/posts', 'https://jsonplaceholder.typicode.com/users/{parentId}/posts');
  LResolver.MapEntity('users/comments', 'https://jsonplaceholder.typicode.com/users/{parentId}/comments');
  LResolver.MapEntity('users/todos', 'https://jsonplaceholder.typicode.com/users/{parentId}/todos');

  FQuery.RegisterResolver(LResolver);

When you define an entity you can specify the name of the id property (default "id"). The id propery will be used if your entity as a detail. For example you have a resource like:

https://jsonplaceholder.typicode.com/users/1
{
  "userId": 1,
  "name": "Luca"
}

and a detail resource like:

https://jsonplaceholder.typicode.com/users/1/todos
[{
  "id": 1,
  "userId": 1,
  "title": "Something to do"
},{
  "id": 2,
  "userId": 1,
  "title": "Another thing to do"
}]

You must define the entities in this way:

  LResolver.MapEntity('users', 'https://jsonplaceholder.typicode.com/users/{id}', 'userId');
  LResolver.MapEntity('users/todos', 'https://jsonplaceholder.typicode.com/users/{parentId}/todos');

Then, when you run the query with FQuery.Run(...), the resolver can call the right ReST API.

Todo