Building and consuming GraphQL in your .NET application

GraphQL is a single API face for different clients like mobile, desktop apps, tablets… It’s built by Facebook.

It’s not a magical library that solves all our Web API issues but it helps us to provide our clients a very flexible Web API.

I wish we would provide GraphQL the EF DbContext and it would solve everything. But things are not so easy!

In this document we will see what’s GraphQL, what it solves, how to build a simple GraphQL back-end for our ASP.NET Core project and also how to consume this GraphQL.

Before starting, notice that you can find the demonstrated project on https://github.com/ebicoglu/AspNetCoreGraphQL-MyHotel

img

GraphQL & Client transmission

img

GraphQL & Clients

img

Rest API & GraphQL pipelines

Note that GraphQL doesn’t need HTTP to work. HTTP is a transporting way to send data to the API. Each request will have unique URLs that’s why HTTP caching is not possible!

img

img

GraphQL Language Basics

You can query popular APIs using GraphQL in your browser from GraphQL Playground

So I’ll play with GitHub’s GraphQL schema. The below query gets the user with the username “ebicoglu” and including repositories of the user. And we name this query “TestQuery”.

query TestQuery {
  graphQLHub
  github {
    user(username: "ebicoglu") {
      id
      login
      company
      avatar_url
      repos {
        name
      }
    }
  }
}

img

A sample request & response

Variables

You can use variables in your queries. It’s similar to SQL variables. Not to make string concatenate we supply variables in a different parameter.

query TestQuery($currentUserName: String!) {
  graphQLHub
  github {
    user(username: $currentUserName) {
      id
      login
      company
      avatar_url
      repos {
        name
      }
    }
  }
}

img

Directives

Directives provide a way describe additional options to the GraphQL executor. Essentially a directive allows the GraphQL to change the result of our queries based on criteria we provide. It can be used for permission management. There are @skip and @include directives. The skip directive, when used on fields or fragments, allows us to exclude fields based on some condition. The include directive, allows us to include fields based on some condition. You see an include directive usage in the below sample.

img

Aliases

Sometimes UI uses different field names rather than what it comes from the host. You can rename a field so that it matches your UI fields and you don’t need to transform the data.

In the following query we rename fields

  • login > user_name
  • company > company_name.
query TestQuery($currentUserName: String!, 
  $includeRepos: Boolean!) {
  graphQLHub
  github {
    user(username: $currentUserName) {
      id
      user_name: login
      company_name: company
      avatar_url
      repos @include(if: $includeRepos) {
        name
      }
    }
  }
}

img

Using aliases to rename fields

Fragments

A fragment is a shared piece of query logic. It reduces code repeat.

A fragment is a template of variables.

In the following query UserInfo is a fragment declared out of the query bracelets. Use three dots (…) prefix when to use a fragment.

query TestQuery {
  github {
    user1: user(username: "ebicoglu") {
      ...UserInfo
    }
    user2: user(username: "shanselman") {
      ...UserInfo
    }
  }
}fragment UserInfo on GithubUser {
  id
  login
  company
}

img

Mutations

We focus on only data fetching. But mutations allow to send data to the server. We will not cover this subject. For more information you can check out https://graphql.org/learn/queries/#mutations


Building GraphQL APIs with ASP.NET Core

Preparing Project

1- Create an ASP.NET Core project. I prefer an ASP.NET Core Angular project to have UI capabilities. So create an empty folder, open CMD in the folder and write the below command to create your empty Angular project.

dotnet new angular

2- Prepare the project for basic CRUD operations. In our sample, the sample project is an hotel reservation application.

