# Integration Standard: GraphQL

## When to Use GraphQL
✅ Client needs fine-grained field selection (mobile apps, different clients)
✅ Complex nested resource relationships (avoid N+1 with DataLoader)
✅ Real-time subscriptions alongside queries
❌ Simple CRUD APIs (REST is simpler)
❌ File upload as primary use case (use REST for uploads)
❌ When caching is critical (GraphQL caching is harder)

## Setup (Hot Chocolate)
```xml
<PackageReference Include="HotChocolate.AspNetCore" Version="14.*" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="14.*" />
```

```csharp
builder.Services
    .AddGraphQLServer()
    .AddQueryType<QueryType>()
    .AddMutationType<MutationType>()
    .AddSubscriptionType<SubscriptionType>()
    .AddProjections()
    .AddFiltering()
    .AddSorting()
    .AddAuthorization();

app.MapGraphQL("/graphql");
```

## Query Type
```csharp
[QueryType]
public class QueryType
{
    [UseProjection]
    [UseFiltering]
    [UseSorting]
    [UsePaging]
    public IQueryable<Order> GetOrders([Service] AppDbContext ctx) => ctx.Orders;

    public async Task<Order?> GetOrder(Guid id, [Service] AppDbContext ctx)
        => await ctx.Orders.FindAsync(id);
}
```

## Mutation Type
```csharp
[MutationType]
public class MutationType
{
    public async Task<CreateOrderPayload> CreateOrder(
        CreateOrderInput input,
        [Service] ISender sender)
    {
        var result = await sender.Send(new CreateOrderCommand(input.UserId, input.Items));
        return new CreateOrderPayload(result.OrderId);
    }
}

public record CreateOrderInput(Guid UserId, List<OrderItemInput> Items);
public record CreateOrderPayload(Guid OrderId);
```

## DataLoader (N+1 Prevention)
```csharp
public class UserDataLoader : BatchDataLoader<Guid, User>
{
    protected override async Task<IReadOnlyDictionary<Guid, User>> LoadBatchAsync(
        IReadOnlyList<Guid> keys, CancellationToken ct)
    {
        return await _context.Users
            .Where(u => keys.Contains(u.Id))
            .ToDictionaryAsync(u => u.Id, ct);
    }
}

// Use in resolver
public async Task<User?> GetUser([Parent] Order order, UserDataLoader loader)
    => await loader.LoadAsync(order.UserId);
```

## Security
- Apply `[Authorize]` on mutations and sensitive queries
- Disable introspection in production
- Depth limiting: max 5 levels
- Complexity limiting: max 1000 nodes per query
```csharp
.AddMaxExecutionDepthRule(5)
.ModifyRequestOptions(opts => opts.MaxAllowedRequestSize = 1_000_000)
```
