GraphQL In .NET Core Web API With Entity Framework Core - Part One

GraphQL In .NET Core Web API With Entity Framework Core - Part One

Let’s first understand why we use GraphQL when we directly expose data through a Web API endpoint. Consider a scenario where you want to create endpoints for the User Entity. So, you can expose the API like GetAllUsers, GetUsersByUserId, and GetUserByUserName in order to return limited fields like UserName and UserId, not the entire User entity. So again, you need to expose other endpoints and ultimately, we will end up with n number of endpoints which is very difficult to maintain. GraphQL helps us here as it’s a schema definition language. You need to expose only one endpoint which expects the GraphQL query and you can get your desired results with a single endpoint.

Let’s see step by step implementation of GraphQL in Web API.

Create an ASP.NET Core web application and select API as template and Core 2.0 as version

In our demo, we need GraphQL as well as Entity Framework Core, so add the NuGet Packages mentioned below.

  • GraphQL
  • GraphQL.Server.Transports.AspnetCore
  • GraphQL.Server.UI.Playground
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Now, let’s create a database in MS SQL Server for our demo purposes. Also, create a table and add some dummy data. You can execute the below script in order to create a table with the data inside the GraphQLDemo database.

USE [GraphQLDemo]  
GO  
/****** Object:  Table [dbo].[Employee]    Script Date: 2/2/2019 9:15:36 PM ******/  
SET ANSI_NULLS ON  
GO  
SET QUOTED_IDENTIFIER ON  
GO  
CREATE TABLE [dbo].[Employee](  
    [Id] [bigint] IDENTITY(1,1) NOT NULL,  
    [Name] [varchar](100) NULL,  
    [Email] [varchar](50) NULL,  
    [Mobile] [varchar](50) NULL,  
    [Company] [varchar](100) NULL,  
    [Address] [varchar](100) NULL,  
    [ShortDescription] [varchar](1000) NULL,  
    [LongDescription] [text] NULL,  
 CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED   
(  
    [Id] ASC  
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]  
GO  
SET IDENTITY_INSERT [dbo].[Employee] ON   
GO  
INSERT [dbo].[Employee] ([Id], [Name], [Email], [Mobile], [Company], [Address], [ShortDescription], [LongDescription]) VALUES (1, N'Akshay', N'akshayblevel@gmail.com', N'9999999999', N'Dotnetbees', N'Hyderabad', N'Short Description', N'Long Description')  
GO  
INSERT [dbo].[Employee] ([Id], [Name], [Email], [Mobile], [Company], [Address], [ShortDescription], [LongDescription]) VALUES (2, N'Panth', N'panth@gmail.com', N'8888888888', N'Radix', N'Vadodara', N'SD', N'LD')  
GO  
SET IDENTITY_INSERT [dbo].[Employee] OFF  
GO

In the next step, we need a connection string to connect with the database. So, as a normal practice, let’s store our connection string in Appsettings.json.

Appsettings.json

"ConnectionStrings": {  
    "DefaultConnection": "Server=akshay-pc\\devsql2016;Database=GraphQLDemo;Trusted_Connection=True;MultipleActiveResultSets=true"  
  }

Run the scaffold command to generate the model and dbcontext class.

Open the Package Manager Console and execute the below command.

Scaffold-DbContext “Server=AKSHAY-PC\DEVSQL2016;Database=GraphQLDemo;Trusted_Connection=True;” Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

Employee.cs

namespace GraphQLInWebApiCore  
{  
    public partial class Employee  
    {  
        public long Id { get; set; }  
        public string Name { get; set; }  
        public string Email { get; set; }  
        public string Mobile { get; set; }  
        public string Company { get; set; }  
        public string Address { get; set; }  
        public string ShortDescription { get; set; }  
        public string LongDescription { get; set; }  
    }  
}

GraphQLDemoContext

using Microsoft.EntityFrameworkCore;  

namespace GraphQLInWebApiCore  
{  
    public partial class GraphQLDemoContext : DbContext  
    {  
        public GraphQLDemoContext()  
        {  
        }  

        public GraphQLDemoContext(DbContextOptions<GraphQLDemoContext> options)  
            : base(options)  
        {  
        }  

        public virtual DbSet<Employee> Employee { get; set; }  
    }  
}

Add the DbContext under ConfigureServices in Startup.cs, which adds into the service collection and can be injected whenever required.

Startup.cs

services.AddDbContext<GraphQLDemoContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")

Create an employee repository to get all the employees from the database through dbcontext, i.e., Entitiy Framework Core.

IEmployeeRepository.cs

using System.Collections.Generic;  
using System.Threading.Tasks;  

namespace GraphQLInWebApiCore  
{  
    public interface IEmployeeRepository  
    {  
        Task<List<Employee>> GetEmployees();  
    }  
}

EmployeeRepository.cs

using Microsoft.EntityFrameworkCore;  
using System.Collections.Generic;  
using System.Linq;  
using System.Threading.Tasks;  

namespace GraphQLInWebApiCore  
{  
    public class EmployeeRepository : IEmployeeRepository  
    {  
        private readonly GraphQLDemoContext _context;  
        public EmployeeRepository(GraphQLDemoContext context)  
        {  
            _context = context;  
        }  

        public Task<List<Employee>> GetEmployees()  
        {  
            return _context.Employee.ToListAsync();  
        }  
    }  
}

So far, we are done with -

  • Creating a table in DB
  • Adding sample data into the table
  • Configuring Entity Framework Core and setting the connection with a database
  • Creating a repository which will return the list of employees

Let’s start implementing GraphQL. This requires Schema, Query, and ObjectGraphType.

image.png

We need to create EmployeeType which inherits ObjectGraphType and set all the fields of an employee in the constructor.

using GraphQL.Types;  

namespace GraphQLInWebApiCore  
{  
    public class EmployeeType : ObjectGraphType<Employee>  
    {  
        public EmployeeType()  
        {  
            Field(a => a.Id);  
            Field(a => a.Name);  
            Field(a => a.Email);  
            Field(a => a.Mobile);  
            Field(a => a.Company);  
            Field(a => a.Address);  
            Field(a => a.ShortDescription);  
            Field(a => a.LongDescription);  
        }  
    }  
}

Create a query which will set the field of the previously created object graph type and internally fetch the data from the repository and resolve the context.

using GraphQL.Types;  

namespace GraphQLInWebApiCore  
{  
    public class EmployeeQuery : ObjectGraphType  
    {  
        public EmployeeQuery(IEmployeeRepository employeeRepository)  
        {  
            Field<ListGraphType<EmployeeType>>(  
                "employees",  
                resolve: context => employeeRepository.GetEmployees()  
                );  
        }  
    }  
}

Schema will be used to resolve the query. We will resolve the previously created query; i.e., EmployeeQuery.

using GraphQL;  
using GraphQL.Types;  

namespace GraphQLInWebApiCore  
{  
    public class EmployeeSchema : Schema  
    {  
        public EmployeeSchema(IDependencyResolver resolver) : base(resolver)  
        {  
            Query = resolver.Resolve<EmployeeQuery>();  
        }  
    }  
}

Add the below code snippet under configureservices in startup.cs which will be used to resolve the dependency. That means we are adding interfaces and the concrete implementation against each interface inside the service collection.

    services.AddScoped<IEmployeeRepository, EmployeeRepository>();  
    //GraphQL configuration  
           services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));  
           services.AddScoped<EmployeeSchema>();  
           services.AddGraphQL(o => { o.ExposeExceptions = false; })  
            .AddGraphTypes(ServiceLifetime.Scoped);