2.1 — Create entities: Guest, Room and Reservation.

    public class Guest
    {
        [Key]
        public int Id { get; set; }

        [Required]
        [StringLength(300)]
        public string Name { get; set; }

        public DateTime RegisterDate { get; set; }

        public Guest()
        {
            
        }

        public Guest(string name, DateTime registerDate)
        {
            Name = name;
            RegisterDate = registerDate;
        }
    }    public enum RoomStatus
    {
        Unavailable = 0,
        Available = 1,
    }    public class Room
    {
        [Key]
        public int Id { get; set; }

        [Required]
        public int Number { get; set; }

        [StringLength(200)]
        public string Name { get; set; }

        [Required]
        public RoomStatus Status { get; set; }

        public bool AllowedSmoking { get; set; }

        public Room()
        {
            
        }
        public Room(int number, string name, RoomStatus status, bool allowedSmoking)
        {
            Number = number;
            Name = name;
            Status = status;
            AllowedSmoking = allowedSmoking;
        }
    }    public class Reservation
    {
        [Key]
        public int Id { get; set; }        [ForeignKey("RoomId")]
        public Room Room { get; set; }
        public int RoomId { get; set; }

        [ForeignKey("GuestId")]
        public Guest Guest { get; set; }
        public int GuestId { get; set; }

        [Required]
        public DateTime CheckinDate { get; set; }

        public DateTime CheckoutDate { get; set; }

        public Reservation()
        {

        }

        public Reservation(DateTime checkinDate, DateTime checkoutDate, int roomId, int guestId)
        {
            CheckinDate = checkinDate;
            CheckoutDate = checkoutDate;
            RoomId = roomId;
            GuestId = guestId;
        }
    }

2.2 —Reference Microsoft.EntityFrameworkCore package from NuGet.

2.3 — Create your DbContext.

public class MyHotelDbContext : DbContext
    {
        public static string DbConnectionString = "Server=localhost; Database=MyHotelDb; Trusted_Connection=True;";

        public MyHotelDbContext(DbContextOptions<MyHotelDbContext> options)
            : base(options)
        { }

        public DbSet<Reservation> Reservations { get; set; }

        public DbSet<Guest> Guests { get; set; }

        public DbSet<Room> Rooms { get; set; }

         protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //GUESTS
            modelBuilder.Entity<Guest>().HasData(new Guest("Alper Ebicoglu", DateTime.Now.AddDays(-10)) { Id = 1 });
            modelBuilder.Entity<Guest>().HasData(new Guest("George Michael", DateTime.Now.AddDays(-5)) { Id = 2 });
            modelBuilder.Entity<Guest>().HasData(new Guest("Daft Punk", DateTime.Now.AddDays(-1)) { Id = 3 });

            //ROOMS
            modelBuilder.Entity<Room>().HasData(new Room(101, "yellow-room", RoomStatus.Available, false) { Id = 1 });
            modelBuilder.Entity<Room>().HasData(new Room(102, "blue-room", RoomStatus.Available, false) { Id = 2 });
            modelBuilder.Entity<Room>().HasData(new Room(103, "white-room", RoomStatus.Unavailable, false) { Id = 3 });
            modelBuilder.Entity<Room>().HasData(new Room(104, "black-room", RoomStatus.Unavailable, false) { Id = 4 });

            //RESERVATIONS
            modelBuilder.Entity<Reservation>().HasData(new Reservation(DateTime.Now.AddDays(-2), DateTime.Now.AddDays(3), 3, 1) { Id = 1 });
            modelBuilder.Entity<Reservation>().HasData(new Reservation(DateTime.Now.AddDays(-1), DateTime.Now.AddDays(4), 4, 2) { Id = 2 });
 
            base.OnModelCreating(modelBuilder);
        }
    }

2.4 — Create a repository for the Reservation entity

    public class ReservationRepository
    {
        private readonly MyHotelDbContext _myHotelDbContext;

        public ReservationRepository(MyHotelDbContext myHotelDbContext)
        {
            _myHotelDbContext = myHotelDbContext;
        }

        public async Task<List<T>> GetAll<T>()
        {
            return await _myHotelDbContext
                .Reservations
                .Include(x => x.Room)
                .Include(x => x.Guest)
                .ProjectTo<T>()
                .ToListAsync();
        }

        public async Task<IEnumerable<Reservation>> GetAll()
        {
            return await _myHotelDbContext
                .Reservations
                .Include(x => x.Room)
                .Include(x => x.Guest)
                .ToListAsync();
        }
    }

2.4 — Add DbContext and your repository to the ASP.NET Core Startup.cs.

public void ConfigureServices(IServiceCollection services)
     {
            //...
            services.AddDbContext<MyHotelDbContext>(options => options.UseSqlServer(MyHotelDbContext.DbConnectionString));            services.AddTransient<ReservationRepository>();
            //...
     }

