TLDR: GraphQL lets clients request only the needed data, improving efficiency over REST APIs. Let’s see how to connect and manage data in the Syncfusion Blazor DataGrid using GraphQL. This guide covers configuring a GraphQL server, creating a data source, and implementing CRUD operations.
Welcome to this blog post, where we’ll explore using GraphQL to connect data with the Syncfusion Blazor DataGrid component. We will cover various operations, such as sorting, filtering, grouping, paging, and CRUD (Create, Read, Update, Delete) actions. By the end of this guide, you’ll better understand how GraphQL can enhance your data management capabilities within the Blazor DataGrid.
GraphQL is an open-source query language for APIs, enabling clients to request specific data they need. This empowers clients to control data retrieval, shifting the focus from the server. GraphQL is not tied to any particular database or storage engine and serves as a flexible and efficient alternative to traditional REST APIs.
As defined by the official GraphQL website, “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.” Unlike conventional REST APIs, GraphQL facilitates a streamlined data querying and manipulation approach. It fosters decoupling the frontend and backend layers by enabling clients to specify precise data requirements in a single request.
First, we need to create a new ASP.NET Core Empty app as the GraphQL Server. To do so, open Visual Studio. In the Create a New Project window, choose the ASP.NET Core Empty project template.
Refer to the following image.
var builder = WebApplication.CreateBuilder(args); //GraphQL resolver is defined in the GraphQLQuery class, and mutation methods are defined in the GraphQLMutation class. builder.Services.AddGraphQLServer().AddQueryType<GraphQLQuery>().AddMutationType<GraphQLMutation>(); //CORS is enabled to access the GraphQL server from the client app. builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => { builder.WithOrigins("https://xxxxxx") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials().Build(); }); }); var app = builder.Build(); app.UseCors("AllowSpecificOrigin"); app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapGraphQL()); app.Run();
This configuration sets up the GraphQL server, defines resolver and mutation methods, and enables CORS to facilitate seamless client-server communication. Adjust the origins of the CORS policy according to your specific requirements.
The following code demonstrates creating a data source with a list of Order data in the GraphQLQuery class, which will be bound to the Blazor DataGrid.
public class GraphQLQuery { public static List<Order> Orders { get; set; } = GetOrdersList(); private static List<Order> GetOrdersList() { var data = new List<Order>(); int count = 1000; int employeeCount = 0; for (int i = 0; i < 10; i++) { data.Add(new Order() { OrderID = count + 1, EmployeeID = employeeCount + 1, CustomerID = "ALFKI", OrderDate = new DateTime(2023, 08, 23), Freight = 5.7 * 2, Address = new CustomerAddress() { ShipCity = "Berlin", ShipCountry = "Denmark" } }); data.Add(new Order() { OrderID = count + 2, EmployeeID = employeeCount + 2, CustomerID = "ANANTR", OrderDate = new DateTime(1994, 08, 24), Freight = 6.7 * 2, Address = new CustomerAddress() { ShipCity = "Madrid", ShipCountry = "Brazil" } }); data.Add(new Order() { OrderID = count + 3, EmployeeID = employeeCount + 3, CustomerID = "BLONP", OrderDate = new DateTime(1993, 08, 25), Freight = 7.7 * 2, Address = new CustomerAddress() { ShipCity = "Cholchester", ShipCountry = "Germany" } }); data.Add(new Order() { OrderID = count + 4, EmployeeID = employeeCount + 4, CustomerID = "ANTON", OrderDate = new DateTime(1992, 08, 26), Freight = 8.7 * 2, Address = new CustomerAddress() { ShipCity = "Marseille", ShipCountry = "Austria" } }); data.Add(new Order() { OrderID = count + 5, EmployeeID = employeeCount + 5, CustomerID = "BOLID", OrderDate = new DateTime(1991, 08, 27), Freight = 9.7 * 2, Address = new CustomerAddress() { ShipCity = "Tsawassen", ShipCountry = "Switzerland" } }); count += 5; employeeCount += 5; } return data; } }
To bind data to the Blazor DataGrid component, the resolver function should return the data with Result, Count, and optional Aggregates properties defined in the ReturnType<T> class.
public class ReturnType<T> { public int Count { get; set; } public IEnumerable<T> Result { get; set; } [GraphQLType(typeof(AnyType))] public IDictionary<string, object> Aggregates { get; set; } } }
The GraphQL query will be passed from the grid with the dataManager property. So, to accept this parameter in the resolver function, we have to create the DataManagerRequest class, and the necessary classes required for the DataManagerRequest properties.
Refer to the following code examples.
public class DataManagerRequest { [GraphQLName("Skip")] public int Skip { get; set; } [GraphQLName("Take")] public int Take { get; set; } [GraphQLName("RequiresCounts")] public bool RequiresCounts { get; set; } = false; [GraphQLName("Params")] [GraphQLType(typeof(AnyType))] public IDictionary<string, object> Params { get; set; } [GraphQLName("Aggregates")] [GraphQLType(typeof(AnyType))] public List<Aggregate>? Aggregates { get; set; } [GraphQLName("Search")] public List<SearchFilter>? Search { get; set; } [GraphQLName("Sorted")] public List<Sort>? Sorted { get; set; } [GraphQLName("Where")] [GraphQLType(typeof(AnyType))] public List<WhereFilter>? Where { get; set; } [GraphQLName("Group")] public List<string>? Group { get; set; } [GraphQLName("antiForgery")] public string? antiForgery { get; set; } [GraphQLName("Table")] public string? Table { get; set; } [GraphQLName("IdMapping")] public string? IdMapping { get; set; } [GraphQLName("Select")] public List<string>? Select { get; set; } [GraphQLName("Expand")] public List<string>? Expand { get; set; } [GraphQLName("Distinct")] public List<string>? Distinct { get; set; } [GraphQLName("ServerSideGroup")] public bool? ServerSideGroup { get; set; } [GraphQLName("LazyLoad")] public bool? LazyLoad { get; set; } [GraphQLName("LazyExpandAllGroup")] public bool? LazyExpandAllGroup { get; set; } }
public class Aggregate { [GraphQLName("Field")] public string Field { get; set; } [GraphQLName("Type")] public string Type { get; set; } }
public class Sort { [GraphQLName("Name")] public string Name { get; set; } [GraphQLName("Direction")] public string Direction { get; set; } [GraphQLName("Comparer")] [GraphQLType(typeof(AnyType))] public object Comparer { get; set; } }
public class SearchFilter { [GraphQLName("Fields")] public List<string> Fields { get; set; } [GraphQLName("Key")] public string Key { get; set; } [GraphQLName("Operator")] public string Operator { get; set; } [GraphQLName("IgnoreCase")] public bool IgnoreCase { get; set; } }
public class WhereFilter { [GraphQLName("Field")] public string? Field { get; set; } [GraphQLName("IgnoreCase")] public bool? IgnoreCase { get; set; } [GraphQLName("IgnoreAccent")] public bool? IgnoreAccent { get; set; } [GraphQLName("IsComplex")] public bool? IsComplex { get; set; } [GraphQLName("Operator")] public string? Operator { get; set; } [GraphQLName("Condition")] public string? Condition { get; set; } [GraphQLName("value")] [GraphQLType(typeof(AnyType))] public object? value { get; set; } [GraphQLName("predicates")] public List<WhereFilter>? predicates { get; set; } }
public class Order { [GraphQLName("OrderID")] public int? OrderID { get; set; } [GraphQLName("CustomerID")] public string? CustomerID { get; set; } [GraphQLName("EmployeeID")] public int? EmployeeID { get; set; } [GraphQLName("OrderDate")] public DateTime? OrderDate { get; set; } [GraphQLName("Freight")] public double? Freight { get; set; } [GraphQLName("Address")] public CustomerAddress? Address { get; set; } }
public class CustomerAddress { [GraphQLName("ShipCity")] public string ShipCity { get; set; } [GraphQLName("ShipCountry")] public string ShipCountry { get; set; } }
The following code demonstrates the resolver function used in the GraphQL server to bind data and perform data operations like paging, sorting, filtering, and more.
public class GraphQLQuery { public ReturnType<Order> OrdersData(DataManagerRequest dataManager) { IEnumerable<Order> result = Orders; if (dataManager.Search != null) { //Perform searching here. } if (dataManager.Sorted != null) { //Perform sorting here. } if (dataManager.Where != null) { //Perform filtering here. } int count = result.Count(); if (dataManager.Skip != 0) { //Perform paging here. } if (dataManager.Take != 0) { //Perform Paging here. } if (dataManager.Aggregates != null) { //Perform total aggregate here. IDictionary<string, object> aggregates = new Dictionary<string, object>();; return new ReturnType<Order>() { Count = count, Result = result, Aggregates = aggregates }; } return dataManager.RequiresCounts ? new ReturnType<Order>() { Result = result, Count = count } : new ReturnType<Order>() { Result = result }; }
The following code demonstrates the mutation methods used in the GraphQL server to perform CRUD operations.
public class GraphQLMutation { public Order CreateOrder(Order order, int index, string action, [GraphQLType(typeof(AnyType))] IDictionary<string, object> additionalParameters) { GraphQLQuery.Orders.Insert(index, order); return order; } public Order UpdateOrder(Order order, string action, string primaryColumnName, int primaryColumnValue, [GraphQLType(typeof(AnyType))] IDictionary<string, object> additionalParameters) { Order updatedOrder = GraphQLQuery.Orders.Where(x => x.OrderID == primaryColumnValue).FirstOrDefault(); updatedOrder.OrderID = order.OrderID; updatedOrder.CustomerID = order.CustomerID; updatedOrder.Freight = order.Freight; updatedOrder.OrderDate = order.OrderDate; return updatedOrder; } public Order DeleteOrder(int primaryColumnValue, string action, string primaryColumnName, [GraphQLType(typeof(AnyType))] IDictionary<string, object> additionalParameters) { Order deletedOrder = GraphQLQuery.Orders.Where(x => x.OrderID == primaryColumnValue).FirstOrDefault(); GraphQLQuery.Orders.Remove(deletedOrder); return deletedOrder; } }
To run the GraphQL server app, use the Banana Cake Pop IDE. Alternatively, you can use any other IDE that supports running GraphQL queries.
To add the Syncfusion Blazor DataGrid component and leverage its capabilities with the GraphQL Adaptor, follow these detailed steps:
Follow these steps to bind data from a GraphQL service to the Blazor DataGrid:
Refer to the following code example.
@using Syncfusion.Blazor @using Syncfusion.Blazor.Data @using Syncfusion.Blazor.Grids <SfGrid TValue="Order" AllowPaging=true AllowSorting=true AllowFiltering=true> <SfDataManager Url="https://xxxxxx" GraphQLAdaptorOptions=@adaptorOptions Adaptor="Adaptors.GraphQLAdaptor"></SfDataManager> <GridColumns> <GridColumn Field=@nameof(Order.OrderID) HeaderText="Order ID" IsPrimaryKey=true TextAlign="TextAlign.Right" Width="120"></GridColumn> <GridColumn Field=@nameof(Order.CustomerID) HeaderText="Customer Name" Width="150"></GridColumn> <GridColumn Field=@nameof(Order.OrderDate) HeaderText="Order Date" Format="d" TextAlign="TextAlign.Right" Width="130"></GridColumn> <GridColumn Field=@nameof(Order.Freight) HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn> </GridColumns> </SfGrid> @code{ private GraphQLAdaptorOptions adaptorOptions { get; set; } = new GraphQLAdaptorOptions { Query = @" query ordersData($dataManager: DataManagerRequestInput!){ ordersData(dataManager: $dataManager) { count, result { OrderID, CustomerID, OrderDate, Freight } , aggregates } }", ResolverName = "OrdersData" }; public class Order { public int? OrderID { get; set; } public string CustomerID { get; set; } public DateTime? OrderDate { get; set; } public double? Freight { get; set; } } }
You can perform the CRUD operations by setting the mutation queries in the Mutation property of the GraphQLAdaptorOptions class.
Set the Insert mutation query in the Insert property of Mutation in the GraphQLAdaptorOptions. Similarly, you have to set the Update and Delete mutation queries in the Update and Delete properties of Mutation in the GraphQLAdaptorOptions, respectively.
The following variables are passed as parameters to the mutation method written for the Insert operation on the server side.
Properties | Description |
record | The new record needs to be inserted. |
index | Specifies the index at which the newly added record will be inserted. |
action | Indicates the type of operation being performed. When the same method is used for all CRUD actions, the respective argument distinguishes the actions, such as Add, Delete, and Update. |
additionalParameters | An optional parameter that can be used to perform any operations. |
The following variables are passed as parameters to the mutation method written for the Update operation on the server side.
Properties | Description |
record | The new record needs to be updated. |
action | Indicates the type of operation being performed. When the same method is used for all CRUD actions, the respective argument distinguishes the actions, such as Add, Delete, and Update. |
primaryColumnName | Specifies the field name of the primary column. |
primaryColumnValue | Specifies the primary column value that needs to be updated in the collection. |
additionalParameters | An optional parameter that can be used to perform any operations. |
The following variables are passed as parameters to the mutation method written for the Delete operation on the server side.
Properties | Description |
primaryColumnValue | Specifies the primary column value that needs to be removed from the collection. |
action | Indicates the type of operation being performed. When the same method is used for all CRUD actions, the respective argument distinguishes the actions, such as Add, Delete, and Update. |
primaryColumnName | Specifies the field name of the primary column. |
additionalParameters | An optional parameter that can be used to perform any operations. |
Refer to the following code example.
@using Syncfusion.Blazor @using Syncfusion.Blazor.Data @using Syncfusion.Blazor.Grids <SfGrid TValue="Order" AllowPaging=true AllowSorting=true AllowFiltering=true Toolbar="@(new List<string>() { "Search", "Add", "Edit", "Delete", "Cancel", "Update" })"> <GridEditSettings AllowAdding=true AllowEditing=true AllowDeleting=true Mode="EditMode.Normal"></GridEditSettings> <SfDataManager Url="https://xxxxxx" GraphQLAdaptorOptions=@adaptorOptions Adaptor="Adaptors.GraphQLAdaptor"></SfDataManager> <GridColumns> <GridColumn Field=@nameof(Order.OrderID) HeaderText="Order ID" IsPrimaryKey=true TextAlign="TextAlign.Right" Width="120"></GridColumn> <GridColumn Field=@nameof(Order.CustomerID) HeaderText="Customer Name" Width="150"></GridColumn> <GridColumn Field=@nameof(Order.OrderDate) HeaderText="Order Date" Format="d" TextAlign="TextAlign.Right" Width="130"></GridColumn> <GridColumn Field=@nameof(Order.Freight) HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" Width="120"></GridColumn> </GridColumns> </SfGrid> @code{ private GraphQLAdaptorOptions adaptorOptions { get; set; } = new GraphQLAdaptorOptions { Query = @" query ordersData($dataManager: DataManagerRequestInput!){ ordersData(dataManager: $dataManager) { count, result { OrderID, CustomerID, OrderDate, Freight } , aggregates } }", Mutation = new GraphQLMutation { Insert = @" mutation create($record: OrderInput!, $index: Int!, $action: String!, $additionalParameters: Any) { createOrder(order: $record, index: $index, action: $action, additionalParameters: $additionalParameters) { OrderID, CustomerID, OrderDate, Freight } }", Update = @" mutation update($record: OrderInput!, $action: String!, $primaryColumnName: String! , $primaryColumnValue: Int!, $additionalParameters: Any) { updateOrder(order: $record, action: $action, primaryColumnName: $primaryColumnName, primaryColumnValue: $primaryColumnValue, additionalParameters: $additionalParameters) { OrderID, CustomerID, OrderDate, Freight } }", Delete = @" mutation delete($primaryColumnValue: Int!, $action: String!, $primaryColumnName: String!, $additionalParameters: Any) { deleteOrder(primaryColumnValue: $primaryColumnValue, action: $action, primaryColumnName: $primaryColumnName, additionalParameters: $additionalParameters) { OrderID, CustomerID, OrderDate, Freight } }" }, ResolverName = "OrdersData" }; public class Order { public int? OrderID { get; set; } public string CustomerID { get; set; } public DateTime? OrderDate { get; set; } public double? Freight { get; set; } } }
After executing the above code examples, we’ll get the following output.
The GraphQLAdaptor integrated into our Syncfusion Blazor DataManager empowers efficient data management by facilitating CRUD operations and advanced data manipulations like paging, sorting, and filtering. The precise transmission of required parameters to the GraphQL server enables tailored data retrieval based on client requirements, seamlessly integrating with various data-bound components for displaying the retrieved data.
Also, explore performing CRUD actions in the Blazor DataGrid using GraphQL GitHub demo for more details.
Thanks for reading! By now, you should have a comprehensive understanding of leveraging the GraphQL server within our Syncfusion Blazor DataGrid component for data binding and CRUD operations. With GraphQL’s single endpoint, you can save time and significantly boost productivity while fetching data. Try the steps outlined in this blog and share your valuable feedback in the comments section below!
Existing Syncfusion users can access the latest version of Essential Studio® from the License and Downloads page. If you’re new to Syncfusion, use our 30-day free trial to discover these features.
For any inquiries or assistance, please contact us through our support forum, support portal, or feedback portal. We are always here to help you!