Add the below code under configure method in startup.cs. With UseGraphQL method, we can set the path to access GraphQL in the parameter. But if you don’t set it, it will consider the /graphql path.

app.UseGraphQL<EmployeeSchema>();  
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());

At the starting stage, we added a NuGet package called GraphQL.Server.UI.Playground which will provide the UI where we can write our GraphQL queries and at the same time, we can see the result. For that, we need to browse the ui/playground path. We can configure the same at the launch of the browser.

image.png

Now, run the application and you can see that the UI is rendered in the browser where on the left side, we have a query input section and at the right side, the result section. In middle, we have an "Execute" button to execute the query. If you click on the Schema button which is in the extreme right side of the browser, you can see the query and graph object type. This detail will help to write the query.

image.png

As soon as you start writing a query, IntelliSense will suggest query and object graph type that we have created.

image.png

image.png

Click the "Run" button and you can see the result; i.e., the list of employees on the right side. If you observe the query and result, we asked for the name and email fields of employees and we get the same as the result. This means we can pass a response parameter which we would like to return as a result and we will get that only.

image.png

In the object graph type, we can add the description as well so that a user can understand the field better. Here, we have added the description with company field.

using GraphQL.Types;  

namespace GraphQLInWebApiCore  
{  
    public class EmployeeType : ObjectGraphType<Employee>  
    {  
        public EmployeeType()  
        {  
            Field(a => a.Id);  
            Field(a => a.Name);  
            Field(a => a.Email);  
            Field(a => a.Mobile);  
            Field(a => a.Company).Description("Company Name");  
            Field(a => a.Address);  
            Field(a => a.ShortDescription);  
            Field(a => a.LongDescription);  
        }  
    }  
}

Once you run the application, in the playground UI, you can see the description for that particular field.

image.png

Now, we might have a question about how we can use GraphQL inside the API and expose through the endpoint.

In order to use GraphQL, we need to add the GraphQL.Client NuGet package.

Create an Employee controller and create one GET method inside that.

Initialize GraphQLClient with GraphQL URL

Create an instance of GraphQLRequest and write the same query which we tried in playground UI.

Call PostAsync method of GraphQLClient and you will get the response.

Convert that response to List of Employees and return it.

using GraphQL.Client;  
using GraphQL.Common.Request;  
using Microsoft.AspNetCore.Mvc;  
using System.Collections.Generic;   
using System.Threading.Tasks;  

namespace GraphQLGraphTypeFirstSingleTable.Controllers  
{  
    [Route("Employee")]  
    public class EmployeeController : Controller  
    {  
        [HttpGet]  
        public async Task<List<Employee>> Get()  
        {  
            using (GraphQLClient graphQLClient = new GraphQLClient("http://localhost:64034/graphql"))  
            {  
                var query = new GraphQLRequest  
                {  
                    Query = @"   
                        { employees   
                            { name email }   
                        }",  
                };  
                var response = await graphQLClient.PostAsync(query);  
                return response.GetDataFieldAs<List<Employee>>("employees");   
            }  
        }  
    }  
}

Run the application and open Postman to verify preceding API. Select GET as the method and add the API URL to call the Employee controller GET method.

Click on the "Send" button and you can see the list of employees.

image.png

In this article, we saw how to use GraphQL with one table without argument. In the next part, we will see how we can pass the argument in GraphQL query.

You can download the sample from here.