2.5 — We’ll use AutoMapper for mapping objects from entity to model/DTO. This is optional but you can add AutoMapper package to your project. AutoMapper must be configured in the startup of your project. So write the below line in Configure method of Startup.cs

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, MyHotelDbContext dbContext)
        {
           //...           InitializeMapper();
        }private static void InitializeMapper()
        {
            Mapper.Initialize(x =>
            {
                x.CreateMap<Guest, GuestModel>();
                x.CreateMap<Room, RoomModel>();
                x.CreateMap<Reservation, ReservationModel>();
            });
        }

2.6 — Add Entity Framework migrations. And apply migration to create your database. Write the below commands to Package Manager Console.

add-migration "InitialCreate"
update-database

Till now you’ve created your entities, configured Entity Framework library and created a repository to get all reservations. To be able to list the reservations let’s change the default Angular template. First of all, create a controller to feed the Angular client. The URL for listing all reservations will be http://*domain*/reservations/list

    [Route("api/[controller]")]
    public class ReservationsController : Controller
    {
        private readonly ReservationRepository _reservationRepository;

        public ReservationsController(ReservationRepository reservationRepository)
        {
            _reservationRepository = reservationRepository;
        }

        [HttpGet("[action]")]
        public async Task<List<ReservationModel>> List()
        {
            return await _reservationRepository.GetAll<ReservationModel>();
        }
    }

The new Angular template has a fetch-data page to show sample data. Let’s modify fetch-data.component.ts to show our data.

import { Component, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-fetch-data',
  templateUrl: './fetch-data.component.html'
})
export class FetchDataComponent {
  public reservations: Reservation[];

  constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
    http.get<Reservation[]>(baseUrl + 'api/Reservations/List').subscribe(result => {
      this.reservations = result;
    }, error => console.error(error));
  }
}

interface Room {
  id: number;
  number: number;
  name: string;
  status: string;
  allowedSmoking: boolean;
}

interface Guest {
  id: number;
  name: string;
  registerDate: Date;
}

interface Reservation {
  checkinDate: Date;
  checkoutDate: Date;
  room: Room;
  guest: Guest;
}

Now change the content of fetch-data.component.html.

<h1>Reservations ({{reservations ? reservations.length : 0}})</h1>

<p *ngIf="!reservations"><em>Loading...</em></p>

<table class='table' *ngIf="reservations">
  <thead class="thead-dark">
    <tr>
      <th>Guest Name</th>
      <th>Room Number</th>
      <th>Checkin Date</th>
      <th>Checkout Date</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let reservation of reservations">
      <td>{{ reservation.guest.name }}</td>
      <td>{{ reservation.room.number }}</td>
      <td>{{ reservation.checkinDate | date: 'dd.MM.yyyy'}}</td>
      <td>{{ reservation.checkoutDate | date: 'dd.MM.yyyy' }}</td>
    </tr>
  </tbody>
</table>

Run the application and click the Fetch Data link to list our reservations on the page. This fetches the data from ReservationsController which is a classical RestAPI. Now we’ll add GraphQL to our project.

3- Add the GraphQL package. The GitHub link is https://github.com/graphql-dotnet/graphql-dotnet

img

Also add the following packages (needs later on)

Simply you can add the below 3 lines to MyHotel.csproj

<ItemGroup>
<PackageReference Include="GraphQL" Version="2.4.0" />
    <PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="3.4.0" />
    <PackageReference Include="GraphQL.Server.Ui.Playground" Version="3.4.0" />
</ItemGroup>

4- Create GraphQL Type for each of your entities:

    public class GuestType : ObjectGraphType<Guest>
    {
        public GuestType()
        {
            Field(x => x.Id);
            Field(x => x.Name);
            Field(x => x.RegisterDate);
        }
    }    public class ReservationType : ObjectGraphType<Reservation>
    {
        public ReservationType()
        {
            Field(x => x.Id);
            Field(x => x.CheckinDate);
            Field(x => x.CheckoutDate);
            Field<GuestType>(nameof(Reservation.Guest));
            Field<RoomType>(nameof(Reservation.Room));
        }
    }    public class RoomStatusType: EnumerationGraphType<RoomStatus>
    {
    }    public class RoomType : ObjectGraphType<Room>
    {
        public RoomType()
        {
            Field(x => x.Id);
            Field(x => x.Name);
            Field(x => x.Number);
            Field(x => x.AllowedSmoking);
            Field<RoomStatusType>(nameof(Room.Status));
        }
    }
