Telerik blogs
How to Bind the Kendo UI Grid to a GraphQL API_870x220

By now you have almost certainly heard of GraphQL, the runtime query language for APIs. With rapidly growing popularity, it is becoming an increasingly adopted standard for API development. This has generated demand for frameworks and UI tools that can easily consume the data from a GraphQL API – just like the Kendo UI components can do, as we provide seamless integration through the DataSource component.

This blog provides a comprehensive guide on how to setup the Kendo UI jQuery Grid component to perform CRUD operations through GraphQL queries and mutations. It includes a sample application, code snippets and documentation resources to get you up and running with Kendo UI and GraphQL.

Why are Kendo UI Widgets Easy to Integrate with a GraphQL API?

The backbone of every data-driven Kendo UI Component is the DataSource. It possesses the agility to adapt to any data/service scenario and fully supports CRUD data operations. It is also highly configurable and provides tons of features for fine-tuning its behavior. This is also the main reason why consuming a GraphQL API is so easy with the DataSource abstraction.

Setup a GraphQL Service

First things first – we need to create a GraphQL service that can receive queries and mutations to validate and execute:

First, start by defining a type which describes the possible data that can be queried on the service:

import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLID,
  GraphQLFloat,
} from 'graphql';

module.exports = new GraphQLObjectType({
  name: 'Product',
  fields: () => ({
    ProductID: { type: GraphQLID },
    ProductName: { type: GraphQLString },
    UnitPrice: { type: GraphQLFloat },
    UnitsInStock: { type: GraphQLFloat }
  })
});

Next, create queries for fetching the data and mutations for modifying the data server-side:

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    products: {
      type: new GraphQLList(Product),
      resolve(parent, args) {
        return products;
      }
    }
  }
});

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    AddProduct: {
      type: Product,
      args: {
        ProductName: { type: new GraphQLNonNull(GraphQLString) },
        UnitPrice: { type: new GraphQLNonNull(GraphQLFloat) },
        UnitsInStock: { type: new GraphQLNonNull(GraphQLFloat) }
      },
      resolve(parent, args) {
        let newProduct = {
          ProductID: uuidv1(),
          ProductName: args.ProductName,
          UnitsInStock: args.UnitsInStock,
          UnitPrice: args.UnitPrice
        }
        products.unshift(newProduct);

        return newProduct;
      }
    },
    UpdateProduct: {
      type: Product,
      args: {
        ProductID: { type: new GraphQLNonNull(GraphQLID) },
        ProductName: { type: new GraphQLNonNull(GraphQLString) },
        UnitPrice: { type: new GraphQLNonNull(GraphQLFloat) },
        UnitsInStock: { type: new GraphQLNonNull(GraphQLFloat) }
      },
      resolve(parent, args) {
        let index = products.findIndex(product => product.ProductID == args.ProductID);
        let product = products[index];

        product.ProductName = args.ProductName;
        product.UnitsInStock = args.UnitsInStock;
        product.UnitPrice = args.UnitPrice;

        return product;
      }
    },
    DeleteProduct: {
      type: Product,
      args: {
        ProductID: { type: new GraphQLNonNull(GraphQLID) }
      },
      resolve(parent, args) {
        let index = products.findIndex(product => product.ProductID == args.ProductID);
        products.splice(index, 1);

        return { ProductID: args.ProductID };
      }
    }
  }
});

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation
});

Then, serve the GraphQL service over HTTP via a single endpoint which expresses the full set of its capabilities:

import express from 'express';
import cors from 'cors';
import graphqlHTTP from 'express-graphql';
import schema from './schema';
import { createServer } from 'http';

const PORT = 3021;

var app = express();

app.use(cors());

app.use('/graphql', graphqlHTTP({
  schema
}));

const server = createServer(app);

server.listen(PORT, () => {
  console.log(`API Server is now running on http://localhost:${PORT}/graphql`)
});

For additional information on how to setup the server, required packages and the full GraphQL schema, refer to the source code of the sample application.

Configuring the jQuery Grid

To be able to use the Kendo UI jQuery Grid component, just reference the required client-side resources of the framework:

<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.default-v2.min.css" />
<script src="https://kendo.cdn.telerik.com/2018.2.620/js/jquery.min.js"></script>
<script src="https://kendo.cdn.telerik.com/2018.2.620/js/kendo.all.min.js"></script>

Once you have the right setup, adding the Grid is as simple as placing a container element on the page and then initializing the widget with JavaScript:

<div id="content">
  <div id="grid"></div>
</div>

<script>
  $(document).ready(function () {
    $("#grid").kendoGrid({
      dataSource: dataSource,
      height: 550,
      groupable: true,
      sortable: true,
      pageable: true,
      toolbar: ["create"],
      editable: "inline",
      columns: [
        { field: "ProductID", title: "Product ID" },
        { field: "ProductName", title: "Product Name" },
        { field: "UnitPrice", title: "Unit Price" },
        { field: "UnitsInStock", title: "Units in stock" },
        { command: ["edit", "destroy"], title: "Options ", width: "250px" }
      ]
    });
  });
