https://github.com/lucasteles/fsharp.minimalapi
Library to use AspNet Core Minimal Api with F#
https://github.com/lucasteles/fsharp.minimalapi
Last synced: 6 months ago
JSON representation
Library to use AspNet Core Minimal Api with F#
- Host: GitHub
- URL: https://github.com/lucasteles/fsharp.minimalapi
- Owner: lucasteles
- Created: 2023-02-15T22:05:35.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2024-12-13T21:46:41.000Z (10 months ago)
- Last Synced: 2025-03-28T13:04:26.032Z (6 months ago)
- Language: F#
- Homepage:
- Size: 92.8 KB
- Stars: 93
- Watchers: 7
- Forks: 2
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[](https://github.com/lucasteles/FSharp.MinimalApi/actions/workflows/ci.yml)
[](https://www.nuget.org/packages/FSharp.MinimalApi)# FSharp.MinimalApi
Easily define your routes in your [ASP.NET Core MinimalAPI](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis) with [`TypedResults`](https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-7.0#typed-results-for-minimal-apis) support
## Getting started
[NuGet package](https://www.nuget.org/packages/FSharp.MinimalApi) available:
```ps
$ dotnet add package FSharp.MinimalApi
```> **💡** You can check a complete sample [HERE](https://github.com/lucasteles/FSharp.MinimalApi/tree/master/BasicApi)
## Defining Routes
```fsharp
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Http.HttpResultsopen FSharp.MinimalApi
open FSharp.MinimalApi.Builder
open type TypedResultstype CustomParams =
{ []
foo: int
[]
bar: string
[]
logger: ILogger }let routes =
endpoints {
get "/hello" (fun () -> "world")// request bindable parameters must be mapped to objects/records
get "/ping/{x}" (fun (req: {| x: int |}) -> $"pong {req.x}")get "/inc/{v:int}" (fun (req: {| v: int; n: Nullable |}) -> req.v + (req.n.GetValueOrDefault 1))
get "/params/{foo}" (fun (param: CustomParams) ->
param.logger.LogInformation "Hello Params"
$"route={param.foo}; query={param.bar}")// better static/openapi typing
get "/double/{v}" produces> (fun (req: {| v: int |}) -> Ok(req.v * 2))get "/even/{v}" produces, BadRequest> (fun (req: {| v: int; logger: ILogger<_> |}) ->
(if req.v % 2 = 0 then
// TypedResult relies havely on implict convertions
// the (!!) operator help us to call the implicit cast
!! Ok("even number!")
else
req.logger.LogInformation $"Odd number: {req.v}"
!! BadRequest()))
// nesting
endpoints {
group "user"
tags "Users"get "/" produces> (fun (req: {| db: MyDbContext |}) ->
task {
let! users = req.db.Users.ToArrayAsync()
return Ok(users)
})get "/{userId}" produces, NotFound> (fun (req: {| userId: Guid; db: MyDbContext |}) ->
task {
let! res = req.db.Users.Where(fun x -> x.Id = UserId req.userId).TryFirstAsync()match res with
| Some user -> return !! Ok(user)
| None -> return !! NotFound()
})// group mappping
route "profile" {
allowAnonymouspost
"/"
produces, Conflict, ValidationProblem>
(fun (req: {| userInfo: NewUser; db: MyDbContext |}) ->
task {
match NewUser.parseUser req.userInfo with
| Error err -> return !! ValidationProblem(err)
| Ok newUser ->
let! exists = req.db.Users.TryFirstAsync(fun x -> x.Email = newUser.Email)match exists with
| Some _ -> return !! Conflict()
| None ->
req.db.Users.add newUser
do! req.db.saveChangesAsync ()
return !! Created($"/user/{newUser.Id.Value}", newUser)
})delete "/{userId}" produces (fun (req: {| userId: Guid; db: MyDbContext |}) ->
task {
let! exists = req.db.Users.TryFirstAsync(fun x -> x.Id = UserId req.userId)match exists with
| None -> return !! NotFound()
| Some user ->
req.db.Users.remove user
do! req.db.saveChangesAsync ()
return !! NoContent()
})}
}
}[]
let main args =
let builder = WebApplication.CreateBuilder(args)
// ... builder configuration ...
app.MapGroup("api").WithTags("Root") |> routes.Apply |> ignore
// ... app configuration ...
app.Run()
0
```