Mapping for GraphQL and .NET

Create a schema in the project as below:

public class MyHotelSchema : Schema
{
        public MyHotelSchema(IDependencyResolver resolver) : base(resolver)
        {
            Query = resolver.Resolve<MyHotelQuery>();
        }
}

Now add your new Schema, Graph Types and GraphQL middle ware to your ASP.NET Core startup.

public void ConfigureServices(IServiceCollection services)
        {
            //...
           
            services.AddScoped<IDependencyResolver>(x =>
                new FuncDependencyResolver(x.GetRequiredService));

            services.AddScoped<MyHotelSchema>();

            services.AddGraphQL(x =>
            {
                x.ExposeExceptions = true; //set true only in development mode. make it switchable.
            })
            .AddGraphTypes(ServiceLifetime.Scoped);            
        }

And in the Configure method of Startup.cs use GraphQL. Also we’ll use GraphQL Playground library which is cool to explore the data with GrapQL. It’s similar to Swagger.

       public void Configure(IApplicationBuilder app, IHostingEnvironment env, MyHotelDbContext dbContext)
        {
           //...

            app.UseGraphQL<MyHotelSchema>();
            app.UseGraphQLPlayground(new GraphQLPlaygroundOptions()); //to explorer API navigate https://*DOMAIN*/ui/playground           //... UseMVC ....
            
        }

Run the project and navigate to the following URL

https://*DOMAIN*/ui/playground

To see your reservation data in the GraphQL Playground page you can write the below query.

    query TestQuery {
              reservations {
                id
                checkinDate
                checkoutDate                guest {
                  id
                  name
                  registerDate
                }                room {
                  id
                  name
                  number
                  allowedSmoking
                  status
                }
           }
        }

This query is a request to get the list of reservations including Guest and Room child entities. You can try different GraphQL queries to test it out.

5- Filtering Data

To accept arguments from your query, you need to add arguments to your query. Each argument can be added with a type, description and default value.

public class MyHotelQuery : ObjectGraphType
    {
        public MyHotelQuery(ReservationRepository reservationRepository)
        {
            Field<ListGraphType<ReservationType>>("reservations",
                arguments: new QueryArguments(new List<QueryArgument>
                {
                    new QueryArgument<IdGraphType>
                    {
                        Name = "id"
                    },
                    new QueryArgument<DateGraphType>
                    {
                        Name = "checkinDate"
                    },
                    new QueryArgument<DateGraphType>
                    {
                        Name = "checkoutDate"
                    },
                    new QueryArgument<BooleanGraphType>
                    {
                        Name = "roomAllowedSmoking"
                    },
                    new QueryArgument<RoomStatusType>
                    {
                        Name = "roomStatus"
                    }
                }),
                resolve: context =>
                {
                    var query = reservationRepository.GetQuery();

                    var reservationId = context.GetArgument<int?>("id");
                    if (reservationId.HasValue)
                    {
                        return reservationRepository.GetQuery().Where(r => r.Id == reservationId.Value);
                    }

                    var checkinDate = context.GetArgument<DateTime?>("checkinDate");
                    if (checkinDate.HasValue)
                    {
                        return reservationRepository.GetQuery()
                            .Where(r => r.CheckinDate.Date == checkinDate.Value.Date);
                    }

                    var checkoutDate = context.GetArgument<DateTime?>("checkoutDate");
                    if (checkoutDate.HasValue)
                    {
                        return reservationRepository.GetQuery()
                            .Where(r => r.CheckoutDate.Date >= checkoutDate.Value.Date);
                    }

                    var allowedSmoking = context.GetArgument<bool?>("roomAllowedSmoking");
                    if (allowedSmoking.HasValue)
                    {
                        return reservationRepository.GetQuery()
                            .Where(r => r.Room.AllowedSmoking == allowedSmoking.Value);
                    }

                    var roomStatus = context.GetArgument<RoomStatus?>("roomStatus");
                    if (roomStatus.HasValue)
                    {
                        return reservationRepository.GetQuery().Where(r => r.Room.Status == roomStatus.Value);
                    }

                    return query.ToList();
                }
            );

        }
    }

Now you can filter your query by the following fields: id, checkinDate, checkoutDate, roomAllowedSmoking, roomStatus.

In the below GraphQL query we filter reservations by room.allowedSmoking boolean field and room.status enum.

