I've written about how nice Freya is as a library, but documentation is still a little light on the ground.
So here's a minimal implementation of a "microservice" Freya API, starting from which dotnet commands to run to install the Freya template, through to a running web service.
Make sure you have an up to date .NET Core SDK installed, and grab yourself the handy dandy Freya template:
1
|
|
Then create yourself a directory and go into it. The following command will set up a brand new Freya project using kestrel as the underlying webserver, and Hopac (rather than F# Async) for concurrency. Alternatively, you can leave both the options off and you'll get Freya running on Suave with standard Async.
1
|
|
Your project should run at this point; dotnet run
will spin up a webserver on port 5000 which will give a 404 at the root and text responses on /hello and /hello/name paths.
Api.fs is where all the magic of configuring Freya happens - KestrelInterop.fs contains boilerplate for making sure Routing information passes correctly between Kestrel and Freya, and Program.fs just starts Kestrel with our Freya API as an OWIN app.
Adding JSON
So, this is great and all, but we're building a microservice aren't we? That normally means JSON (or at least something more structured than plain text!).
Let's change things up so that as well as supplying the name to greet in the route, we can POST JSON with a name field to the /hello end point.
To respond in JSON, we need a Freya Represent
record. We're sending a result with a fixed structure, so we don't need a serialization library or anything, we'll just construct the JSON by hand. Stick this near the top of Api.fs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
So here we're defining an HTTP representation of a response, including media type and other important information.
Aside: why do we return a lambda at the end rather than making representGreeting itself a function? That's so that we don't want to rebuild the two byte arrays and the regex every time we call the function.
We also need to be able to read incoming JSON. Well, all we want is a string so lets just check that there's an '"' at the beginning and end…
1 2 3 4 5 6 7 8 |
|
Now we can start hooking up the actual root that we want. We need to make some additions to helloMachine
:
1 2 3 4 5 6 |
|
Magically our endpoint now knows not only that we accept POSTs, but it will end the correct error code if the media type of the POST is not set to JSON.
We also need to update sayHello
and name
; we'll extract the method of the request and choose logic for working out the name and creating the response respectively.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
And that's everything we should need. Firing up PostMan we can find out that posting an empty body gets a 500 (we should probably handle that, looks like the request stream can be null), firing in a string with no media type header gets back a "415 Unsupported Media Type" (did you know that off hand?) and a POST with a correct body (i.e., starts and ends with a '"') gets us back:
1
|
|
So there you have it. Adding a POST endpoint to Freya.
Appendix
Here is the complete Api.fs for you to follow along, with open statements moved to the top of the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
|