Friday, July 3, 2020

Building and consuming GraphQL API in ASP.NET Core 3.1

It was in the year 2012 that Facebook decided to rebuild their apps to improve performance and efficiency. It was a time when Facebook’s mobile strategy wasn’t working because of high network usage. Optimization strategies using caching might have improved the performance, but since the app was too complex, it was thought that the data fetching strategy itself should be changed. Here’s where GraphQL came in, and today it has become extremely popular among the development community worldwide. GraphQL, developed by Facebook in 2012 and open-sourced 2015, is now maintained by the GraphQL Foundation.

GraphQL is a platform-agnostic, language-neutral query language that has been around for quite some time now and can be used to execute queries and fetch data. Similar to REST, GraphQL is a specification that provides an elegant and flexible way to query your data. This article discusses the features and benefits of GraphQL and then illustrates how one can work with GraphQL in ASP.NET Core 3.1.

Prerequisites

To work with the code examples illustrated in this article, you should have Visual Studio 2019 installed on your computer. If you don’t have a copy of it yet, you can grab one from here. If you don’t have .NET Core installed in your system, you can download a copy from here.

Why do you need GraphQL?

GraphQL is a JSON-like query language for APIs as well as a server-side runtime for executing your queries. Unlike REST where the request and response are defined by the API, in GraphQL, the client has complete control on what data the API should return. You can integrate GraphQL with ASP.NET, ASP.NET Core, Java, etc.

If you are working on an application that leverages RESTful architecture, the endpoints might grow over time and maintaining them might become a nightmare. On the contrary, with GraphQL, you just need one endpoint api/graphql, and that’s all. This is another significant difference between REST and GraphQL.

In using GraphQL you need fewer roundtrips to the server, i.e., you need fewer back-and-forth calls to the server to get all the data you need. With REST, you will have several endpoints such as api/students, api/teachers, api/batches, etc.

Unlike REST, when using GraphQL, you will never have too little or too much data – you can define your queries and all data you need.

When using GraphQL, you need not worry about versioning. The fact is that GraphQL doesn’t need versioning and as long as you don’t delete fields from the types, the clients or consumers of the API wouldn’t break.

When using GraphQL, you would typically need fewer requests and less bandwidth. You need just a single call to the API to get the data you need. Moreover, you can specify the fields you need when making a request. Instead of returning all fields for a type, you can retrieve only what you need hence saving on bandwidth and resource usage.

Similar to Swagger that generates documentation for the REST endpoints, GraphQL can also generate documentation for the GraphQL endpoint.

Albeit all the advantages GraphQL has to offer, there are a few downsides as well. You can read about those by clicking the link.

GraphQL vs REST

Here’s a quick look at how GraphQL compares with REST:

  1. Unlike REST where you might need to have several endpoints to get you the data you need, GraphQL exposes only a single endpoint.
  2. REST works on Http only while GraphQL doesn’t need Http to work.
  3. Unlike REST where you can use any Http verb, GraphQL recommends using only the Http POST verb
  4. Unlike REST where the request and response are defined by the API, the clients or the consumers of GraphQL can define what data they need. While the size of the resource is determined by the server when working with REST, in GraphQL, the API defines the available resources, and the client requests only what it needs.
  5. Both REST and GraphQL are platform and language agnostic, and both are adept at returning JSON.

Building Blocks of GraphQL

The main building blocks of GraphQL include schemas and types.

Schema

There is one, and only one endpoint in GraphQL. This endpoint exposes a schema that is used to let the API consumer know the functionality available for the clients to consume, i.e., what data they can expect and the actions they can perform. A Schema in GraphQL is represented by a class that extends the Schema class pertaining to the GraphQL.Types namespace.

A schema contains a Query, Mutation, and a Subscription.

  1. Query – Queries enable you to consume data efficiently. The consumer or the client can mention the field or fields it needs in lieu of getting data for all fields from a particular type. Note that the client can only consume the fields that have been exposed by the API.
  2. Mutation – In GraphQL mutations are used to send data to the server, i.e., you can take advantage of mutations to add, edit, or delete data. The client can only take advantage of the mutations that have been exposed by the schema to modify the data. It should be noted here that if there are no mutations in a GraphQL schema, the client cannot manipulate the data in the API.
  3. Subscription – Subscriptions allow a server to send data to its clients, hence notifying them when events occur. Subscriptions provide support for event-driven architectures and for real-time notifications they take advantage of WebSockets.

GraphQL Object Types

The most fundamental components of a GraphQL schema are object types, which in turn are used to represent the type of object you can retrieve from your API. Object Types in GraphQL are represented by the GraphQL.Types.ObjectGraphType class and contain Fields and Methods. While the former is a property in the class, the latter is used to modify field values when needed based on a client query.

Getting Started: Configuring the GraphQL Middleware

First off, create a new ASP.NET Core 3.1 MVC project in Visual Studio 2019. Next, follow the steps outlined in the next section to install the necessary NuGet packages.

