Search code examples
f#type-providersf#-giraffe

How to fix empty objects returned by Giraffe API fetched from DB through SqlDataProvider


I am trying to learn F# by creating an F# application with a repository to fetch Exercises for a future Gym-application.

I use SqlServer to store the data and am fetching the data using Fsharp.Data.Sql. The unit tests for this work well.

I am trying to expose the data through a web api using Giraffe, the problem is that i get back a list with the correct length but with empty objects inside.

Here is the context:

module Context

open FSharp.Data.Sql

let [<Literal>] dbVendor = Common.DatabaseProviderTypes.MSSQLSERVER
let [<Literal>] connectionString = "Server=DESKTOP-20JMHNK;Database=GymStrongDB;Trusted_Connection=True;"
let [<Literal>] indivAmount = 1000
let [<Literal>] useOptTypes  = true
let [<Literal>] owner = "public, admin, references"

type sql =
    SqlDataProvider<
        dbVendor,
        connectionString,
        "",
        "",
        indivAmount,
        useOptTypes,
        owner>

let gymStrongContext = sql.GetDataContext()

Here is the Repository

namespace GymStrong.Repositories

open ...

module ExerciseRepository =
    let ctx = gymStrongContext.Dbo

    let getExercises = 
        ctx.Exercises |> Seq.toList

    let getExercisesHandler : HttpHandler =
        fun (next : HttpFunc) (ctx : HttpContext) ->
            Successful.OK getExercises next ctx

And here is the relevant part from Program.fs

open ...

let webApp =
    choose [
        GET >=> 
            choose [
                route "/" >=> text "Hello World"
                route "/user" >=> mustBeLoggedIn >=> getLoggedInUser
                route "/logout" >=> mustBeLoggedIn >=> logoutHandler
                route "/exercises" >=> mustBeLoggedIn >=> getExercisesHandler
                ]
        POST >=> 
            choose [
                route "/register" >=> registerHandler
                route "/login" >=> loginHandler
            ]
    ]

In the unit-tests, this works properly. The unit tests are written in C#.

When i consume the API through PostMan i get this response

[{},{}]

Which has the correct length since i have two exercises in the database, but the objects seem empty.


Solution

  • This might be useful to someone, so i will post it.

    Both Giraffe and SqlDataProvider are doing what they should. The problem was that i thought that i could pass the provided type as a json-response in the API. That is wrong since the provided types are not real types and are "erased" (instead of Generated which is the type that i thought i was working with).

    In order to fix this, i needed to map out the values to a "real" anemic DTO object and pass it as a response. Now i need to figure out if one can provide these anemic DTO-objects based on the entityTypes and have them be generated and usable from other assemblies.

    Additional code:

    In the repository/service

        type exerciseDto = { 
            Id : string;
            Name : string;
            Description : string;
            Owner : string;
            Type : int; 
        }
    
        let getExercises = 
            ctx.Exercises |> Seq.map(fun exercise -> exercise.MapTo<exerciseDto>()) |> Seq.toList
    
    

    And in program.cs

    let handlerWrapper action : HttpHandler =
        fun (next : HttpFunc) (ctxHttp : HttpContext) ->
            Successful.OK action next ctxHttp
    
    let webApp =
        choose [
            GET >=> 
                choose [
                    route "/" >=> text "Hello World"
                    route "/user" >=> mustBeLoggedIn >=> getLoggedInUser
                    route "/logout" >=> mustBeLoggedIn >=> logoutHandler
                    route "/exercises" >=> mustBeLoggedIn >=> handlerWrapper getExercises
                    ]
            POST >=> 
                choose [
                    route "/register" >=> registerHandler
                    route "/login" >=> loginHandler
                ]
        ]
    

    Hope this helps anyone save time in the future. It took me at least a few hours.