Skip to content

Commit

Permalink
#19 get graphql working via the extension method
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian-Webster committed Dec 6, 2023
1 parent fd0a791 commit d9074be
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 114 deletions.
18 changes: 18 additions & 0 deletions Example/DataAccess.Example.Data/Queries/BookQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using HotChocolate;
using HotChocolate.Types;
using HotChocolate.Resolvers;
using HotChocolate.Types.Pagination;

namespace DataAccess.Example.Data.Queries;

Expand All @@ -15,4 +16,21 @@ public class BookQuery
{
return await repository.GetBookForGraphQuery(context, token);
}

[UseProjection]
[UseFiltering]
[UseSorting]
public async Task<IEnumerable<Book>?> GetBooks([Service] IBookRepository repository, IResolverContext context, CancellationToken token)
{
return await repository.GetBooksForGraphQuery(context, token);
}

[UsePaging(IncludeTotalCount = true)]
[UseProjection]
[UseFiltering]
[UseSorting]
public async Task<Connection<Book>> GetPagedBooks([Service] IBookRepository repository, IResolverContext context, CancellationToken token)
{
return await repository.GetPagedBooksForGraphQuery(context, token);
}
}
11 changes: 11 additions & 0 deletions Example/DataAccess.Example.Data/Repositories/BookRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using DataAccess.Repository;
using DataAccess.Repository.HotChocolate;
using HotChocolate.Resolvers;
using HotChocolate.Types.Pagination;

namespace DataAccess.Example.Data.Repositories;

Expand Down Expand Up @@ -62,4 +63,14 @@ public async Task<bool> RemoveBook(Guid bookId, CancellationToken token)
{
return await _bookRepo.GetQueryItem(context, token);
}

public async Task<IEnumerable<Book>?> GetBooksForGraphQuery(IResolverContext context, CancellationToken token)
{
return await _bookRepo.GetQueryItems(context, token);
}