Install NuGet Packages

Since support for GraphQL is not in-built in ASP.NET Core, you’ll need to install the necessary NuGet packages via the NuGet Package Manager or the NuGet Package Manager Console.

To install the required packages, run the following commands at the NuGet Package Manager Console Window:

Install-Package GraphQL
Install-Package GraphiQL

Run the application now and browse the /graphql endpoint. Here’s how the output will look in the web browser.

Configuring the GraphQL Middleware

Once you’ve installed the necessary NuGet packages, you should add GraphiQL to your ASP.NET Core application by specifying the following in the Configure method of the Startup class.

app.UseGraphiQl("/graphql");

To use the above extension method, you should add the following using directive as well.

using GraphiQl;

You should also add the services mentioned below. Don’t be confused looking at these types – you’ll implement each of these types (classes and interfaces) later in this article.

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IDependencyResolver>(_ => new 
    FuncDependencyResolver(_.GetRequiredService));
    services.AddScoped<IDocumentExecuter, DocumentExecuter>();
    services.AddScoped<IDocumentWriter, DocumentWriter>();
    services.AddScoped<AuthorService>();
    services.AddScoped<AuthorRepository>();
    services.AddScoped<AuthorQuery>();
    services.AddScoped<AuthorType>();
    services.AddScoped<BlogPostType>();
    services.AddScoped<ISchema, GraphQLDemoSchema>();
    services.AddControllers();
}

To avoid compilation errors, you may want to comment out this code until all these types have been implemented. You’ll also need to be sure to add using statements to get rid of the errors. This program needs GraphQL.Types, GraphQL, your Types folder, and GraphQL.HTTP.

Building the Object Model

First off, create the model classes or the entities. This example contains two entities named Author and BlogPost. Create a solution folder called Models; this is where the entity classes will go. Inside this folder, create two .cs files named Author.cs and BlogPost.cs with the following content.

Author.cs

public class Author
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

BlogPost.cs

public class BlogPost
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public Author Author { get; set; }
    }

The BlogPost class contains a reference to the Author class. Hence a BlogPost can be written by only one Author, but an Author can write many blog posts.

Building GraphQL Schema

Since GraphQL is not bound to any language or framework, in particular, it is not adept at understanding the CLR classes, i.e., C# POCO classes. Hence, to query data using GraphQL, you should create a type that extends ObjectGraphType<T> and pass the entity type as an argument. You should also register the properties of the class as “Field” types so that GraphQL can recognize this type.

Create a folder called Types and create the following two classes in the files AuthorType.cs and BlogPostType.cs files, respectively.

AuthorType.cs

public class AuthorType : ObjectGraphType<Author>
    {
        public AuthorType()
        {
            Name = "Author";
            Field(_ => _.Id).Description("Author's Id.");
            Field(_ => _.FirstName).Description
            ("First name of the author");
            Field(_ => _.LastName).Description
            ("Last name of the author");
        }
    }

BlogPostType.cs

public class BlogPostType : ObjectGraphType<BlogPost>
    {
        public BlogPostType()
        {
            Name = "BlogPost";
            Field(_ => _.Id, type: 
            typeof(IdGraphType)).Description
           ("The Id of the Blog post.");
            Field(_ => _.Title).Description
            ("The title of the blog post.");
            Field(_ => _.Content).Description
            ("The content of the blog post.");
        }
    }

You also need a class that would fetch author and blog post related data. To do this, create a file called AuthorQuery.cs with the following content inside. Make sure to include using GraphQL.Types. You’ll need this using statement in many other classes as well.

AuthorQuery.cs

public class AuthorQuery : ObjectGraphType
    {
        public AuthorQuery(AuthorService authorService)
        {
            int id = 0;
            Field<ListGraphType<AuthorType>>(
            name:"authors", resolve: context =>
            {
                return authorService.GetAllAuthors();
            });
            Field<AuthorType>(
                name: "author",
                arguments: new QueryArguments(new 
                QueryArgument<IntGraphType> { Name = "id" }),
                resolve: context =>
                {
                    id = context.GetArgument<int>("id");
                    return authorService.GetAuthorById(id);
                }
            );
            Field<ListGraphType<BlogPostType>>(
                name: "blogs",
                arguments: new QueryArguments(new 
                QueryArgument<IntGraphType> { Name = "id" }),
                resolve: context =>
                {
                    return authorService.GetPostsByAuthor(id);
                }
            );
        }
    }

When working with GraphQL, the client will always make an HTTP POST call which would, in turn, contain the query name, name of the operation and the variables. Now create a POCO class which would be used as a model for managing schema, variables and the arguments. You can create this file inside the Controllers folder in the project.

GraphQLQueryDTO.cs

public class GraphQLQueryDTO
    {
        public string OperationName { get; set; }
        public string NamedQuery { get; set; }
        public string Query { get; set; }
        public string Variables { get; set; }
}

 

