Skip to content

Queries and mutations

Stanislav Silin edited this page May 23, 2023 · 2 revisions

Let's suppose that schema.graphql file contains the following:

schema {
  query: Queries
  mutation: Mutation
}

type Queries {
  me: User!
  user(id: Int!): User
}

type Mutation {
  addUser(firstName: String!, lastName: String!): User!
  addUserProfileImage(userId: Int! file: Upload!): Int!
}

type User {
  id: Int!
  firstName: String!
  lastName: String!
  role: Role!
}

type Role {
  id: Int!
  name: String!
}

and we want to execute the query like that:

query { me { id firstName lastName } }

GraphQL lambda syntax

Here how we can achieve it with ZeroQL "lambda" syntax:

var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:10000/graphql");

var client = new TestServerGraphQLClient(httpClient);

var response = await client.Query(o => o.Me(o => new { o.Id, o.FirstName, o.LastName }));

Console.WriteLine($"GraphQL: {response.Query}"); // GraphQL: query { me { id firstName lastName } }
Console.WriteLine($"{response.Data.Id}: {response.Data.FirstName} {response.Data.LastName}"); // 1: Jon Smith

You can pass arguments inside lambda if needed:

var userId = 1;
var response = await client.Query(o => o.User(userId, o => new User(o.Id, o.FirstName, o.LastName)));

Console.WriteLine($"GraphQL: {response.Query}"); // GraphQL: query ($id: Int!) { user(id: $id) { id firstName lastName } }
Console.WriteLine($"{response.Data.Id}: {response.Data.FirstName} {response.Data.LastName}"); // 1: Jon Smith

There is a limitation for lambda syntax. The variable should be a local variable or a parameter of the function. Otherwise, it will not be included in the lambda closure. As a result, ZeroQL would not be able to get a value.

Here is an example of the function parameter:

public Task<User> GetUser(int userId)
{
    var response = await client.Query(o => o.User(userId, o => new User(o.Id, o.FirstName, o.LastName)));
    return response.Data;
}

To be clear, you don't need actively account for it. ZeroQL will analyze and report errors if something is wrong.

For example the next sample will not work:

public int UserId { get; set; }

public Task<User> GetUser()
{
    var response = await client.Query(o => o.User(UserId, o => new User(o.Id, o.FirstName, o.LastName))); // ZeroQL will report a compilation error here
    return response.Data;
}

Also, there is a way to avoid lambda closure:

var variables = new { Id = 1 };
var response = await client.Query(variables, static (i, o) => o.User(i.Id, o => new User(o.Id, o.FirstName, o.LastName)));

You can fetch attached fields:

var variables = new { Id = 1 };
var response = await client.Query(
    variables,
    static (i, o) => o
        .User(i.Id,
            o => new
            {
                o.Id,
                o.FirstName,
                o.LastName,
                Role = o.Role(role => role.Name)
            }));

Console.WriteLine($"GraphQL: {response.Query}"); // GraphQL: query GetUserWithRole($id: Int!) { user(id: $id) { id firstName lastName role { name }  } }
Console.WriteLine($"{response.Data.Id}: {response.Data.FirstName} {response.Data.LastName}, Role: {response.Data.Role}"); // 1: Jon Smith, Role: Admin

GraphQL request syntax

In more complex queries, the "lambda" syntax may look verbose, and extracting requests into a separate entity would be nice. Now it is possible to do it via the "request" syntax. Here is an example:

// define a request
public record GetUserQuery(int Id) : GraphQL<Queries, UserModel?>
{
    public override UserModel? Execute(Queries query) 
        => query.User(Id, o => new UserModel(o.Id, o.FirstName, o.LastName));
}

// execute a request
var response = await client.Execute(new GetUserQuery(variables.FriendId));

Console.WriteLine(response.Query); // query GetUserQuery($id: Int!) { user(id: $id) { id firstName lastName } }
Console.WriteLine(response.Data); // UserModel { Id = 2, FirstName = Ben, LastName = Smith }

You need to create a record from the base record GraphQL<TOperationType, TResult>. Where the TOperationType is a root query type(Query, Mutation) that associated with the GraphQLClient<TQuery, TMutataion> instance.