Dotnet api graphql linux: Difference between revisions
(13 intermediate revisions by the same user not shown) | |||
Line 215: | Line 215: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==DataLoader== | ==DataLoader== | ||
===Introduction=== | |||
Might be being unfair but this looks the same a creating a view of tables to cache parent /child relationships | Might be being unfair but this looks the same a creating a view of tables to cache parent /child relationships | ||
* Add to Starup Services | |||
* Define a Lookup Function in Repository | |||
* Inject Loader Accessor into Graph QL Type= | |||
* For the Graph QL field, define the loader | |||
===Add to Starup Services=== | |||
<syntaxhighlight lang="c#"> | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
... | |||
services.AddGraphQL(o => { o.ExposeExceptions = false; }) | |||
.AddGraphTypes(ServiceLifetime.Scoped) | |||
.AddDataLoader(); | |||
} | |||
</syntaxhighlight> | |||
===Define a Lookup Function in Repository=== | |||
<syntaxhighlight lang="c#"> | |||
public class ProductReviewRepository | |||
{ | |||
... | |||
public async Task<ILookup<int, ProductReview>> GetForProducts(IEnumerable<int> productIds) | |||
{ | |||
var reviews = await _dbContext.ProductReviews.Where(pr => productIds.Contains(pr.ProductId)).ToListAsync(); | |||
return reviews.ToLookup(r => r.ProductId); | |||
} | |||
</syntaxhighlight> | |||
===Inject Loader Accessor into Graph QL Type=== | |||
<syntaxhighlight lang="c#"> | |||
public class ProductType : ObjectGraphType<Product> | |||
{ | |||
// public ProductType(ProductReviewRepository reviewRepository) | |||
public ProductType( | |||
ProductReviewRepository reviewRepository, | |||
IDataLoaderContextAccessor dataLoaderAccessor) | |||
{ | |||
... | |||
</syntaxhighlight> | |||
===For the Graph QL field, define the loader=== | |||
<syntaxhighlight lang="c#"> | |||
public class ProductType : ObjectGraphType<Product> | |||
{ | |||
... | |||
Field<ListGraphType<ProductReviewType>>( | |||
"Reviews", | |||
resolve: context => | |||
{ | |||
// Create the loader passing | |||
// Name of the loader Key | |||
// The Function to call on the repository | |||
// this returns a look up by product id, ProductReview | |||
var loader = | |||
dataLoaderAccessor.Context.GetOrAddCollectionBatchLoader<int, ProductReview>( | |||
"GetReviewsByProductId", | |||
reviewRepository.GetForProducts); | |||
return loader.LoadAsync(context.Source.Id); | |||
}); | |||
</syntaxhighlight> | |||
==Add Arguments To Query== | |||
===Introduction=== | |||
* Add Support to Repository to Get One Item | |||
* Create A Query For Graph QL Type | |||
===Add Support to Repository to Get One Item=== | |||
<syntaxhighlight lang="c#"> | |||
public async Task<Product> GetOne(int id) | |||
{ | |||
return await _dbContext.Products.SingleOrDefaultAsync(p => p.Id == id); | |||
} | |||
</syntaxhighlight> | |||
===Create A Query For Graph QL Type=== | |||
Add the arguments you want with the name for the argument along with a context to suppport it. | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockQuery : ObjectGraphType | |||
{ | |||
public CarvedRockQuery(ProductRepository productRepository) | |||
{ | |||
... | |||
Field<ProductType>( | |||
"product", | |||
arguments: new QueryArguments( | |||
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "id" }), | |||
resolve: context => | |||
{ | |||
var id = context.GetArgument<int>("id"); | |||
return productRepository.GetOne(id); | |||
}); | |||
</syntaxhighlight> | |||
==Creating A Mutation== | |||
* Create Repository function to support Mutation | |||
* Create an Input Type | |||
* Create a Mutation | |||
* Add To Schema | |||
* Test in Playground | |||
===Create Repository function to support Mutation=== | |||
<syntaxhighlight lang="c#"> | |||
public class ProductReviewRepository | |||
{ | |||
... | |||
public async Task<ProductReview> AddReview(ProductReview review) | |||
{ | |||
_dbContext.ProductReviews.Add(review); | |||
await _dbContext.SaveChangesAsync(); | |||
return review; | |||
} | |||
</syntaxhighlight> | |||
===Create an Input Type=== | |||
Create an Input Type. This defines the fields associated with the type. | |||
<syntaxhighlight lang="c#"> | |||
public class ProductReviewInputType: InputObjectGraphType | |||
{ | |||
public ProductReviewInputType() | |||
{ | |||
Name = "reviewInput"; | |||
Field<NonNullGraphType<StringGraphType>>("title"); | |||
Field<StringGraphType>("review"); | |||
Field<NonNullGraphType<IntGraphType>>("productId"); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===Create a Mutation=== | |||
Create a Mutation which specifies the arguments and the type to be used. This defines the Input Type to use and the repository function which will support it. | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockMutation : ObjectGraphType | |||
{ | |||
public CarvedRockMutation(ProductReviewRepository reviewRepository) | |||
{ | |||
FieldAsync<ProductReviewType>( | |||
"createReview", | |||
arguments: new QueryArguments( | |||
new QueryArgument<NonNullGraphType<ProductReviewInputType>> {Name = "review"}), | |||
resolve: async context => | |||
{ | |||
var review = context.GetArgument<ProductReview>("review"); | |||
return await context.TryAsyncResolve( | |||
async c => await reviewRepository.AddReview(review)); | |||
}); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===Add To Schema=== | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockSchema : Schema | |||
{ | |||
public CarvedRockSchema(IDependencyResolver resolver) : base(resolver) | |||
{ | |||
Query = resolver.Resolve<CarvedRockQuery>(); | |||
Mutation = resolver.Resolve<CarvedRockMutation>(); | |||
} | |||
</syntaxhighlight> | |||
===Test in Playground=== | |||
Create the mutation | |||
<syntaxhighlight lang="graphql"> | |||
mutation ($review: reviewInput!) { | |||
createReview(review: $review) { | |||
id | |||
title | |||
} | |||
} | |||
</syntaxhighlight> | |||
Create the arguments | |||
<syntaxhighlight lang="graphql"> | |||
{ | |||
"review": { | |||
"title": "Test is fab", | |||
"productId": 1 | |||
} | |||
} | |||
</syntaxhighlight> | |||
==Create Subscriptions== | |||
===Create Subscriptions=== | |||
Subscriptions allow the consumer to be notified when an event occurs. E.g. someone adds a review. | |||
* Create a Message | |||
* Create a Service to send notifications | |||
* Create a Graph QL Message Type | |||
* Create a Subscription | |||
* Add Subscription to Schema | |||
* Configure ASP .NET Core | |||
===Create a Message=== | |||
<syntaxhighlight lang="c#"> | |||
public class ReviewAddedMessage | |||
{ | |||
public int ProductId { get; set; } | |||
public string Title { get; set; } | |||
} | |||
</syntaxhighlight> | |||
===Create a Service to send notifications=== | |||
<syntaxhighlight lang="c#"> | |||
public class ReviewMessageService | |||
{ | |||
private readonly ISubject<ReviewAddedMessage> _messageStream = new ReplaySubject<ReviewAddedMessage>(1); | |||
public ReviewAddedMessage AddReviewAddedMessage(ProductReview review) | |||
{ | |||
var message = new ReviewAddedMessage | |||
{ | |||
ProductId = review.ProductId, | |||
Title = review.Title | |||
}; | |||
_messageStream.OnNext(message); | |||
return message; | |||
} | |||
public IObservable<ReviewAddedMessage> GetMessages() | |||
{ | |||
return _messageStream.AsObservable(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===Change Mutation to notify Message Service=== | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockMutation : ObjectGraphType | |||
{ | |||
public CarvedRockMutation(ProductReviewRepository reviewRepository, | |||
ReviewMessageService reviewMessageService) | |||
{ | |||
FieldAsync<ProductReviewType>( | |||
"createReview", | |||
arguments: new QueryArguments( | |||
new QueryArgument<NonNullGraphType<ProductReviewInputType>> { Name = "review" }), | |||
resolve: async context => | |||
{ | |||
var review = context.GetArgument<ProductReview>("review"); | |||
await reviewRepository.AddReview(review); | |||
reviewMessageService.AddReviewAddedMessage(review); | |||
return review; | |||
}); | |||
... | |||
</syntaxhighlight> | |||
===Create a Graph QL Message Type=== | |||
<syntaxhighlight lang="c#"> | |||
public class ReviewAddedMessageType : ObjectGraphType<ReviewAddedMessage> | |||
{ | |||
public ReviewAddedMessageType() | |||
{ | |||
Field(t => t.ProductId); | |||
Field(t => t.Title); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===Create a Subscription=== | |||
This is a type for the schema and defines type and subscriber. | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockSubscription: ObjectGraphType | |||
{ | |||
public CarvedRockSubscription(ReviewMessageService messageService) | |||
{ | |||
Name = "Subscription"; | |||
AddField(new EventStreamFieldType | |||
{ | |||
Name = "reviewAdded", | |||
Type = typeof(ReviewAddedMessageType), | |||
Resolver = new FuncFieldResolver<ReviewAddedMessage>(c => c.Source as ReviewAddedMessage), | |||
Subscriber = new EventStreamResolver<ReviewAddedMessage>(c => messageService.GetMessages()) | |||
}); | |||
} | |||
} | |||
</syntaxhighlight> | |||
===Add Subscription to the Schema=== | |||
<syntaxhighlight lang="c#"> | |||
public class CarvedRockSchema: Schema | |||
{ | |||
public CarvedRockSchema(IDependencyResolver resolver): base(resolver) | |||
{ | |||
... | |||
Subscription = resolver.Resolve<CarvedRockSubscription>(); | |||
} | |||
</syntaxhighlight> | |||
===Configure ASP .NET Core=== | |||
====Add Packages==== | |||
Add the GraphQL Transports Package | |||
<syntaxhighlight lang="bash"> | |||
dotnet add package GraphQL.Server.Transports.WebSockets | |||
</syntaxhighlight> | |||
====Configure Startup==== | |||
Configure the Service | |||
<syntaxhighlight lang="c#"> | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
... | |||
services.AddGraphQL(o => { o.ExposeExceptions = false; }) | |||
.AddGraphTypes(ServiceLifetime.Scoped) | |||
.AddDataLoader() | |||
.AddWebSockets(); | |||
} | |||
</syntaxhighlight> | |||
Configure the middleware for Web Sockets and GraphQL Websockets | |||
<syntaxhighlight lang="c#"> | |||
public void Configure(IApplicationBuilder app, CarvedRockDbContext dbContext) | |||
{ | |||
app.UseWebSockets(); | |||
app.UseGraphQLWebSockets<CarvedRockSchema>("/graphql"); | |||
</syntaxhighlight> | |||
==Client Example== | |||
The GraphQL Client is provided for .Net. This implements an interface very similar to the server. An example of reading and writing it shown below. | |||
<syntaxhighlight lang="c#"> | |||
public class ProductGraphClient | |||
{ | |||
private readonly GraphQLClient _client; | |||
public ProductGraphClient(GraphQLClient client) | |||
{ | |||
_client = client; | |||
} | |||
public async Task<ProductModel> GetProduct(int id) | |||
{ | |||
var query = new GraphQLRequest | |||
{ | |||
Query = @" | |||
query productQuery($productId: ID!) | |||
{ product(id: $productId) | |||
{ id name price rating photoFileName description stock introducedAt | |||
reviews { title review } | |||
} | |||
}", | |||
Variables = new {productId = id} | |||
}; | |||
var response = await _client.PostAsync(query); | |||
return response.GetDataFieldAs<ProductModel>("product"); | |||
} | |||
public async Task<ProductReviewModel> AddReview(ProductReviewInputModel review) | |||
{ | |||
var query = new GraphQLRequest | |||
{ | |||
Query = @" | |||
mutation($review: reviewInput!) | |||
{ | |||
createReview(review: $review) | |||
{ | |||
id | |||
} | |||
}", | |||
Variables = new { review } | |||
}; | |||
var response = await _client.PostAsync(query); | |||
return response.GetDataFieldAs<ProductReviewModel>("createReview"); | |||
} | |||
</syntaxhighlight> |
Latest revision as of 05:40, 7 August 2020
Creating a Project
Copy the project from RAID array
Sorry no help here
Add the packages
dotnet add package GraphQL
Create Database
Change the connection string
"CarvedRock": "Server=.;Database=CarvedRock;Trusted_Connection=False;User Id=test;Password=nottherealone"
Update the database
dotnet ef database update
Run the Site
To the site and look at https://localhost:5001/ui/playground
Creating a Schema
Create a Product Graph Type
Create Fields based on Meta Fields within ObjectGraphType
public class ProductType: ObjectGraphType<Product>
{
public ProductType()
{
Field(t => t.Id);
Field(t => t.Name).Description("The name of the product");
Field(t => t.Description);
}
}
Create a Query
Create a Query which looks in the repository (DB) and gets all of the products.
public class CarvedRockQuery: ObjectGraphType
{
public CarvedRockQuery(ProductRepository productRepository)
{
Field<ListGraphType<ProductType>>(
"products",
resolve: context => productRepository.GetAll()
);
}
}
Create a Schema for the Carved Rock Query
This holds the queries the schema supports. In our case the CarvedRockQuery created above
public class CarvedRockSchema: Schema
{
public CarvedRockSchema(IDependencyResolver resolver): base(resolver)
{
Query = resolver.Resolve<CarvedRockQuery>();
}
}
Configuring ASP .NET Core
In the Startup, Add the dependency resolver to the services, add the Schema and GraphQL configuring options.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
services.AddScoped<CarvedRockSchema>();
services.AddGraphQL(o => { o.ExposeExceptions = false; })
.AddGraphTypes(ServiceLifetime.Scoped);
Add the GraphQL to the middleware
public void Configure(
IApplicationBuilder app,
CarvedRockDbContext dbContext)
{
app.UseGraphQL<CarvedRockSchema>();
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());
Creating a GraphQL API
Adding Scalar Type
- Create the enumerator
- Derive a Graph QL Type to hold the Enum
- Add to Graph QL Type
- Add the new Graph QL Type to the existing Type
Create the enumerator
public enum ProductType
{
Boots,
ClimbingGear,
Kayaks
}
Derive a Graph QL Type to hold the Enum
public class ProductTypeEnumType: EnumerationGraphType<Data.ProductType>
{
public ProductTypeEnumType()
{
Name = "Type";
Description = "The type of product";
}
}
Add the new Graph QL Type to the existing Type
public class ProductType: ObjectGraphType<Product>
{
public ProductType()
{
Field(t => t.Id);
...
Field<ProductTypeEnumType>("Type", "The type of product");
...
Adding Complex Type
Steps Required to Add a Complex Type
- Create a class
- Add a Accessor to the DbContext
- Add the new Repository Class to Startup
- Add a GraphQL Type
- Create a Repository Class
- Add the Graph QL Type to the Product
- Update the Database
Create the class
public class ProductReview
{
public int Id {get; set;}
public int ProductId {get; set;}
public Product Product {get; set;}
[StringLength(200), Required]
public string Title {get; set;}
public string Review {get; set;}
}
Add a Accessor to the DbContext
public class CarvedRockDbContext: DbContext
{
...
public DbSet<ProductReview> ProductReviews { get; set; }
}
Add a GraphQL Type
public class ProductReviewType : ObjectGraphType<ProductReview>
{
public ProductReviewType()
{
Field(t => t.Id);
Field(t => t.Title);
Field(t => t.Review);
}
}
Create a Repository Class
public class ProductReviewRepository
{
private readonly CarvedRockDbContext _dbContext;
public ProductReviewRepository(CarvedRockDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<IEnumerable<ProductReview>> GetForProduct(int productId)
{
return await _dbContext.ProductReviews.Where(pr => pr.ProductId == productId).ToListAsync();
}
public async Task<ILookup<int, ProductReview>> GetForProducts(IEnumerable<int> productIds)
{
var reviews = await _dbContext.ProductReviews.Where(pr => productIds.Contains(pr.ProductId)).ToListAsync();
return reviews.ToLookup(r => r.ProductId);
}
}
Add the new Repository Class to Startup
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ProductRepository>();
services.AddScoped<ProductReviewRepository>();
Add the Graph QL Type to the Product
public class ProductType : ObjectGraphType<Product>
{
public ProductType(ProductReviewRepository reviewRepository)
{
Field(t => t.Id);
Field(t => t.Name).Description("The name of the product");
...
Field<ListGraphType<ProductReviewType>> (
"Reviews",
resolve : context => reviewRepository.GetForProduct(context.Source.Id)
);
Update the Database
This did not work for me the first time so this might be wrong. Whatever you need to make sure the table exists on the database.
dotnet ef migrations add AddReviews
dotnet ef database update
DataLoader
Introduction
Might be being unfair but this looks the same a creating a view of tables to cache parent /child relationships
- Add to Starup Services
- Define a Lookup Function in Repository
- Inject Loader Accessor into Graph QL Type=
- For the Graph QL field, define the loader
Add to Starup Services
public void ConfigureServices(IServiceCollection services)
{
...
services.AddGraphQL(o => { o.ExposeExceptions = false; })
.AddGraphTypes(ServiceLifetime.Scoped)
.AddDataLoader();
}
Define a Lookup Function in Repository
public class ProductReviewRepository
{
...
public async Task<ILookup<int, ProductReview>> GetForProducts(IEnumerable<int> productIds)
{
var reviews = await _dbContext.ProductReviews.Where(pr => productIds.Contains(pr.ProductId)).ToListAsync();
return reviews.ToLookup(r => r.ProductId);
}
Inject Loader Accessor into Graph QL Type
public class ProductType : ObjectGraphType<Product>
{
// public ProductType(ProductReviewRepository reviewRepository)
public ProductType(
ProductReviewRepository reviewRepository,
IDataLoaderContextAccessor dataLoaderAccessor)
{
...
For the Graph QL field, define the loader
public class ProductType : ObjectGraphType<Product>
{
...
Field<ListGraphType<ProductReviewType>>(
"Reviews",
resolve: context =>
{
// Create the loader passing
// Name of the loader Key
// The Function to call on the repository
// this returns a look up by product id, ProductReview
var loader =
dataLoaderAccessor.Context.GetOrAddCollectionBatchLoader<int, ProductReview>(
"GetReviewsByProductId",
reviewRepository.GetForProducts);
return loader.LoadAsync(context.Source.Id);
});
Add Arguments To Query
Introduction
- Add Support to Repository to Get One Item
- Create A Query For Graph QL Type
Add Support to Repository to Get One Item
public async Task<Product> GetOne(int id)
{
return await _dbContext.Products.SingleOrDefaultAsync(p => p.Id == id);
}
Create A Query For Graph QL Type
Add the arguments you want with the name for the argument along with a context to suppport it.
public class CarvedRockQuery : ObjectGraphType
{
public CarvedRockQuery(ProductRepository productRepository)
{
...
Field<ProductType>(
"product",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<int>("id");
return productRepository.GetOne(id);
});
Creating A Mutation
- Create Repository function to support Mutation
- Create an Input Type
- Create a Mutation
- Add To Schema
- Test in Playground
Create Repository function to support Mutation
public class ProductReviewRepository
{
...
public async Task<ProductReview> AddReview(ProductReview review)
{
_dbContext.ProductReviews.Add(review);
await _dbContext.SaveChangesAsync();
return review;
}
Create an Input Type
Create an Input Type. This defines the fields associated with the type.
public class ProductReviewInputType: InputObjectGraphType
{
public ProductReviewInputType()
{
Name = "reviewInput";
Field<NonNullGraphType<StringGraphType>>("title");
Field<StringGraphType>("review");
Field<NonNullGraphType<IntGraphType>>("productId");
}
}
Create a Mutation
Create a Mutation which specifies the arguments and the type to be used. This defines the Input Type to use and the repository function which will support it.
public class CarvedRockMutation : ObjectGraphType
{
public CarvedRockMutation(ProductReviewRepository reviewRepository)
{
FieldAsync<ProductReviewType>(
"createReview",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<ProductReviewInputType>> {Name = "review"}),
resolve: async context =>
{
var review = context.GetArgument<ProductReview>("review");
return await context.TryAsyncResolve(
async c => await reviewRepository.AddReview(review));
});
}
}
Add To Schema
public class CarvedRockSchema : Schema
{
public CarvedRockSchema(IDependencyResolver resolver) : base(resolver)
{
Query = resolver.Resolve<CarvedRockQuery>();
Mutation = resolver.Resolve<CarvedRockMutation>();
}
Test in Playground
Create the mutation
mutation ($review: reviewInput!) {
createReview(review: $review) {
id
title
}
}
Create the arguments
{
"review": {
"title": "Test is fab",
"productId": 1
}
}
Create Subscriptions
Create Subscriptions
Subscriptions allow the consumer to be notified when an event occurs. E.g. someone adds a review.
- Create a Message
- Create a Service to send notifications
- Create a Graph QL Message Type
- Create a Subscription
- Add Subscription to Schema
- Configure ASP .NET Core
Create a Message
public class ReviewAddedMessage
{
public int ProductId { get; set; }
public string Title { get; set; }
}
Create a Service to send notifications
public class ReviewMessageService
{
private readonly ISubject<ReviewAddedMessage> _messageStream = new ReplaySubject<ReviewAddedMessage>(1);
public ReviewAddedMessage AddReviewAddedMessage(ProductReview review)
{
var message = new ReviewAddedMessage
{
ProductId = review.ProductId,
Title = review.Title
};
_messageStream.OnNext(message);
return message;
}
public IObservable<ReviewAddedMessage> GetMessages()
{
return _messageStream.AsObservable();
}
}
Change Mutation to notify Message Service
public class CarvedRockMutation : ObjectGraphType
{
public CarvedRockMutation(ProductReviewRepository reviewRepository,
ReviewMessageService reviewMessageService)
{
FieldAsync<ProductReviewType>(
"createReview",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<ProductReviewInputType>> { Name = "review" }),
resolve: async context =>
{
var review = context.GetArgument<ProductReview>("review");
await reviewRepository.AddReview(review);
reviewMessageService.AddReviewAddedMessage(review);
return review;
});
...
Create a Graph QL Message Type
public class ReviewAddedMessageType : ObjectGraphType<ReviewAddedMessage>
{
public ReviewAddedMessageType()
{
Field(t => t.ProductId);
Field(t => t.Title);
}
}
Create a Subscription
This is a type for the schema and defines type and subscriber.
public class CarvedRockSubscription: ObjectGraphType
{
public CarvedRockSubscription(ReviewMessageService messageService)
{
Name = "Subscription";
AddField(new EventStreamFieldType
{
Name = "reviewAdded",
Type = typeof(ReviewAddedMessageType),
Resolver = new FuncFieldResolver<ReviewAddedMessage>(c => c.Source as ReviewAddedMessage),
Subscriber = new EventStreamResolver<ReviewAddedMessage>(c => messageService.GetMessages())
});
}
}
Add Subscription to the Schema
public class CarvedRockSchema: Schema
{
public CarvedRockSchema(IDependencyResolver resolver): base(resolver)
{
...
Subscription = resolver.Resolve<CarvedRockSubscription>();
}
Configure ASP .NET Core
Add Packages
Add the GraphQL Transports Package
dotnet add package GraphQL.Server.Transports.WebSockets
Configure Startup
Configure the Service
public void ConfigureServices(IServiceCollection services)
{
...
services.AddGraphQL(o => { o.ExposeExceptions = false; })
.AddGraphTypes(ServiceLifetime.Scoped)
.AddDataLoader()
.AddWebSockets();
}
Configure the middleware for Web Sockets and GraphQL Websockets
public void Configure(IApplicationBuilder app, CarvedRockDbContext dbContext)
{
app.UseWebSockets();
app.UseGraphQLWebSockets<CarvedRockSchema>("/graphql");
Client Example
The GraphQL Client is provided for .Net. This implements an interface very similar to the server. An example of reading and writing it shown below.
public class ProductGraphClient
{
private readonly GraphQLClient _client;
public ProductGraphClient(GraphQLClient client)
{
_client = client;
}
public async Task<ProductModel> GetProduct(int id)
{
var query = new GraphQLRequest
{
Query = @"
query productQuery($productId: ID!)
{ product(id: $productId)
{ id name price rating photoFileName description stock introducedAt
reviews { title review }
}
}",
Variables = new {productId = id}
};
var response = await _client.PostAsync(query);
return response.GetDataFieldAs<ProductModel>("product");
}
public async Task<ProductReviewModel> AddReview(ProductReviewInputModel review)
{
var query = new GraphQLRequest
{
Query = @"
mutation($review: reviewInput!)
{
createReview(review: $review)
{
id
}
}",
Variables = new { review }
};
var response = await _client.PostAsync(query);
return response.GetDataFieldAs<ProductReviewModel>("createReview");
}