When you build an api service, do you feel boring when repeatedly creating the same simple actions: get all, insert, update, delete, and each query type, you have to create a specific action? I'm really tired of that. That's why I rely on GraphQL, I try to build a library that helps to speed up that works. With this library, you can freely query from client with Linq and built-in support GetAll, paging, GetById, insert, update, delete. Of course, you can override the default action for the complex case. I don't explain detail about GraphQL, you can easy find the clearly documentation at link https://graphql.org/, https://graphql-dotnet.github.io/
This library requires .NET Core 3.x, Entity Framework Core that can integrated with any SQL database you want.
Install-Package GraphQLDoorNet
dotnet add package GraphQLDoorNet
- Implement for IRepository and IUnitOfWork (If you prefer using PostgreSQL, i suggest library EFPostgresEngagement, it helps integration Entity Framework Core and PostgreSQL in some line of code)
- Make sure you already registered IUnitOfWork in container, i recommend you should use transient, it will helpful for data consistency in asynchronous
services.AddTransient<GraphQLDoorNet.Abstracts.IUnitOfWork, GraphQLUnitOfWork>();
- Create the Query class and let's it inherits IQuery
internal sealed class Query: IQuery
{
}
- Create the Mutation class and let's it inherits IMutation
internal sealed class Mutation : IMutation
{
}
- Create a controller as a door between GraphQL and database. This is only an example, you can modify anything as you want
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using GraphQLDoorNet.Abstracts;
[Route("api/graphql")]
[ApiController]
public class GraphQLController : ControllerBase
{
private readonly IResolver graphqlResolver;
public GraphQLController(IResolver resolver)
{
this.graphqlResolver = resolver;
}
[HttpPost]
public async Task<IActionResult> Post()
{
var response = await this.graphqlResolver.Resolve(this.Request.Body);
return new ContentResult
{
Content = response.Content,
ContentType = "application/json; charset=utf-8",
StatusCode = response.StatusCode
};
}
}
- In Startup.cs, add the following code to ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddGraphQLServices<Query, Mutation>();
...
}
Finally, you completed the initialization for this library. Next step, let's explore how to use it.
Let's example, we develop a service to manage project. We have Project table and client need to CRUD on this table. To support that actions with this library, you follows the step:
- Open the Query class, add a Query object for Project table.
internal sealed class Query: IQuery
{
public EntityQueryBase<Project> Project([Inject] EntityQueryBase<Project> entityQuery) => entityQuery;
}
The EntityQueryBase supports actions: QueryMany, GetById, QueryOne, Count. The details will be explained below. This class is freely overrided.
- For the CUD, it is mutation behaviors, these actions need the separate models rather than the root entities. So, let's create an input model for Project entity
[InputType(EntityType = typeof(Project))]
public class ProjectInput: OptionalWrapper
{
public string Name { get; set; }
public string Code { get; set; }
}
- Open the Mutation class, add an Mutation object for Project table
internal sealed class Mutation : IMutation
{
public EntityMutationBase<Project, ProjectInput> Project(
[Inject] EntityMutationBase<Project, ProjectInput> entityMutation) =>
entityMutation;
}
- DONE!
Let's try to query by Postman. The endpoint should be: https://your-domain/api/graphql
You can easily replace Project with other entity, queryMany with other action - please explore more action in EntityQueryBase
You can easily replace Project with other entity, add with other action - please explore more action in EntityMutationBase.
In the real world, you may need to override the default CUD or add more mutation. Just create a new mutation and inherit EntityMutationBase, then add it to the Mutation
internal sealed class Mutation : IMutation
{
public ProjectMutation Project([Inject] ProjectMutation entityMutation) => entityMutation;
}
Example, ProjectMutation has a custom action that EstablishProduct. Please don't forget to mark the Mutation attribute for the custom mutation.
[Mutation]
public class ProjectMutation : EntityMutationBase<Project, ProjectInput>
{
public ProjectMutation(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
public async Task<MutationResult> EstablishDatabase(ProjectDatabaseEstablishInput input)
{
...
}
}