query TestQuery {
  reservations (roomAllowedSmoking:false, roomStatus: AVAILABLE){
    id
    checkinDate
    checkoutDate
    guest {
      id
      name
      registerDate
    }
    room {
      id
      name
      number
      allowedSmoking
      status
    }
  }
}

6- Adding Authorization

GraphQL middle-ware is a new layer that doesn’t depend on MVC middle-ware. That’s why the request will not be authorized by MVC. So we have to authorize it by injecting AuthorizationService and check policies. To do this;

Open Startup.cs and in the ConfigureServices method append the below code to services.AddGraphQL()

services.AddGraphQL(x =>
{
    x.ExposeExceptions = true;
})
.AddGraphTypes(ServiceLifetime.Scoped)
.AddUserContextBuilder(httpContext => httpContext.User)
.AddDataLoader();

Then you can get the user identity within the resolve code block of your query

public MyHotelQuery(ReservationRepository reservationRepository)
{           
     Field<ListGraphType<ReservationType>>("reservations",
                arguments: new QueryArguments(new List<QueryArgument>
                {
                  //............
                }),
                resolve: context =>
                {                 
                    var user = (ClaimsPrincipal)context.UserContext;
                    var isUserAuthenticated = ((ClaimsIdentity) user.Identity).IsAuthenticated;

                    //............
                }
        );

 }

7- Interfaces

public class ReservationInterface : InterfaceGraphType<Reservation>
    {
        public ReservationInterface()
        {
            Name = "Reservation";

            Field(x => x.Id);
            Field(x => x.CheckinDate).Description("The first day of the stay");
            Field(x => x.CheckoutDate).Description("The leaving day");
            Field<GuestType>(nameof(Reservation.Guest));
            Field<RoomType>(nameof(Reservation.Room));
        }
    }

    public class ReservationType : ObjectGraphType<LuggageReservation>
    {
        public ReservationType()
        {
            Name = "LuggageReservation";

            Interface<ReservationInterface>();
        }
    }

    public class LuggageReservation : Reservation
    {
       
    }

8- Consuming GraphQL API

Sending a query via an HTTP request is simple! You send a get request to the below URL including your query which you use on the GraphQL Playground page.

http://*domain*/graphql?query=

Let’s make a simple query to get the list of reservations with id, checkinDate and checkoutDate

Here’s our query:

query TestQuery {
  reservations {
    id
    checkinDate
    checkoutDate
  }
}

To send this query as a HTTP request, remove the first 2 words: query and TestQuery then flatten the string into one line as below:

{reservations {id checkinDate checkoutDate}}

Now we append this query string to our graphql URL:

https://localhost:44349/graphql?query={reservations {id checkinDate checkoutDate}}

When you go to this URL on your browser, you’ll see the response as JSON object. Simple and straight-forward!

{
   "data":{
      "reservations":[
         {
            "id":1,
            "checkinDate":"2019-02-05",
            "checkoutDate":"2019-02-18"
         },
         {
            "id":2,
            "checkinDate":"2019-02-10",
            "checkoutDate":"2019-02-20"
         }
      ]
   }
}

Write your queries with the help of GraphQL Playground and then use it in your code.

8.1 — Sending Multiple Queries In a Single Request

To send multiple queries, you can use aliases in the query.

In the following query we assign names to each query as reservation_1 and reservation_2.

{
  reservation_1: reservations(id: 1) {
    id
    checkinDate
    checkoutDate
  }
  
  reservation_2: reservations(id: 2) {
    id
    checkinDate
    checkoutDate
  }
}

And we successfully get 2 results in a single response

{
  "data": {
    "reservation_1": [
      {
        "id": 1,
        "checkinDate": "2019-02-05",
        "checkoutDate": "2019-02-18"
      }
    ],
    "reservation_2": [
      {
        "id": 2,
        "checkinDate": "2019-02-10",
        "checkoutDate": "2019-02-20"
      }
    ]
  }
}

8.2 — Reducing Repetition of Fields in a Query

In the previous sample, there were 2 queries asking for the same fields of reservation. To reduce this field repetitions we can use Fragments.

We already covered Fragments in previous topics. See how we do it:

{
  reservation_1: reservations(id: 1) {
    ...reservationFields
  }reservation_2: reservations(id: 2) {
    ...reservationFields
  }
}fragment reservationFields on ReservationType {
  id
  checkinDate
  checkoutDate
}