public async Task<Connection<Book>> GetPagedBooksForGraphQuery(IResolverContext context, CancellationToken token)
{
return await _bookRepo.GetPagedQueryItems(context, token);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using DataAccess.Example.Data.Entities;
using HotChocolate.Resolvers;
using HotChocolate.Types.Pagination;

namespace DataAccess.Example.Data.Repositories;

Expand All @@ -16,4 +17,8 @@ public interface IBookRepository
Task<bool> RemoveBook(Guid bookId, CancellationToken token);

Task<Book?> GetBookForGraphQuery(IResolverContext context, CancellationToken token);

Task<IEnumerable<Book>?> GetBooksForGraphQuery(IResolverContext context, CancellationToken token);

Task<Connection<Book>> GetPagedBooksForGraphQuery(IResolverContext context, CancellationToken token);
}
2 changes: 2 additions & 0 deletions Example/DataAccess.Example.Web/DataAccess.Example.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<PackageReference Include="HotChocolate.AspNetCore" Version="13.7.0" />
<PackageReference Include="HotChocolate.Data" Version="13.7.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DataAccess.Repository" Version="2.0.0-wip" />
<PackageReference Include="HotChocolate" Version="13.7.0" />
<PackageReference Include="HotChocolate.AspNetCore" Version="13.7.0" />
<PackageReference Include="HotChocolate.Data" Version="13.7.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\DataAccess.Repository\DataAccess.Repository.csproj" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public static async Task<Connection<TEntity>> GetPagedQueryItems<TEntity>(this I
IResolverContext context, CancellationToken token) where TEntity : class
{
return await repository.DbSet
.AsQueryable()
.AsNoTracking()
.AsQueryable()
.Filter(context)
.Project(context)
.Sort(context)
Expand Down
218 changes: 109 additions & 109 deletions Extensions/DataAccess.Repository.HotChocolate/readme.md
Original file line number Diff line number Diff line change
@@ -1,116 +1,116 @@
# HotChocolate Extension
## Introduction
This extension provides GraphQL query functionality via the [HotChocolate Library](https://chillicream.com/docs/hotchocolate/v13)
# HotChocolate Extension
## Introduction
This extension provides GraphQL query functionality via the [HotChocolate Library](https://chillicream.com/docs/hotchocolate/v13)

## Installation
1. You'll need to install and set up the core data access NuGet package (see instructions [here](https://github.com/Ian-Webster/DataAccess#usage))
2. Add the following NuGet packages to your IoC project;
1. [HotChocolate](https://www.nuget.org/packages/HotChocolate/13.7.0?_src=template)
2. [HotChocolate.AspNetCore](https://www.nuget.org/packages/HotChocolate.AspNetCore/13.7.0?_src=template)
3. [HotChocolate.Data](https://www.nuget.org/packages/HotChocolate.Data/13.7.0?_src=template)
3. Add the following NuGet packages to your Repository project;
1. [HotChocolate](https://www.nuget.org/packages/HotChocolate/13.7.0?_src=template)
2. [HotChocolate.Data](https://www.nuget.org/packages/HotChocolate.Data/13.7.0?_src=template)
3. TBC- this extension
4. Modify your IoC services to add and configure HotChocolate;
```csharp
// set up HotChocolate
builder.Services.AddGraphQLServer()
.AddQueryType(q => q.Name("Query"))
.AddProjections()
.AddFiltering()
.AddSorting();
```
5.Modify your middleware configuration to include `app.MapGraphQL();`
6. We'll revisit your IoC configuration later to add queries.
## Installation
1. You'll need to install and set up the core data access NuGet package (see instructions [here](https://github.com/Ian-Webster/DataAccess#usage))
2. Add the following NuGet packages to your IoC project;
1. [HotChocolate](https://www.nuget.org/packages/HotChocolate/13.7.0?_src=template)
2. [HotChocolate.AspNetCore](https://www.nuget.org/packages/HotChocolate.AspNetCore/13.7.0?_src=template)
3. [HotChocolate.Data](https://www.nuget.org/packages/HotChocolate.Data/13.7.0?_src=template)
3. Add the following NuGet packages to your Repository project;
1. [HotChocolate](https://www.nuget.org/packages/HotChocolate/13.7.0?_src=template)
2. [HotChocolate.Data](https://www.nuget.org/packages/HotChocolate.Data/13.7.0?_src=template)
3. TBC- this extension
4. Modify your IoC services to add and configure HotChocolate;
```csharp
// set up HotChocolate
builder.Services.AddGraphQLServer()
.AddQueryType(q => q.Name("Query"))
.AddProjections()
.AddFiltering()
.AddSorting();
```
5.Modify your middleware configuration to include `app.MapGraphQL();`
6. We'll revisit your IoC configuration later to add queries.

## Building queries
Queries in GraphQL are like a stand-in for controller endpoints in REST, they provide a public API into your data, you can read more here https://chillicream.com/docs/hotchocolate/v13/defining-a-schema/queries
## Building queries
Queries in GraphQL are like a stand-in for controller endpoints in REST, they provide a public API into your data, you can read more here https://chillicream.com/docs/hotchocolate/v13/defining-a-schema/queries

HotChocolate can query your DBContext directly should you wish, however it does also provide a number of IQueryable extension methods and a query context class that allow you to use your own services and repository methods to perform the data operations.
HotChocolate can query your DBContext directly should you wish, however it does also provide a number of IQueryable extension methods and a query context class that allow you to use your own services and repository methods to perform the data operations.

The purpose of this extension library to obfuscate the complexities involved in modifying repository methods to support the HotChocolate extensions by providing a number of extension methods in the QueryExtensions;
* GetQueryItem - returns a single entity, supports filtering and projection
* GetQueryItems - returns a collection of entities, supports filtering, projection and sorting
* GetPagedQueryItems - returns a [Connection](https://chillicream.com/docs/hotchocolate/v13/fetching-data/pagination/#connections) object provided by the HotChocolate library, supports filtering, projection, sorting and pagination
The purpose of this extension library to obfuscate the complexities involved in modifying repository methods to support the HotChocolate extensions by providing a number of extension methods in the QueryExtensions;
* GetQueryItem - returns a single entity, supports filtering and projection
* GetQueryItems - returns a collection of entities, supports filtering, projection and sorting
* GetPagedQueryItems - returns a [Connection](https://chillicream.com/docs/hotchocolate/v13/fetching-data/pagination/#connections) object provided by the HotChocolate library, supports filtering, projection, sorting and pagination

Usage for this extension is as follows;
1. Modify your repository to accept IResolverContext and to call the extension, as an example this is how to get a single book entity for GraphQL;
```csharp
// interface
using HotChocolate.Resolvers; // namespace for IResolverContext
namespace DataAccess.Example.Data.Repositories;

public interface IBookRepository
{
Task<Book?> GetBookForGraphQuery(IResolverContext context, CancellationToken token);
}
Usage for this extension is as follows;
1. Modify your repository to accept IResolverContext and to call the extension, as an example this is how to get a single book entity for GraphQL;
```csharp
// interface
using HotChocolate.Resolvers; // namespace for IResolverContext
namespace DataAccess.Example.Data.Repositories;

public interface IBookRepository
{
Task<Book?> GetBookForGraphQuery(IResolverContext context, CancellationToken token);
}

// implementation
using DataAccess.Repository.HotChocolate; // namespace for this extension
using HotChocolate.Resolvers; // namespace for IResolverContext
namespace DataAccess.Example.Data.Repositories;

public class BookRepository: IBookRepository
{
private readonly IRepository<Book> _bookRepo;

public BookRepository(RepositoryFactory<LibraryDatabaseContext> repositoryFactory)
{
_bookRepo = repositoryFactory.GetRepositoryByType<Book>();
}

public async Task<Book?> GetBookForGraphQuery(IResolverContext context, CancellationToken token)
{
return await _bookRepo.GetQueryItem(context, token);
}
}
```
2. Create a query class here is the book query;
```csharp
using HotChocolate;
using HotChocolate.Types;
using HotChocolate.Resolvers;

namespace DataAccess.Example.Data.Queries;

[ExtendObjectType("Query")]
public class BookQuery
{
[UseProjection] // enable projecton for this query
[UseFiltering] // enable filtering for this query
// [Service] is an attribute hint provided by the HotChocolate library
// telling the code to use DI to retrieve the IBookRepository dependency
// IResolverContext and CancellationToken objects are provide as part of the
// GraphQL context
public async Task<Book?> GetBook([Service] IBookRepository repository, IResolverContext context, CancellationToken token)
{
return await repository.GetBookForGraphQuery(context, token);
}
}
```
3. Modify the HotChocolate IoC configuration you added earlier to include your query;
```csharp
// set up HotChocolate
builder.Services.AddGraphQLServer()
.AddQueryType(q => q.Name("Query"))
.AddTypeExtension<BookQuery>() // adding the book query type
.AddProjections()
.AddFiltering()
.AddSorting();
```
4. Run the API project as normal, you should be able to visit /graphql and see the [Banana Cake Pop](https://chillicream.com/products/bananacakepop/) UX
5. Run a query through the UX, as an example this will return the book "Fight Club" from the example project;
```json
{
book (where: {bookId: {eq: "2c4a20f5-3fc8-4694-9c03-cb444bf416dc"}})
{
bookId,
name
}
}
```
// implementation
using DataAccess.Repository.HotChocolate; // namespace for this extension
using HotChocolate.Resolvers; // namespace for IResolverContext
namespace DataAccess.Example.Data.Repositories;

public class BookRepository: IBookRepository
{
private readonly IRepository<Book> _bookRepo;

public BookRepository(RepositoryFactory<LibraryDatabaseContext> repositoryFactory)
{
_bookRepo = repositoryFactory.GetRepositoryByType<Book>();
}

public async Task<Book?> GetBookForGraphQuery(IResolverContext context, CancellationToken token)
{
return await _bookRepo.GetQueryItem(context, token);
}
}
```
2. Create a query class here is the book query;
```csharp
using HotChocolate;
using HotChocolate.Types;
using HotChocolate.Resolvers;

namespace DataAccess.Example.Data.Queries;

[ExtendObjectType("Query")]
public class BookQuery
{
[UseProjection] // enable projecton for this query
[UseFiltering] // enable filtering for this query
// [Service] is an attribute hint provided by the HotChocolate library
// telling the code to use DI to retrieve the IBookRepository dependency
// IResolverContext and CancellationToken objects are provide as part of the
// GraphQL context
public async Task<Book?> GetBook([Service] IBookRepository repository, IResolverContext context, CancellationToken token)
{
return await repository.GetBookForGraphQuery(context, token);
}
}
```
3. Modify the HotChocolate IoC configuration you added earlier to include your query;
```csharp
// set up HotChocolate
builder.Services.AddGraphQLServer()
.AddQueryType(q => q.Name("Query"))
.AddTypeExtension<BookQuery>() // adding the book query type
.AddProjections()
.AddFiltering()
.AddSorting();
```
4. Run the API project as normal, you should be able to visit /graphql and see the [Banana Cake Pop](https://chillicream.com/products/bananacakepop/) UX
5. Run a query through the UX, as an example this will return the book "Fight Club" from the example project;
```json
{
book (where: {bookId: {eq: "2c4a20f5-3fc8-4694-9c03-cb444bf416dc"}})
{
bookId,
name
}
}
```

## Version history
* 0.1.0 - Initial project creation, getting everything figured out and running locally
## Version history
* 0.1.0 - Initial project creation, getting everything figured out and running locally

0 comments on commit d9074be

Please sign in to comment.