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
- Check permission on the parent resource
- Create the FGA resource
- Create your domain entity with the resource ID
- 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();
});