8.3 —Running a Single Query Among Multiple Queries

Sometimes it needs to be run a single query among the other queries. To do this, we use named queries. You see 2 queries below: all_reservations and selected_reservation.

query all_reservations {
  reservations {
    id
    checkinDate
    checkoutDate
  }
}query selected_reservation {
  reservations(id: 1) {
    id
    guest {
      name
    }
    room {
      name
    }
  }
}

To run one of these queries in a URL simply use operationName in your GraphQL request. In the below URL, it’ll run selected_reservation query.

https://localhost:44349/graphql?query=query all_reservations {reservations {id checkinDate checkoutDate}} query selected_reservation {reservations(id: 1) {id guest{name} room{name}}}&operationName=selected_reservation

And the response of selected_reservation query:

{
    "data": {
        "reservations": [{
            "id": 1,
            "guest": {
                "name": "Alper Ebicoglu"
            },
            "room": {
                "name": "white-room"
            }
        }]
    }
}

You can use “tools.knowledgewalls.com” to convert your GraphQL JSON Query to URL Query String!

8.4 — Using Variables in a HTTP Request

We learned the usage of variables in a query as below.

img

8.5 — Directives

GraphQL directives have been created to deal with the problem of recurring tasks during the implementation. In the below sample, we see “include” directive is used. This directive gets an argument “if” which is a Boolean value. We created a variable called “includeGuest” and if it’s true it includes guest to the response.

img

And there’s the opposite one for include called “skip”. It behaves as the opposite way of “include”. We set the “skip” value to false so so it’s included.

img

8.6 —Sending Error From the Host

If you want to send your custom error message to the client, you can add a new ExecutionError in the context.Errors list of the resolve method in your query. See the below sample, we check the reservationId, if it’s lower than zero, then we add

img

public class MyHotelQuery : ObjectGraphType
{
        public MyHotelQuery(ReservationRepository reservationRepository)
        {
            Field<ListGraphType<ReservationType>>("reservations",
                arguments: new QueryArguments(new List<QueryArgument>
                {
                    //....
                }),
                resolve: context =>
                {
                    var query = reservationRepository.GetQuery();

                    //...
                    var reservationId = context.GetArgument<int?>("id");
                    if (reservationId.HasValue)
                    {
                       if (reservationId.HasValue)
                       {
                        if (reservationId.Value <= 0)
                        {
                            context.Errors.Add(new ExecutionError("reservationId must be greater than zero!"));
                            return new List<Reservation>();
                        }

                        return reservationRepository.GetQuery().Where(r => r.Id == reservationId.Value);
                    }
 
                   //...
                }
            );

        }
}

9 — Consuming Your GraphQL API from C#

9.1 — Consuming GraphQL API With a Simple C# Http Client

To consume your GraphQL in a C# application you can create a simple class to map the data from host. Here you see my custom class for that;

public class Response<T>
    {
        public T Data { get; set; }

        public List<ErrorModel> Errors { get; set; }

        public void OnErrorThrowException()
        {
            if (Errors != null && Errors.Any())
            {
                throw new ApplicationException($"Message: {Errors[0].Message} Code: {Errors[0].Code}");
            }
        }
    }

    public class ErrorModel
    {
        public string Message { get; set; }

        public string Code { get; set; }
    }

And this a basic client to transfer data with GraphQL. We get a JSON from GraphQL endpoint. Then we deserialize it to our custom Response class.

public class MyHotelGraphqlClient
    {
        public const string GraphqlAddress = "https://localhost:44349/graphql/";

        private readonly HttpClient _httpClient;

        public MyHotelGraphqlClient(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        public async Task<Response<ReservationContainer>> GetReservationsAsync()
        {
            var response = await _httpClient.GetAsync(
                @"?query={
                reservations {
                checkinDate
                guest  {
                  name
                }
                room {
                  name
                }
              }
            }");

            var stringResult = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<Response<ReservationContainer>>(stringResult);
        }
    }

To be able to inject this client, you need to register it in the Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            //we add this after services.AddMvc()

