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.
What is GraphQL?
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.
Create a GraphQL server
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.
Configuring the GraphQL server app
- Begin by installing the latest version of the HotChocolate.AspNetCore package. For this blog, we’ve utilized version 13.3.1.
- Create GraphQLQuery and GraphQLMutation classes to define the GraphQL resolver and mutation methods, respectively.
- Implement the following configuration code to set up GraphQL query and mutation types and enable CORS.
Program.csvar 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.
Create a data source
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; } }
Return data with required format
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; } } }
Create resolver function argument classes
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.
DataManagerRequest class
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; } }
Aggregate class
public class Aggregate { [GraphQLName("Field")] public string Field { get; set; } [GraphQLName("Type")] public string Type { get; set; } }
Sorting class
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; } }
Searching class
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; } }
Filtering class
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; } }
Order class
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; } }
Customer class
public class CustomerAddress { [GraphQLName("ShipCity")] public string ShipCity { get; set; } [GraphQLName("ShipCountry")] public string ShipCountry { get; set; } }
Performing data operations
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 }; }
Performing CRUD operations
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; } }
Run the GraphQL server app
To run the GraphQL server app, use the Banana Cake Pop IDE. Alternatively, you can use any other IDE that supports running GraphQL queries.
Add Syncfusion Blazor DataGrid component
To add the Syncfusion Blazor DataGrid component and leverage its capabilities with the GraphQL Adaptor, follow these detailed steps:
- Create a Blazor application and add Syncfusion.Blazor NuGet package.
- Now, bind data in the Syncfusion Blazor DataGrid using the GraphQL Adaptor:
- Connect your GraphQL service data to the Blazor DataGrid by providing the GraphQL query string via the Query property of the GraphQLAdaptorOptions class.
- Set the ResolverName property of GraphQLAdaptorOptions to efficiently map the response from your GraphQL service to the Blazor DataGrid.
- Implement a Resolver function within your GraphQL server to handle data operations such as paging, sorting, and filtering.
- Configure mutation queries in the Mutation property of the GraphQLAdaptorOptions to enable CRUD operations within the DataGrid.
Fetching data from the GraphQL service
Follow these steps to bind data from a GraphQL service to the Blazor DataGrid:
- Provide the GraphQL query string via the Query property of the GraphQLAdaptorOptions.
- Set the ResolverName property of GraphQLAdaptorOptions to map the response efficiently.
- The GraphQLAdaptor expects the response in JSON format with properties such as Result, Count, and Aggregates, containing the collection of entities, total number of records, and aggregate values, respectively.
- Ensure that the GraphQL response is returned in JSON format with the structure { “data”: { … } } with the query name as a field.
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; } } }
Performing CRUD operations using mutation
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.
Syncfusion DataManager with GraphQL adaptor
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.
GitHub reference
Also, explore performing CRUD actions in the Blazor DataGrid using GraphQL GitHub demo for more details.
Syncfusion Blazor components can be transformed into stunning and efficient web apps.
Conclusion
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!
Related blogs
- Secure File Handling in Blazor: Implement JWT Authentication
- Navigating the World: Shapefile Integration in Syncfusion Blazor Maps
- Easily Perform CRUD Actions in Blazor Pivot Table with SQL Database & Entity Framework
- Boost Your Data Analysis: Best Practices for Optimizing Syncfusion Blazor Pivot Table Performance