Documentation

Mutations

← All docs

Mutations

Authorize writes and create resources in the hierarchy.

For create, update, and delete operations, check authorization first with CheckAccessAsync, then perform the mutation.

Create pattern

  1. Check permission on the parent resource
  2. Create the FGA resource
  3. Create your domain entity with the resource ID
  4. Save in one transaction
app.MapPost("/api/chains/{chainId}/locations", async (
    string chainId,
    CreateLocationRequest request,
    ExampleAppDbContext context,
    ISqlOSFgaAuthService authService,
    HttpContext http) =>
{
    var subjectId = http.GetSubjectId();
    var chain = await context.Chains.FindAsync(chainId);
    if (chain == null) return Results.NotFound();

    var access = await authService.CheckAccessAsync(
        subjectId, "LOCATION_EDIT", chain.ResourceId);
    if (!access.Allowed)
        return Results.Json(new { error = "Permission denied" }, statusCode: 403);

    var resourceId = context.CreateResource(
        chain.ResourceId, request.Name, "location");

    var location = new Location
    {
        ResourceId = resourceId,
        ChainId = chainId,
        Name = request.Name,
        Address = request.Address
    };
    context.Locations.Add(location);
    await context.SaveChangesAsync();

    return Results.Created($"/api/locations/{location.Id}", location);
});

Update pattern

app.MapPut("/api/chains/{id}", async (
    string id,
    UpdateChainRequest request,
    ExampleAppDbContext context,
    ISqlOSFgaAuthService authService,
    HttpContext http) =>
{
    var subjectId = http.GetSubjectId();
    var chain = await context.Chains.FindAsync(id);
    if (chain == null) return Results.NotFound();

    var access = await authService.CheckAccessAsync(
        subjectId, "CHAIN_EDIT", chain.ResourceId);
    if (!access.Allowed)
        return Results.Json(new { error = "Permission denied" }, statusCode: 403);

    chain.Name = request.Name;
    chain.Description = request.Description;
    await context.SaveChangesAsync();

    return Results.Ok(chain);
});

Delete pattern

app.MapDelete("/api/chains/{id}", async (
    string id,
    ExampleAppDbContext context,
    ISqlOSFgaAuthService authService,
    HttpContext http) =>
{
    var subjectId = http.GetSubjectId();
    var chain = await context.Chains.FindAsync(id);
    if (chain == null) return Results.NotFound();

    var access = await authService.CheckAccessAsync(
        subjectId, "CHAIN_EDIT", chain.ResourceId);
    if (!access.Allowed)
        return Results.Json(new { error = "Permission denied" }, statusCode: 403);

    context.Chains.Remove(chain);
    await context.SaveChangesAsync();

    return Results.NoContent();
});