            services.AddHttpClient<MyHotelGraphqlClient>(x => x.BaseAddress = new Uri(MyHotelGraphqlClient.GraphqlAddress));//........
}

And now it’s ready to be used. In MyHotelGraphqlClient with the name _myHotelGraphClient and use it;

var response = await _myHotelGraphqlClient.GetReservationsAsync();
response.OnErrorThrowException();
var reservations = response.Data.Reservations;  

9.2 — Consuming GraphQL API With GraphQl.Client Library

GraphQl.Client is a .NET Standard library that helps to make request to GraphQL API. It’s open-source. Here’s the GitHub Repo.

GraphQL.Client 1.0.3A GraphQL Clientwww.nuget.org

Create a class and name it ReservationGraphqlClient. This is our client that will use Graphql.Client to fetch the reservations data. Inject GraphQLClient which is in the GraphQL.Client namespace. Create an GraphQLRequest and set it’s Query property. Then post it via GraphQLClient. Incase you get any error check the Errors property of the response. You can retrieve the reservations from GetDataFieldAs() method.

public class ReservationGraphqlClient
    {
        private readonly GraphQLClient _client;

        public ReservationGraphqlClient(GraphQLClient client)
        {
            _client = client;
        }

        public async Task<List<ReservationModel>> GetReservationsAsync()
        {
            var query = new GraphQLRequest
            {
                Query = @"
query reservation {
  reservations {
    checkinDate
    guest  {
      name
    }
    room {
      name
    }
  }
}
"
            };

            var response = await _client.PostAsync(query);
            if (response.Errors != null && response.Errors.Any())
            {
                throw new ApplicationException(response.Errors[0].Message);
            }

            var reservations = response.GetDataFieldAs<List<ReservationModel>>("reservations");
            return reservations;
        }
    }

To be able to inject GraphQLClient and ReservationGraphqlClient, register in them Startup as singleton dependencies.

public void ConfigureServices(IServiceCollection services)
        {
            //Add it after Mvc middleware.          
           
            services.AddSingleton(t => new GraphQLClient(Configuration["GraphQlEndpoint"]));
            services.AddSingleton<ReservationGraphqlClient>();           //...
}

10 — Consuming Your GraphQL API

10.1— Fetching Data Using Angular2

To make HTTP requests you need to inject HttpClient. Then make a GET request to http://*domain*/graphql/. Add your query to query parameter of your URL.

export class FetchDataComponent {
 public reservations: Reservation[];

 constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {

    this.fetchDirectlyFromGraphQL = function () {
      var query = `?query=
                    {
                      reservations {
                        checkinDate
                        guest  {
                          name
                        }
                        room {
                          name
                        }
                      }
                    }`  ;

      http.get<Reservation[]>(baseUrl + 'graphql/' + query).subscribe(result => {
        this.reservations = result.data.reservations;       
      }, error => console.error(error));
    }   }
}

10.2 — Fetching Data Using JavaScript

To fetch using JavaScript import the following library

<script src="https://unpkg.com/[email protected]"></script>

Use Fetch command to send the request:

    fetch('/graphql', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: JSON.stringify({
          query: 'query reservation {reservations {checkinDate guest  {name} room {name}}}'
        })
      })
      .then(r => r.json())
      .then(response => {
        this.reservations = response.data.reservations;
      });
  

10.3 — Using Apollo Client

There’s a rich client that helps us to fetch data with GraphQL. Apollo Client is the ultra-flexible, community driven GraphQL client for Angular, JavaScript, and native platforms. It is designed from the ground up to make it easy to build UI components that fetch data with GraphQL. It’s open-source but also there’re paid plans.

Here’s how to query using Apollo Client:

var client = new Apollo.lib.ApolloClient(
      {
        networkInterface: Apollo.lib.createNetworkInterface({
          uri: "https://localhost:44349/graphql"
        })
      });

    const query = Apollo.gql`
query reservation {
  reservations {
    checkinDate
    guest  {
      name
    }
    room {
      name
    }
  }
}`;
     
    client.query({
      query: query
    }).then(result => {
      this.reservations = result.data.reservations;
    });

Finally all the code mentioned here can be accessible on GitHub.

https://github.com/ebicoglu/AspNetCoreGraphQL-MyHotel


Read More:

  1. How to Use Attribute Directives to Avoid Repetition in Angular Templates
  2. ASP.NET Core 3.1 Webhook Implementation Using Pub/Sub Pattern
  3. Why You Should Prefer Singleton Pattern over a Static Class?