Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# platform-examples

Standalone, runnable example applications for the ChilliCream platform products
(currently [Mocha](https://github.com/ChilliCream/graphql-platform/tree/main/src/Mocha)).
(currently [Mocha](https://github.com/ChilliCream/graphql-platform/tree/main/src/Mocha) and
[Hot Chocolate](https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate)).
Examples live here instead of in the main
[graphql-platform](https://github.com/ChilliCream/graphql-platform) repository so
they can be built, run, and versioned independently of the product source and
Expand All @@ -21,9 +22,20 @@ examples/Mocha/Demo/
examples/Mocha/PostgresTransport/
examples/Mocha/AotExample/
examples/Mocha/ExceptionPolicies/
examples/HotChocolate/DataLoaderExplained/
examples/HotChocolate/GreenDonutData/
examples/HotChocolate/LayeredDataLoader/
examples/HotChocolate/RelativeCursors/
examples/HotChocolate/ConnectionAggregations/
examples/HotChocolate/EfCoreProjections/
examples/HotChocolate/StatefulDataLoader/
examples/Architecture/Bookshop/
```

`examples/HotChocolate/` contains the source code of the
[ChilliCream TV](https://chillicream.com/videos) episodes. Each example pins the Hot Chocolate version
used in its episode through its own `Directory.Packages.props`, so the code matches the video.

`examples/Architecture/` contains cross-product architecture examples. `Bookshop` demonstrates
clean architecture and DDD with HotChocolate, GreenDonut, Mocha.Mediator, and EF Core.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.0",
"commands": [
"dotnet-ef"
]
}
}
}
1 change: 1 addition & 0 deletions examples/HotChocolate/ConnectionAggregations/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.generated/
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)../'))" />
<ItemGroup Label="GraphQL">
<PackageVersion Include="HotChocolate.AspNetCore" Version="15.1.0-rc.4" />
<PackageVersion Include="HotChocolate.AspNetCore.CommandLine" Version="15.1.0-rc.4" />
<PackageVersion Include="HotChocolate.Data.EntityFramework" Version="15.1.0-rc.4" />
<PackageVersion Include="HotChocolate.Types.Analyzers" Version="15.1.0-rc.4" />
</ItemGroup>
<ItemGroup Label="EF Core">
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2" />
</ItemGroup>
</Project>
31 changes: 31 additions & 0 deletions examples/HotChocolate/ConnectionAggregations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Make Your GraphQL Connections Smarter! Add Aggregations

This example shows how to use the Connections API in Hot Chocolate to add aggregations, such as totals and counts, to GraphQL connections. The aggregates are returned next to the paged data, so clients get insights and items from the same field.

- Video: https://www.youtube.com/watch?v=DtISlxOBmPQ
- Episode page: https://chillicream.com/videos/2025-03-24-connection-aggregations

Hot Chocolate version: `15.1.0-rc.4` (see `Directory.Packages.props`).

## Prerequisites

- .NET SDK, as pinned by the `global.json` of the `platform-examples` repository
- Docker, to run PostgreSQL (`compose.yml` starts PostgreSQL with pgvector and pgAdmin)

## Run

Start PostgreSQL:

```bash
docker compose up -d
```

Run the API from the example folder:

```bash
dotnet run --project src/Catalog.API
```

The database `CatalogDB` is migrated and seeded from `src/Catalog.API/Migrations/catalog.json` on startup. The GraphQL endpoint is `http://localhost:5224/graphql` (from `src/Catalog.API/Properties/launchSettings.json`).

The connection string in `src/Catalog.API/appsettings.json` uses the local development credentials from `compose.yml` (user `admin`, password `root`). pgAdmin is available at `http://localhost:5050` with `admin@admin.com` and password `root`.
26 changes: 26 additions & 0 deletions examples/HotChocolate/ConnectionAggregations/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
services:
postgres:
container_name: container-pg
image: ankane/pgvector
hostname: localhost
ports:
- "5432:5432"
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: root
restart: unless-stopped

pgadmin:
container_name: container-pgadmin
image: dpage/pgadmin4
depends_on:
- postgres
ports:
- "5050:80"
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: root
restart: unless-stopped

volumes:
postgres-data:
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using eShop.Catalog.Data.Entities;
using eShop.Catalog.Data.EntityConfigurations;

namespace eShop.Catalog.Data;

public class CatalogContext(DbContextOptions<CatalogContext> options) : DbContext(options)
{
public DbSet<ProductEntity> Products => Set<ProductEntity>();

public DbSet<ProductTypeEntity> ProductTypes => Set<ProductTypeEntity>();

public DbSet<BrandEntity> Brands => Set<BrandEntity>();

protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new BrandEntityTypeConfiguration());
builder.ApplyConfiguration(new ProductTypeEntityTypeConfiguration());
builder.ApplyConfiguration(new ProductEntityTypeConfiguration());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// ReSharper disable CollectionNeverUpdated.Global

using System.ComponentModel.DataAnnotations;

namespace eShop.Catalog.Data.Entities;

public sealed class BrandEntity
{
public int Id { get; set; }

[Required]
public string Name { get; set; } = default!;

public ICollection<ProductEntity> Products { get; set; } = new List<ProductEntity>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ReSharper disable CollectionNeverUpdated.Global

using System.ComponentModel.DataAnnotations;

namespace eShop.Catalog.Data.Entities;

public sealed class ProductEntity
{
public int Id { get; set; }

[Required]
public string Name { get; set; } = default!;

public string? Description { get; set; }

public decimal Price { get; set; }

public string? ImageFileName { get; set; }

public int TypeId { get; set; }

public ProductTypeEntity? Type { get; set; }

public int BrandId { get; set; }

public BrandEntity? Brand { get; set; }

public int AvailableStock { get; set; }

public int RestockThreshold { get; set; }

public int MaxStockThreshold { get; set; }

public bool OnReorder { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ReSharper disable CollectionNeverUpdated.Global

using System.ComponentModel.DataAnnotations;

namespace eShop.Catalog.Data.Entities;

public sealed class ProductTypeEntity
{
public int Id { get; set; }

[Required] public string Name { get; set; } = default!;

public ICollection<ProductEntity> Products { get; set; } = new List<ProductEntity>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using eShop.Catalog.Data.Entities;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace eShop.Catalog.Data.EntityConfigurations;

internal sealed class BrandEntityTypeConfiguration : IEntityTypeConfiguration<BrandEntity>
{
public void Configure(EntityTypeBuilder<BrandEntity> builder)
{
builder
.ToTable("Brands");

builder
.Property(cb => cb.Name)
.HasMaxLength(100);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using eShop.Catalog.Data.Entities;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace eShop.Catalog.Data.EntityConfigurations;

internal sealed class ProductEntityTypeConfiguration : IEntityTypeConfiguration<ProductEntity>
{
public void Configure(EntityTypeBuilder<ProductEntity> builder)
{
builder
.ToTable("Products");

builder
.Property(ci => ci.Name)
.HasMaxLength(50);

builder
.Property(ci => ci.Description)
.HasMaxLength(2048);

builder
.Property(ci => ci.ImageFileName)
.HasMaxLength(256);

builder
.HasOne(ci => ci.Brand)
.WithMany(ci => ci.Products)
.HasForeignKey(ci => ci.BrandId);

builder
.HasOne(ci => ci.Type)
.WithMany(ci => ci.Products)
.HasForeignKey(ci => ci.TypeId);

builder
.HasIndex(ci => ci.Name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using eShop.Catalog.Data.Entities;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace eShop.Catalog.Data.EntityConfigurations;

internal sealed class ProductTypeEntityTypeConfiguration : IEntityTypeConfiguration<ProductTypeEntity>
{
public void Configure(EntityTypeBuilder<ProductTypeEntity> builder)
{
builder
.ToTable("ProductTypes");

builder
.Property(cb => cb.Name)
.HasMaxLength(100);
}
}
Loading