Follow links, not hardcoded URLs.

Chatter.Rest.Hal — HAL for .NET. Fluent builders, System.Text.Json, source generators.

$ dotnet add package Chatter.Rest.Hal

Build it fluently.

One method per HAL fragment. The JSON assembles as you type.

var hal = ResourceBuilder
  .WithState(new { currentlyProcessing = 14 })
  .AddSelf().AddLinkObject("/orders")
  .AddCuries()
    .AddLinkObject("http://example.com/docs/rels/{rel}", "ea")
  .AddLink("next").AddLinkObject("/orders?page=2")
  .AddLink("ea:find")
    .AddLinkObject("/orders{?id}").Templated()
  .AddEmbedded("ea:order")
    .AddResource(new { total = 30.00F, status = "shipped" })
      .AddSelf().AddLinkObject("/orders/123")
  .Build();
// What you get

The whole HAL round trip, spec-compliant.

1

Fluent builder

Chain state, links, curies and embedded resources. Each stage only offers what is valid next.

ResourceBuilder
  .WithState(new { id = 123 })
  .AddSelf().AddLinkObject("/orders/123")
  .Build();
2

Typed deserialization

Read responses into your own types, a Resource, or source-generated classes. [HalResponse] adds the _links and _embedded plumbing for you.

[HalResponse]
public partial class OrderCollection
{
  public int CurrentlyProcessing { get; set; }
}
3

Stable link arrays

Opt in to AlwaysUseArrayForLinks and a single link never flips between object and array again.

{ "_links": {
    "self": [{ "href": "/orders" }]
} }
4

RFC 6570 URI templates

Levels 1–3 template parsing and expansion are built in, so templated links resolve without string glue.

"ea:find": { "href": "/orders{?id}",
             "templated": true }
// → /orders?id=123