{"id":19016295,"url":"https://github.com/akhansari/aspfeat","last_synced_at":"2026-01-14T05:01:26.402Z","repository":{"id":46018293,"uuid":"318895537","full_name":"akhansari/AspFeat","owner":"akhansari","description":"A modular and low ceremony toolkit for ASP.Net Core and F#","archived":true,"fork":false,"pushed_at":"2022-01-18T21:06:36.000Z","size":96,"stargazers_count":27,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-09-26T11:58:46.166Z","etag":null,"topics":["aspfeat","aspnetcore","fsharp"],"latest_commit_sha":null,"homepage":"","language":"F#","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/akhansari.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":"2020-12-05T21:46:30.000Z","updated_at":"2024-12-16T10:06:19.000Z","dependencies_parsed_at":"2022-08-24T19:31:25.249Z","dependency_job_id":null,"html_url":"https://github.com/akhansari/AspFeat","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/akhansari/AspFeat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akhansari%2FAspFeat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akhansari%2FAspFeat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akhansari%2FAspFeat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akhansari%2FAspFeat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akhansari","download_url":"https://codeload.github.com/akhansari/AspFeat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akhansari%2FAspFeat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28410076,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["aspfeat","aspnetcore","fsharp"],"created_at":"2024-11-08T19:42:59.326Z","updated_at":"2026-01-14T05:01:26.385Z","avatar_url":"https://github.com/akhansari.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AspFeat [![NuGet Status](http://img.shields.io/nuget/v/AspFeat.svg)](https://www.nuget.org/packages/AspFeat) ![ASP.NET Core 6.0](https://img.shields.io/badge/ASP.NET%20Core-6.0-blue)\n\nA modular and low ceremony toolkit for ASP .Net and F#.\n\n- Modular injection of services and middlewares.\n- Set of low ceremony ready-to-use setups.\n- Functional helpers over ASP .Net and nothing else.\n- Focused on Web APIs.\n\nYou can find examples in the samples folder.\n\n## Startup\n\nIn order to setup a feature properly, it's necessary to first add the [services](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) to `IServiceCollection` and then use the [middlewares](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/) with `IApplicationBuilder`. The downside is that they are mixed with other features and moreover the order of calls are important, which makes everything complicated.\n\nTo keep the startup clean, the idea is to package features into modules and then expose the setup of `WebApplicationBuilder` and `WebApplication` as a tuple.\n\nThe end result is that ASP .Net startup has never been so easy:\n\n```fsharp\n[\u003cEntryPoint\u003e]\nlet main args =\n    let configure bld = uhttp bld Get \"/\" (write \"hello world\")\n    WebApp.run args [ Endpoint.feat configure ]\n```\n\nSwagger sample:\n\n```fsharp\nmodule Swagger =\n    open Microsoft.AspNetCore.Builder\n    open Microsoft.Extensions.DependencyInjection\n\n    let feat () : Feat =\n        fun builder -\u003e\n            builder.Services\n                .AddEndpointsApiExplorer()\n                .AddSwaggerGen()\n            |\u003e ignore\n        ,\n        fun app -\u003e\n            app\n                .UseSwagger()\n                .UseSwaggerUI()\n            |\u003e ignore\n\n[\u003cEntryPoint\u003e]\nlet main args =\n    [ Endpoint.feat configureEndpoints\n      Swagger.feat () ]\n    |\u003e WebApp.run args\n```\n\n## Endpoint Routing\n\nAspFeat comes with several helpers that ease the use of the functional programming paradigm.\\\nWe do not intend to completely change your way of using ASP .Net but rather to offer a more nice and more F#-idiomatic way of using ASP .Net.\\\nSo you are not limited to AspFeat and you can still use Vanilla ASP .Net, if you need to.\n\nFor further information please refer to [Microsoft Docs](https://docs.microsoft.com/aspnet/core/fundamentals/routing)\n\n### Example\n\n**Without** AspFeat toolkit:\n```fsharp\nlet configureEndpoints (bld: IEndpointRouteBuilder) =\n    bld.MapGet(\"/\", RequestDelegate getHandler) |\u003e ignore\n```\n\n**With** AspFeat toolkit:\n```fsharp\nlet configureEndpoints bld =\n    uhttp bld Get \"/\" getHandler\n```\n\nWith the DSL:\n```fsharp\nlet configureEndpoints bld =\n    endpoints bld {\n        get \"/\" getHandler\n    }\n```\n\nWith the OpenApi/Swagger DSL:\n```fsharp\nlet getHandler =\n    writeAsJson \"hello world\"\n\ntype World =\n    [\u003cProducesResponseType(StatusCodes.Status200OK)\u003e]\n    abstract member GetHandler: unit -\u003e string\n\nlet configureEndpoints bld =\n    endpointsMetadata\u003cWorld\u003e bld {\n        get \"/\" getHandler (nameof getHandler)\n    }\n```\n\n### Route values and JSON content injection\n\nInstead of manually fetching data through `HttpContext`, it is possible to inject them into the handler.\n\n- `httpf` / `uhttpf` injects route values.\n  - A single value is injected as is.\n  - Multiple values are injected _in order_ as a tuple.\n- `httpj` / `uhttpj` injects the deserialized JSON content.\n- `httpfj` / `uhttpfj` combines both.\n\n```fsharp\nlet hello firstname = write $\"Hello {firstname}\"\nlet createGift gift = write $\"Create a {gift}\"\nlet goodbye (firstname, lastname) gift =\n    write $\"Goodbye {firstname} {lastname} and here is your {gift}\"\n\nlet configureEndpoints bld =\n    uhttpf  bld Get  \"/hello/{firstname}\" hello\n    uhttpj  bld Post \"/gift\" createGift\n    uhttpfj bld Put  \"/goodbye/{firstname}/{lastname}\" goodbye\n```\n\n## Http Handlers\n\n### Composition\n\nIt is possible to combine http handlers.\n\nThose with input injection are railwayed with `Result`.\n- `Ok` type can be any value.\n- `Error` type is `Map\u003cstring, string list\u003e` and the error response is a json of problem details with the status code 422 Unprocessable Entity.\n\n#### Normal with `=\u003e`\n\n```fsharp\nlet enrich (ctx: HttpContext) =\n    ctx.Response.GetTypedHeaders().Set(\"X-Powered-By\", \"AspFeat\")\n    Task.CompletedTask\n\nlet configureEndpoints bld =\n    uhttp bld Get \"/\" (enrich =\u003e write \"hello world\")\n```\n\n#### Single value injection with `=|`\n\n`Ok` type could be:\n  - A route value\n  - A tuple of route values\n  - A deserialized JSON model\n  - Or any mapped value\n\n```fsharp\nlet validateGetEcho id ctx =\n    if id \u003e 0\n    then Ok id\n    else Map [ (\"Id\", [ \"Is negative or zero\" ]) ] |\u003e Error\n    |\u003e Task.FromResult\n\nlet getEcho id =\n    write $\"Echo {id}\"\n\nlet configureEndpoints bld =\n    uhttpf bld Get  \"/{id:int}\" (validateGetEcho =| getEcho)\n```\n\n#### Double value injection with `=||`\n\n`Ok` type is a two-value tuple that is then passed to the next function as two parameters.\\\nIt could be:\n  1. Fist, route values\n  2. Second, deserialized JSON model\n\n```fsharp\nlet validateCreateEcho id name ctx =\n    if not (String.IsNullOrWhiteSpace name)\n    then Ok (id, {| Id = id; Name = name |})\n    else Map [ (\"Name\", [ \"Is empty\" ]) ] |\u003e Error\n    |\u003e Task.FromResult\n\nlet createEcho id model =\n    //...\n    createdWith $\"/{id}\" model\n\nlet configureEndpoints bld =\n    uhttpfj bld Post \"/{id:int}\" (validateCreateEcho =|| createEcho)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakhansari%2Faspfeat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakhansari%2Faspfeat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakhansari%2Faspfeat/lists"}