RestProvider


REST Provider: Type providers made easy

F# type providers are amazing - they can be used for a wide range of things such as reading data in CSV, JSON and XML formats, accessing SQL databases, reading data exposed by international organizations such as the World Bank, interoperating with the R langauge, but also fun things like playing adventure games in your editor.

Sadly, writing type providers is not very easy. You have to use fairly complicated provided types API and learn how to use advanced F# features like <@@ "code" + "quotations" @@>. Wouldn't it be nice if you could create type providers without all this complexity? The REST provider is a project that does exactly this. The idea is simple:

  • You write a simple REST service that provides endpoints representing the different provided types and returns information about the types in a simple JSON format. You can write the service in F# or anything else you fancy.

  • You pass the URL for your service to REST type provider by writing RestProvider<"http://myservice/provider"> and the REST provider takes care of doing all the magic that is needed to provide types.

Minimal type provider sample

You can find a number of samples in the REST provider repository and you can read more about the protocol and other fun examples in the detailed documentation (see links below), but just to give you a taste - the following calls the minimal sample provider that returns information about cities:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
#r "TheGamma.RestProvider.dll"
open TheGamma

type cities = RestProvider<"http://localhost:10042/minimal">

cities.London.Population
cities.``New York``.Population

When you clone the repository, you can start the server that hosts sample providers by running build RunServers. This will start a lightweight Suave server that runs on port 10042 which is then called by the RestProvider. In the above example, we look at population of two cities using a type representing a city with members London and New York. The members return value of another type representing an indicator which has a member Population.

Implementing the type provider protocol

The server that is behind the above type provider is very simple. The REST provider first makes a request to the base URL passed in the type provider argument:

GET /minimal/

1: 
2: 
3: 
4: 
[ { "name":"London", "trace":["London"],
    "returns":{"kind":"nested", "endpoint":"/city"} },
  { "name":"New York","trace":["NYC"],
    "returns":{"kind":"nested","endpoint":"/city"} }]

The response returns a list of members with name (the name of the property). The returns field specifies the type of the result - here, we are just saying that the result is another provided type that can be obtained by making a relative request to /city. The trace field represents an information that will be used later once we try to get the data - you can think of /city as a type and trace values ("London" or "NYC") as values of that type. When you type cities.London, the provider requests type for the city:

GET /minimal/city

1: 
2: 
3: 
4: 
[ { "name":"Population", "trace":["Population"],
    "returns":{"kind":"primitive","type":"int","endpoint":"/data"} },
  { "name":"Settled", "trace":["Settled"],
    "returns":{"kind":"primitive","type":"int","endpoint":"/data"} }]

As before, the /city type has a number of members (that will appear as properties). They both add a value to trace to track what data we want to get at the end. The returns field now says that the result is a primitive type int. It also specifies the endpoint for accessing the data. The endpoint is called when you evaluate cities.London.Population:

POST /minimal/data with body London&Population

1: 
538689

The provider accumulates trace values that are generated by the individual members that we encounter as we "dot through" the provided types, concatenates it using & and passes it as the body to the /data endpoint. For primitive int types, the endpoint just returns the number.

More information

Work in progress

This project is still very experimental and everything is likely going to change, including the protocol that is used for the communication between the REST server and the type provider. There are many things that the provider should, but does not support yet (say, providing methods) and the protocol is too simplistic. If you have thoughts, please comment on GitHub issues!

This documentation contains some more information about the current version of the protocol and also a few examples for creating providers (see the menu on the right for the latests links). You can also browse the Suave sources directory on GitHub, which contains sample data providers written using Suave.

Contributing and license

The project is hosted on GitHub where you can report issues, fork the project and submit pull requests. If you're adding a new public API, please also consider adding samples that can be turned into a documentation.

The library is available under the Apache 2.0 license, which allows modification and redistribution for both commercial and non-commercial purposes. For more information see the License file in the GitHub repository.

namespace TheGamma
type cities = RestProvider<...>

Full name: Index.cities
Multiple items
namespace RestProvider

--------------------
type RestProvider

Full name: TheGamma.RestProvider



    <summary>A provider</summary>
    <param name="Source">A thing</param>
property RestProvider<...>.London: RestProvider<...>.types.city


<summary>//city</summary>
property RestProvider<...>.types.city.Population: int


<summary>/primitive</summary>
Fork me on GitHub