Next, you should create a GraphQL schema that extends the GraphQL.Types.Schema class and the GraphQL.Types.ISchema interface, as shown below.

public class GraphQLDemoSchema:Schema, ISchema
    {
        public GraphQLDemoSchema(IDependencyResolver 
        resolver):base(resolver)
        {
            Query = resolver.Resolve<AuthorQuery>();
        }
    }

This is needed to make your query known and available to GraphQL. Note that this class should accept the DependencyResolver that is added to the services collection in the ConfigureServices method of the Startup class. Here’s where you can specify Query, Mutation and Subscription – I’ve discussed each of these earlier in this article. For the sake of simplicity, the example specifies only the Query here.

Create the GraphQL API EndPoint

Next, you should create the API Endpoint with just one action method. The following is the complete source of the GraphQLController class.

[Route("graphql")]
    public class GraphQLController : Controller
    {
        private readonly ISchema _schema;
        private readonly IDocumentExecuter _executer;
        public GraphQLController(ISchema schema, 
        IDocumentExecuter executer)
        {
            _schema = schema;
            _executer = executer;
        }
        [HttpPost]
        public async Task<IActionResult> Post([FromBody] 
        GraphQLQueryDTO query)
        {
            var result = await _executer.ExecuteAsync(_ =>
            {
                _.Schema = _schema;
                _.Query = query.Query;
                _.Inputs = query.Variables?.ToInputs();
                
            });
            if(result.Errors?.Count > 0)
            {
                return BadRequest();
            }
            return Ok(result.Data);
        }
    }

The AuthorService class encapsulates calls to the AuthorRepository class.

public class AuthorService
    {
        private readonly AuthorRepository _authorRepository;
        
public AuthorService(AuthorRepository 
        authorRepository)
        {
            _authorRepository = authorRepository;
        }
        public List<Author> GetAllAuthors()
        {
            return _authorRepository.GetAllAuthors();
        }
        public Author GetAuthorById(int id)
        {
            return _authorRepository.GetAuthorById(id);
        }
        public List<BlogPost> GetPostsByAuthor(int id)
        {
            return _authorRepository.GetPostsByAuthor(id);
        }
    }

The AuthorRepository class

The AuthorRepository class handles data – it contains the necessary methods to fetch data. Note the usage of the list of authors and blog posts. These collections are initialized in the constructor.

AuthorRepository.cs

public class AuthorRepository
    {
        private readonly List<Author> authors = 
        new List<Author>();
        private readonly List<BlogPost> posts = 
        new List<BlogPost>();
        
public AuthorRepository()
        {
            Author author1 = new Author
            {
                Id = 1,
                FirstName = "Joydip",
                LastName = "Kanjilal"
            };
            Author author2 = new Author
            {
                Id = 2,
                FirstName = "Steve",
                LastName = "Smith"
            };
            BlogPost csharp = new BlogPost
            {
                Id = 1,
                Title = "Mastering C#",
                Content = "This is a series of articles 
                on C#.",
                Author = author1
            };
            BlogPost java = new BlogPost
            {
                Id = 2,
                Title = "Mastering Java",
                Content = "This is a series of articles 
                on Java",
                Author = author1
            };
            posts.Add(csharp);
            posts.Add(java);
            authors.Add(author1);
            authors.Add(author2);
        }
        public List<Author> GetAllAuthors()
        {
            return this.authors;
        }
        public Author GetAuthorById(int id)
        {
            return authors.Where(author => author.Id == 
            id).FirstOrDefault<Author>();
        }
        public List<BlogPost> GetPostsByAuthor(int id)
        {
            return posts.Where(post => post.Author.Id == 
            id).ToList<BlogPost>();
        }
    }

Here’s an example of a GraphQL query.

query {
  author (id: 1){
    id
    firstName
    lastName
  }
   blogs
    {
      id
      title
      content
    }
}

GraphQL in Action!

Here’s an example of a GraphQL query you can use to get the data about all authors.

query {
  authors{
    id
    firstName
    lastName
  }
}

When you execute this query, here’s how the output would look in the GraphiQL tool.

If you would like to get the data about a particular author together with the blogs they have written, you can take advantage of the following query instead.

query {
  author (id: 1){
    id
    firstName
    lastName
  }
  blogs
    {
      id
      title
      content
    }
}

Now run the application, browse to the graphql endpoint and run the query. Figure 2 below shows the output in the GraphiQL tool.

Summary

GraphQL, a new API standard open-sourced by Facebook, is a flexible and powerful alternative to REST. GraphQL provides excellent support for declarative data fetching where the consumer can specify the exact data it needs from an API, i.e., you’re the owner of the data you want to get from the API.

The post Building and consuming GraphQL API in ASP.NET Core 3.1 appeared first on Simple Talk.



from Simple Talk https://ift.tt/3gm52SW
via

No comments:

Post a Comment