GraphQL in .NET: Pagination

Introduction

In this article, we will talk about graphQL pagination.

One of the most efficient ways to work with GraphQL is to communicate with the IQueryable interface. Our second project in the repo  (002_GraphQLWithEFCore) is about providing GraphQL communication with EF Core.

First, let's enter Customer and Card data into the database with EF Core.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public List<Card> Cards { get; set; }
}

public class Card
{
    public int Id { get; set; }
    public string Number { get; set; }
    public string CVC { get; set; }
    public string ExpiryDate { get; set; }
    public int CustomerId { get; set; }
    public Customer Customer { get; set; }  
}

Let's configure DbContext:

public class GraphQLAppDbContext : DbContext
{
    public GraphQLAppDbContext(DbContextOptions<GraphQLAppDbContext> options) : base(options) { }

    public DbSet<Customer> Customers { get; set; }
    public DbSet<Card> Cards { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>()
            .HasMany(e => e.Cards)
            .WithOne(x => x.Customer)
            .HasForeignKey(x => x.CustomerId)
            .IsRequired();

        modelBuilder.Entity<Customer>()
            .HasData(new Customer() { Id = 1, Email = "[email protected]", Name = "Simon Baker" },
                     new Customer() { Id = 2, Email = "[email protected]", Name = "Hanayama Kaoru" },
                     new Customer() { Id = 3, Email = "[email protected]", Name = "Hanma Baki" },
                     new Customer() { Id = 4, Email = "[email protected]", Name = "O. Doppo" });

        modelBuilder.Entity<Card>()
            .HasData(new Card() { Id = 1, CustomerId = 1, Number = "1234-2345-3445-3456", CVC = "345", ExpiryDate = "08/26" },
                     new Card() { Id = 2, CustomerId = 2, Number = "1234-2345-3445-3456", CVC = "345", ExpiryDate = "08/26" },
                     new Card() { Id = 3, CustomerId = 3, Number = "3344-7654-3445-3456", CVC = "123", ExpiryDate = "09/25" },
                     new Card() { Id = 4, CustomerId = 4, Number = "5566-5454-3445-3456", CVC = "234", ExpiryDate = "07/24" },
                     new Card() { Id = 5, CustomerId = 1, Number = "6677-3232-3445-3456", CVC = "987", ExpiryDate = "06/26" },
                     new Card() { Id = 6, CustomerId = 1, Number = "8899-9898-3445-3456", CVC = "654", ExpiryDate = "05/27" },
                     new Card() { Id = 7, CustomerId = 2, Number = "9900-5678-3445-3456", CVC = "432", ExpiryDate = "04/28" },
                     new Card() { Id = 8, CustomerId = 1, Number = "1122-5678-3445-3456", CVC = "165", ExpiryDate = "03/23" },
                     new Card() { Id = 9, CustomerId = 2, Number = "2233-2543-3445-3456", CVC = "651", ExpiryDate = "02/29" },
                     new Card() { Id = 10, CustomerId = 3, Number = "3544-1234-3445-3456", CVC = "987", ExpiryDate = "01/26" });

        base.OnModelCreating(modelBuilder);
    }
}

Next, let's set up our Query resolver. (code)

public class Query
{
    public IQueryable<Customer> GetCustomers([Service] GraphQLAppDbContext graphqldbcontext)
    => graphqldbcontext.Customers.Include("Cards");
}

Finally, let's configure hotchocolate as in the first project. 

using _002_GraphQLWithEFCore.Database;
using _002_GraphQLWithEFCore.Queries;

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<GraphQLAppDbContext>(
        options => options.UseSqlServer("Data Source=.;Initial Catalog=GraphQLDb;Integrated Security=SSPI;TrustServerCertificate=True;"));

builder.Services.AddGraphQLServer()
    .AddQueryType<Query>();

var app = builder.Build();

app.MapGraphQL();

app.Run();

Now, when we are ready to start our project, we can get all the customer and his card information in the following form.

GraphQL in .NET- Pagination

Pagination in GraphQL

Pagination is one of the main mechanisms that we constantly use in practice, and it allows us to get data in parts, not the whole.

Currently used forms of pagination are divided into 2 places:

Offset-based pagination

This is a form of pagination with a Skip-Take combination that we also use a lot in EF Core. But in systems with large data flow, this is not a convenient form of pagination. Because imagine you get the top 10 data in a system with a huge data stream. By the time you get the next 10 data, you're at best getting the first 10 data you received again because more data is already being entered into the system. (we are not pinned into items

GraphQL in .NET- Pagination

Let's say skip(3).Take(3) will return the elements [ 8, 7, 6 ] if there is no unique sort identifier. But the next time (Scenario 2) if there is data already entered when pagination is called, we will get repeated data again by saying Skip(3).Take(3). ( [ 9, 8, 7] )

Cursor-based pagination

Unlike offset-based pagination, because this form of pagination depends on specific entity attributes (in most cases, id), it always provides unique information and moves forward by default. In this case, we solve the above problem by forming a command like "give 5 elements after id=7".

GrahpQL uses Cursor-based pagination. It is really easy to configure pagination via the hot chocolate library. You just need to add [UsePaging] decorator on Query functions/ resolvers.

public class Query
{
    [UsePaging(MaxPageSize = 2)]
    public IQueryable<Customer> GetCustomers([Service] GraphQLAppDbContext graphqldbcontext)
    => graphqldbcontext.Customers.Include("Cards");
}

By default, graphQL returns 10 elements per page. However, this can be changed with the help of MaxPagesize. Looking at the graphQL schema again after adding the UsePaging attribute, we see some changes there.

GraphQL in .NET : Pagination (Part 3)

To access the pagination configuration, our pageInfo element is added to the interface, and we can access entity elements through nodes.

GraphQL in .NET- Pagination

To get the next element, we need to use the cursor index defined by the endcursor and indicate how many elements we want to get next.

GraphQL in .NET- Pagination


Similar Articles