{"id":26192948,"url":"https://github.com/eoin55/honeybear.halclient","last_synced_at":"2026-03-09T20:32:23.064Z","repository":{"id":45257443,"uuid":"49719213","full_name":"eoin55/HoneyBear.HalClient","owner":"eoin55","description":"A lightweight fluent .NET client for navigating and consuming HAL APIs.","archived":false,"fork":false,"pushed_at":"2022-12-08T00:54:04.000Z","size":122,"stargazers_count":32,"open_issues_count":6,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T02:48:16.522Z","etag":null,"topics":["api-client","api-rest","dotnet","dotnet-core","dotnetcore","fluent","fluent-chaining-methods","fluent-interface","hal-api","hal-client","hateoas","hateoas-hal","json","netstandard","rest","rest-api","rest-client","restapi","restful","restful-api"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eoin55.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-15T13:04:44.000Z","updated_at":"2024-09-06T15:43:53.000Z","dependencies_parsed_at":"2023-01-25T01:45:46.564Z","dependency_job_id":null,"html_url":"https://github.com/eoin55/HoneyBear.HalClient","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoin55%2FHoneyBear.HalClient","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoin55%2FHoneyBear.HalClient/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoin55%2FHoneyBear.HalClient/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eoin55%2FHoneyBear.HalClient/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eoin55","download_url":"https://codeload.github.com/eoin55/HoneyBear.HalClient/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248997081,"owners_count":21195787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-client","api-rest","dotnet","dotnet-core","dotnetcore","fluent","fluent-chaining-methods","fluent-interface","hal-api","hal-client","hateoas","hateoas-hal","json","netstandard","rest","rest-api","rest-client","restapi","restful","restful-api"],"created_at":"2025-03-12T01:25:22.674Z","updated_at":"2026-03-09T20:32:23.005Z","avatar_url":"https://github.com/eoin55.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HoneyBear.HalClient\n\n![Build Status](https://ci.appveyor.com/api/projects/status/github/eoin55/HoneyBear.HalClient?branch=master\u0026svg=true)\n[![Coverage Status](https://coveralls.io/repos/eoin55/HoneyBear.HalClient/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/eoin55/HoneyBear.HalClient?branch=master)\n[![NuGet Version](https://img.shields.io/nuget/v/HoneyBear.HalClient.svg)](https://www.nuget.org/packages/HoneyBear.HalClient/)\n\nA lightweight fluent .NET client for navigating and consuming HAL APIs.\n\n## What is HAL?\n\nHAL (Hypertext Application Language) is a specification for a lightweight hypermedia type.\n\n* [HAL Specification](http://stateless.co/hal_specification.html)\n* Prezi: [RESTful Design Using HAL](https://prezi.com/4b9fpmopta0g/restful-design-using-hal/)\n\n## What's Nice About this HAL Client\n\nThere are already a number of open-source .NET HAL clients [available](https://github.com/mikekelly/hal_specification/wiki/Libraries#c-sharp).  *HoneyBear.HalClient* differs because it offers all of the following features:\n\n* Provides a fluent-like API for navigating a HAL API.\n* No additional attributes or semantics are required on the API contract.  Resources can be deserialised into [POCOs](http://stackoverflow.com/questions/250001/poco-definition).\n* Supports the [Hypertext Cache Pattern](https://tools.ietf.org/html/draft-kelly-json-hal-06#section-8.3); it treats *embedded* resources in the same way as it handles *links*.\n* Supports [URI templated](https://tools.ietf.org/html/rfc6570) links.  It uses [Tavis.UriTemplates](https://github.com/tavis-software/Tavis.UriTemplates) under the hood.\n\n### Known Limitations\n\n* *HoneyBear.HalClient* only supports the JSON HAL format.\n\n### Feedback Welcome\n\nIf you have any issues, suggests or comments, please create an [issue](https://github.com/eoin55/HoneyBear.HalClient/issues) or a [pull request](https://github.com/eoin55/HoneyBear.HalClient/pulls).\n\n## Getting Started\n\n### 1) Install the NuGet package\n```cs\nInstall-Package HoneyBear.HalClient\n```\n\n### 2) Create an instance of HalClient\n`HalClient` has a dependency on `HttpClient`.  This can be provided in the constructor:\n```cs\nvar halClent = new HalClient(new HttpClient { BaseAddress = new Uri(\"https://api.retail.com/\") });\n```\nOr accessed via a public property:\n```cs\nvar halClent = new HalClient();\nhalClent.HttpClient.BaseAddress = new Uri(\"https://api.retail.com/\");\n```\n\n#### (Optional) Custom serializer settings\nHalClient uses the default JsonMediaTypeFormatter for handling deserialization of responses. If you need to change any of the settings (for handling null values, missing properties, custom date formats and so on), you can build a custom MediaTypeFormatter by subclassing JsonMediaTypeFormatter, and then passing it in to the HalClient constructor:\n```cs\npublic class CustomMediaTypeFormatter : JsonMediaTypeFormatter\n{\n    SerializerSettings.NullValueHandling = NullValueHandling.Ignore;\n\n    SupportedMediaTypes.Add(new MediaTypeHeaderValue(\"application/hal+json\"));\n}\n\nvar halClent = new HalClient(new HttpClient { BaseAddress = new Uri(\"https://api.retail.com/\") }, new List\u003cMediaTypeFormatter\u003e { new CustomMediaTypeFormatter() });\n```\n\n### (Optional) Override default implementation of IJsonHttpClient\nBy default, `HalClient` uses a internal implementation of `IJsonHttpClient`, which uses `HttpClient` to perform HTTP requests (GET, POST, PUT and DELETE).  In some cases, it may be preferable to provide your own implementation of `IJsonHttpClient`.  For example, if you want to specify a different `MediaTypeFormatter` for serializing POST and PUT requests:\n\n```cs\npublic class CustomJsonHttpClient : IJsonHttpClient\n{\n    private readonly CustomMediaTypeFormatter _formatter;\n\n    public CustomJsonHttpClient(HttpClient client, CustomMediaTypeFormatter formatter)\n    {\n        HttpClient = client;\n\t_formatter = formatter;\n        HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(\"application/hal+json\"));\n    }\n\n    public HttpClient HttpClient { get; }\n\n    public Task\u003cHttpResponseMessage\u003e GetAsync(string uri)\n        =\u003e HttpClient.GetAsync(uri);\n\n    public Task\u003cHttpResponseMessage\u003e PostAsync\u003cT\u003e(string uri, T value)\n        =\u003e HttpClient.PostAsync(uri, value, _formatter);\n\n    public Task\u003cHttpResponseMessage\u003e PutAsync\u003cT\u003e(string uri, T value)\n        =\u003e HttpClient.PutAsync(uri, value, _formatter);\n\n    public Task\u003cHttpResponseMessage\u003e DeleteAsync(string uri)\n        =\u003e HttpClient.DeleteAsync(uri);\n}\n```\n\n```cs\nvar jsonClient = new CustomJsonHttpClient(new HttpClient(), new CustomMediaTypeFormatter());\nvar halClent = new HalClient(jsonClient);\n```\n\nor\n\n```cs\nvar jsonClient = new CustomJsonHttpClient(new HttpClient(), new CustomMediaTypeFormatter());\nvar formatters = new List\u003cMediaTypeFormatter\u003e { new CustomMediaTypeFormatter() };\nvar halClent = new HalClient(jsonClient, formatters);\n```\n\n## Usage Examples\n\nThe following examples are based on the [example JSON](#example-json) below.\n\n### 1) Retrieve a single resource\n```cs\nIResource\u003cOrder\u003e order =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order\", new {orderRef = \"46AC5C29-B8EB-43E7-932E-19167DA9F5D3\"}, \"retail\")\n        .Item\u003cOrder\u003e();\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n3. Reads *Order* resource\n\n### 2) Deserialise that resource into a POCO\n```cs\nOrder order =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order\", new {orderRef = \"46AC5C29-B8EB-43E7-932E-19167DA9F5D3\"}, \"retail\")\n        .Item\u003cOrder\u003e()\n        .Data;\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n3. Reads *Order* resource\n4. Deserialises resource into `Order`\n\n### 3) Retrieve a list of resources (embedded in a paged list resource)\n```cs\nIEnumerable\u003cIResource\u003cOrder\u003e\u003e orders =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order-query\", new {pageNumber = 0}, \"retail\")\n        .Get(\"order\", \"retail\")\n        .Items\u003cOrder\u003e();\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order?pagenumber=0\n3. Reads embedded array of *Order* resources\n\n### 4) Deserialise the list of resources into POCOs\n```cs\nIEnumerable\u003cOrder\u003e orders =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order-query\", new {pageNumber = 0}, \"retail\")\n        .Get(\"order\", \"retail\")\n        .Items\u003cOrder\u003e()\n        .Data();\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order?pagenumber=0\n3. Reads embedded array of *Order* resources\n4. Deserialises resources into a list of `Order`s\n\n### 5) Create a resource\n```cs\nvar payload = new { ... };\n\nOrder order =\n    client\n        .Root(\"/v1/version/1\")\n        .Post(\"order-add\", payload, \"retail\")\n        .Item\u003cOrder\u003e()\n        .Data;\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. POST https://api.retail.com/v1/order (with payload)\n3. Reads *Order* resource from response\n4. Deserialises resource into `Order`\n\n### 6) Update a resource\n```cs\nvar payload = new { ... };\n\nOrder order =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order\", new {orderRef = \"46AC5C29-B8EB-43E7-932E-19167DA9F5D3\"}, \"retail\")\n        .Put(\"order-edit\", payload, \"retail\")\n        .Item\u003cOrder\u003e()\n        .Data;\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n3. PUT https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3 (with payload)\n4. Reads *Order* resource from response\n5. Deserialises resource into `Order`\n\n### 7) Delete a resource\n```cs\nclient\n    .Root(\"/v1/version/1\")\n    .Get(\"order\", new {orderRef = \"46AC5C29-B8EB-43E7-932E-19167DA9F5D3\"}, \"retail\")\n    .Delete(\"order-delete\", \"retail\");\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n3. DELETE https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n\n### 8) Retrieve a resource's links\n```cs\nIList\u003cILink\u003e links =\n    client\n        .Root(\"/v1/version/1\")\n        .Get(\"order\", new {orderRef = \"46AC5C29-B8EB-43E7-932E-19167DA9F5D3\"}, \"retail\")\n        .Item\u003cOrder\u003e()\n        .Links;\n```\n\n1. GET https://api.retail.com/v1/version/1\n2. GET https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n3. Reads *Order* resource\n4. Returns the *Order* resource's *links*, e.g. *self*, *retail:order-edit*, *retail:order-delete*.\n\n## Dependency Injection\n\n`HalClient` implements interface `IHalClient`.  Registering it with [Autofac](http://autofac.org/) might look something like this:\n\n```cs\nbuilder\n    .RegisterType\u003cHttpClient\u003e()\n    .WithProperty(\"BaseAddress\", new Uri(\"https://api.retail.com\"))\n    .AsSelf();\n\nbuilder\n    .RegisterType\u003cHalClient\u003e()\n    .As\u003cIHalClient\u003e();\n```\n\n## Example JSON\n\nRoot resource: https://api.retail.com/v1/version/1\n```json\n{\n  \"versionNumber\": 1,\n  \"_links\": {\n    \"curies\": [\n      {\n        \"href\": \"https://api.retail.com/v1/docs/{rel}\",\n        \"name\": \"retail\",\n        \"templated\": true\n      }\n    ],\n    \"self\": {\n      \"href\": \"/v1/version/1\"\n    },\n    \"retail:order-query\": {\n      \"href\": \"/v1/order?pageNumber={pageNumber}\u0026pageSize={pageSize}\",\n      \"templated\": true\n    },\n    \"retail:order\": {\n      \"href\": \"/v1/order/{orderRef}\",\n      \"templated\": true\n    },\n    \"retail:order-add\": {\n      \"href\": \"/v1/order\"\n    },\n    \"retail:order-queryby-user\": {\n      \"href\": \"/v1/order?userRef={userRef}\",\n      \"templated\": true\n    }\n  }\n}\n```\n\nOrder resource: https://api.retail.com/v1/order/46AC5C29-B8EB-43E7-932E-19167DA9F5D3\n```json\n{\n  \"orderRef\": \"46ac5c29-b8eb-43e7-932e-19167da9f5d3\",\n  \"orderNumber\": \"123456\",\n  \"status\": \"AwaitingPayment\",\n  \"total\": {\n    \"amount\": 100.0,\n    \"currency\": \"USD\"\n  },\n  \"_links\": {\n    \"curies\": [\n      {\n        \"href\": \"https://api.retail.com/v1/docs/{rel}\",\n        \"name\": \"retail\",\n        \"templated\": true\n      }\n    ],\n    \"self\": {\n      \"href\": \"/v1/order/46ac5c29-b8eb-43e7-932e-19167da9f5d3\"\n    },\n    \"retail:order-edit\": {\n      \"href\": \"/v1/order/46ac5c29-b8eb-43e7-932e-19167da9f5d3\"\n    },\n    \"retail:order-delete\": {\n      \"href\": \"/v1/order/46ac5c29-b8eb-43e7-932e-19167da9f5d3\"\n    },\n    \"retail:orderitem\": {\n      \"href\": \"/v1/orderitem\"\n    }\n  },\n  \"_embedded\": {\n    \"retail:orderitem\": [\n      {\n        \"orderItemRef\": \"d7161f76-ed17-4156-a627-bc13b43345ab\",\n        \"status\": \"AwaitingPayment\",\n        \"total\": {\n          \"amount\": 20.0,\n          \"currency\": \"USD\"\n        },\n        \"quantity\": 1,\n        \"_links\": {\n          \"self\": {\n            \"href\": \"/v1/orderitem\"\n          },\n          \"retail:product\": {\n            \"href\": \"/v1/product/637ade4e-e927-4d4a-a628-32055ae5a12b\"\n          }\n        }\n      },\n      {\n        \"orderItemRef\": \"25d61931-181b-4b09-b883-c6fb374d5f4a\",\n        \"status\": \"AwaitingPayment\",\n        \"total\": {\n          \"amount\": 30.0,\n          \"currency\": \"USD\"\n        },\n        \"quantity\": 2,\n        \"_links\": {\n          \"self\": {\n            \"href\": \"/v1/orderitem\"\n          },\n          \"retail:product\": {\n            \"href\": \"/v1/product/fdc0d414-23a1-4208-a20a-9eeab0351f76\"\n          }\n        }\n      }\n    ]\n  }\n}\n```\n\nPaged list of Orders resource: https://api.retail.com/v1/order?pageNumber=0\n```json\n{\n  \"pageNumber\": 0,\n  \"pageSize\": 10,\n  \"knownPagesAvailable\": 1,\n  \"totalItemsCount\": 1,\n  \"_links\": {\n    \"curies\": [\n      {\n        \"href\": \"https://api.retail.com/v1/docs/{rel}\",\n        \"name\": \"retail\",\n        \"templated\": true\n      }\n    ],\n    \"self\": {\n      \"href\": \"/v1/order?pageNumber=0\u0026pageSize=10\"\n    },\n    \"retail:order\": {\n      \"href\": \"/v1/order/{orderRef}\",\n      \"templated\": true\n    }\n  },\n  \"_embedded\": {\n    \"retail:order\": [\n      {\n        \"orderRef\": \"e897113c-4c56-404b-8e83-7e7f705046b3\",\n        \"orderNumber\": \"789456\",\n        \"status\": \"AwaitingPayment\",\n        \"total\": {\n          \"amount\": 100.0,\n          \"currency\": \"USD\"\n        },\n        \"_links\": {\n          \"self\": {\n            \"href\": \"/v1/order/e897113c-4c56-404b-8e83-7e7f705046b3\"\n          },\n          \"retail:order-edit\": {\n            \"href\": \"/v1/order/e897113c-4c56-404b-8e83-7e7f705046b3\"\n          },\n          \"retail:order-delete\": {\n            \"href\": \"/v1/order/e897113c-4c56-404b-8e83-7e7f705046b3\"\n          },\n          \"retail:orderitem-queryby-order\": {\n            \"href\": \"/v1/orderitem?pageNumber={pageNumber}\u0026pageSize={pageSize}\u0026orderRef=e897113c-4c56-404b-8e83-7e7f705046b3\",\n            \"templated\": true\n          }\n        }\n      }\n    ]\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feoin55%2Fhoneybear.halclient","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feoin55%2Fhoneybear.halclient","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feoin55%2Fhoneybear.halclient/lists"}