</script>

Using GraphQL on the Client

The wealth of configuration options that the DataSource offers allows you to easily integrate it to work with a GraphQL API and bind the Grid Component to it.

Compose Queries and Mutations

GraphQL is all about asking for specific fields on objects. To populate the Grid initially with records, we need to issue a query against the API to return the object types:

<script>
  var READ_PRODUCTS_QUERY = "query { products { ProductID, ProductName, UnitPrice, UnitsInStock } }";
</script>

And then create the mutations for adding, updating and deleting the object type:

<script>
  var ADD_PRODUCT_QUERY = "mutation AddProduct($ProductName: String!, $UnitPrice: Float!, $UnitsInStock: Float!){" +
  "AddProduct(ProductName: $ProductName, UnitPrice: $UnitPrice, UnitsInStock: $UnitsInStock ){" +
  "ProductID,"+
  "ProductName,"+
  "UnitPrice,"+
  "UnitsInStock"+
  "}"+
  "}";
  var UPDATE_PRODUCT_QUERY = "mutation UpdateProduct($ProductID: ID!, $ProductName: String! ,$UnitPrice: Float!, $UnitsInStock: Float!){" +
  "UpdateProduct(ProductID: $ProductID, ProductName: $ProductName, UnitPrice: $UnitPrice, UnitsInStock: $UnitsInStock){" +
  "ProductID," +
  "ProductName," +
  "UnitPrice," +
  "UnitsInStock" +
  "}" +
  "}";
  var DELETE_PRODUCT_QUERY = "mutation DeleteProduct($ProductID: ID!){" +
  "DeleteProduct(ProductID: $ProductID){" +
  "ProductID" +
  "}" +
  "}";
</script>

Consume the API

To request or modify data through a GraphQL query or mutation, all you have to do is to configure the transport.read method of the DataSource:

  • Set the content-type to "application/json."
  • Set the request type to "POST."
  • Pass the composed GraphQL query/mutation as "query" and the model data as "variables" parameters along with the requests to the remote service when performing CRUD operations. This is achieved through the transport.read's data() method.
var dataSource = new kendo.data.DataSource({
  pageSize: 20,
  transport: {
    read: {
      contentType: "application/json",
      url: "http://localhost:3021/graphql",
      type: "POST",
      data: function () {
        return { query: READ_PRODUCTS_QUERY };
      }
    },
    update: {
      contentType: "application/json",
      url: "http://localhost:3021/graphql",
      type: "POST",
      data: function (model) {
        return {
          query: UPDATE_PRODUCT_QUERY,
          variables: model
        };
      }
    },
    destroy: {
      contentType: "application/json",
      url: "http://localhost:3021/graphql",
      type: "POST",
      data: function (model) {
        return {
          query: DELETE_PRODUCT_QUERY,
          variables: model
        };
      }
    },
    create: {
      contentType: "application/json",
      url: "http://localhost:3021/graphql",
      type: "POST",
      data: function (model) {
        return {
          query: ADD_PRODUCT_QUERY,
          variables: model
        };
      }
    },
    parameterMap: function (options, operation) {
      return kendo.stringify(options);
    }
  },
  schema: {
    data: function (response) {
      var data = response.data;
      if (data.products) {
        return data.products;
      } else if (data.AddProduct) {
        return data.AddProduct;
      } else if (data.UpdateProduct) {
        return data.UpdateProduct;
      } else if (data.DeleteProduct) {
        return data.DeleteProduct;
      }
    },
    model: {
      id: "ProductID",
      fields: {
        ProductID: { type: "string", editable: false },
        ProductName: { type: "string" },
        UnitPrice: { type: "number" },
        UnitsInStock: { type: "number" }
      }
    }
  }
});

Format Request Parameters and Parse the Response

Beyond configuring the transports, there are also other features of the DataSource, like the parameterMap() and the schema options, that are useful for encoding the request parameters and parsing the API response:

Start Exploring the Grid

There you go – by simply setting up the DataSource we got the Grid up and running with a GraphQL API. From here on, you can start exploring the vast options of the Grid and also take advantage of the other 70+ ready-to-use Kendo UI components and easily bind them to a GraphQL service. Get started right away by downloading a free trial of Kendo UI.

Start My Kendo UI Trial

Additional Resources


Dimitar Kalaydzhiev
About the Author

Dimitar Kalaydzhiev

Dimitar is a Technical Support Engineer at Progress. He is passionate about programming and experimenting with new technologies. In his spare time, he enjoys traveling, hiking and photography.

Related Posts

Comments

Comments are disabled in preview mode.