Building an OData service in F# using Entity Framework and Suave
by Tamizh Vendan, Lead Consultant at Ajira Tech
See the original article here: https://fsharp.tv/gazettes/building-an-odata-service-in-f-using-entity-framework-and-suave-gazette-007-2/
OData (Open Data Protocol) is an OASIS standard that defines the best practice for building and consuming RESTful APIs. In this article, you are going to learn how to implement an OData service in F# using Entity Framework and Suave. We will be using PostgreSQL as the backend datastore.
Project Setup
We are going to have three projects in this implementation
Suave.OData.Core — Class Library for having Types and OData Combinators for Suave.
Suave.OData.EF — Class Library for defining DbContext and Entities.
Suave.OData.Web — Console Application for implementing functions that exposes the OData API using Suave.
Building an OData service in F# using Entity Framework and Suave
by Tamizh Vendan, Lead Consultant at Ajira Tech
OData (Open Data Protocol) is an OASIS standard that defines the best practice for building and consuming RESTful APIs. In this article, you are going to learn how to implement an OData service in F# using Entity Framework and Suave. We will be using PostgreSQL as the backend datastore.
Project Setup
We are going to have three projects in this implementation
Suave.OData.Core — Class Library for having Types and OData Combinators for Suave.
Suave.OData.EF — Class Library for defining DbContext and Entities.
Suave.OData.Web — Console Application for implementing functions that exposes the OData API using Suave.
In addition to these projects, we will be having the following files in the root directory.
A FAKE build script build.fsx that orchestrates the database migration and the build process
A paket.dependencies file specifying the NuGet packages the are required
source https://www.nuget.org/api/v2
nuget FAKE
A build.sh file to take care of downloading Paket, restoring the NuGet packages and invoking the FAKE build script.
Setting Up PostgreSQL DB Migration
Let’s start with creating a new database with the name mydb in your PostgreSQL instance
For doing database migration, instead of picking a .NET tool/library, let’s use a simple & novel approach using Node.js. We will be using the node-db-migrate to do the database migration.
To use this npm package, create and update the following two files in the root directory.
packages.json specifying the npm packages that we needed and a migrate npm command to run the db-migrate up.
A database.json to pass the database configuration for the node-db-migrate package.
Do replace the connection string with yours!
Then create the first migration file to define the schema by running the following command
node node_modules/db-migrate/bin/db-migrate create init
This would create a migrations directory and a file inside it with the name 20160713092223-init.js (the number may be different for you).
Let’s define the schema for the people table in this migration file
For Windows, FAKE’s NPM Helper uses the Node.Js Nuget packages to execute the npm commands. So, let’s install them.
For Other Platforms, it assumes a local installation of node and npm. So, You need to download and install them if you don’t have one. To pass the npm installation path, let’s add the following statement in the build.sh file.
Now it’s time to update the build.fsx file to invoke this npm run migrate command during the application build.
That’s all. If run the file ./build.sh, your database will have the shiny new people table in the database mydb
This Node.js based DB migration approach is my personal preference. I’ve used it here to showcase the NPM integration capability of the FAKE library.
Adding Entity Definition and DbContext
With the database table in place, the next step is to create a mapping Class People and expose it via DbContext. As a first step install the following NuGet packages and references it in the Suave.OData.EF project.
• EntityFramework
• Npgsql.EntityFramework
• Npgsql
• System.ComponentModel.Annotations
and follow it up with defining entities
Then define the DbContext for the mydb.
For more details on defining Entity Framework entities in F# refer this MSDN tutorial
The string MyDb in the DbContext(“MyDb”) is the name of the connection string that we need to add. We also need to add some configurations to use the Npgsql Data Provider for Entity Framework to access the PostgreSQL database.
Let’s add both in the Suave.OData.Web project’s config file
For the original article and full code see: https://fsharp.tv/gazettes/building-an-odata-service-in-f-using-entity-framework-and-suave-gazette-007-2/
JSON Handling
As we will be using JSON format for the request and response in the OData API, let’s add some combinators to handle it.
Add Suave and Newtonsoft.Json NuGet packages in the Suave.OData.Core and define the combinators.
Query By Id — “/people(id)”
Now it’s time to implement the OData API. In this section, we are going to implement the Suave OData Combinator for querying the resource by its id. As a good design practice, we need to have an abstraction between the OData Combinator and the underlying data access mechanism (EF in our case)
Let’s start by defining it in the Suave.OData.Core project.
Then define the FindById Combinator.
That’s all it required to create a OData combinator in Suave. Let’s leverage it to expose the Query by id API through the Suave.OData.Web project.
The resource function is kind of a bridge between the Entity Framework and the Resource type that we defined in the Core project. The things inside the <> are generic constraints that are required by the DbSet
Handling the error by printing the exception details and returning the Option type is being used for simplicity. If you want to have a robust error handling, it can be enhanced using Chessie.
The final piece is putting everything together and expose the OData API.
Now, Build and run the Suave.OData.Web project.
As there is no person added to the people table, you will get a 404 response. Just add a new row in the people table directly to see a 200 response!
To keep things simple, I am ignoring the Service Document and Metadata part of the OData.
Adding a new Resource “POST /people”
The first step in adding a new resource is to validate whether it is correct. In EF, we do the validation typically by using Data Annotations.
The next step is extending the Resource type to have the Add function.
and then adding the Suave combinators.
To expose it as OData API, we need to update the Add functionality in the Entity Framework Side.
We added a new parameter db to the resource function that carries the DbContext.
The final step is passing the db argument.
Adding a resource is now up and running!
Deleting a resource “DELETE /poeple(id)”
As we did in the previous section, let’s start with extending the Resource type.
The FindById combinator is already had what requires for implementing the DeleteById combinator. It takes a higher order function representing an action to be performed. So, for deleting a resource by id we just need to pass a different function!
The next step is adding the Delete operation on the Entity Framework side
Now, you can delete a resource by its id.
Updating a resource “PUT /people(id)”
As we did for the other requests, we will be starting with updating the Resource type.
Then we need to define the combinator.
The last step is updating the EfCrud.fs to handle the update request.
The ‘a :> Entity constraint has been added to update the ID property of the entity being updated.
Querying OData Endpoint
The interesting aspect of OData is its flexibility to query the data.
To implement it, we need to parse the query strings in the URL into a query expression(LINQ) and then query the model set using the expression.
In this example implementation, we will be using the Linq2Rest library which exactly does what we required.
Then update the EfCrud adapter.
Filter request is a blocking call as Linq2Rest doesn’t support async at this point of writing.
Summary
As the objective of this article is just to showcase a basic implementation of OData service in Suave, we haven’t covered much ground here. Some of the improvements include:
• Adding OData Metadata Support
• Replacing Linq2Rest with FParsec and making OData queries async
• Replacing Entity Framework with SQLProvider
In a nutshell, if you would like to extend Suave just add a new combinator! The complete source code is available in my GitHub repository
Book Promo
Liked what you read here and interested in learning more about Suave? Do check out Tamizhvendan’s new book F# Applied, Foreword by Don Syme, Henrik Feldt and Ademar Gonzalez.
“F# Applied” is an excellent introduction to applied, modern programming for the web. Starting with Suave, the F# server-side web framework, this book will teach you how to create complete applications using Functional-First Programming with F# In this book you will read:
• How to create complete application using Functional Programming Principles using F#
• An in-depth understanding of Web development in F# using Suave
• How to develop applications using EventSourcing, CQRS, and DDD in F#
• How to set up continuous integration and continuous deployment using FAKE and Docker
• How to leverage libraries like Rx, FSharp.Data and Paket
Author Bio
Tamizh is a Pragmatic, Passionate and Polyglot Programmer. He started his programming journey at the age of 12, and the passion has not left him since then.
He is a Full-Stack solution provider and has a wealth of experience in solving complex problems using different programming languages and technologies. F#, Node.js, Golang, Haskell are some of his favorites.
Tamizh is also a functional programming evangelist and blogs at P3 Programmer