Skip to content

Repository files navigation

drawing

OpenAPI Type Provider

Erased netstandard2.0 type provider for web API providers.

Why to use this type provider?

If you write F# backend for some application providing JSON API, you probably want to document this using OpenAPI v3 specification (previously called Swagger). This documentation can be created basically two ways:

  1. Generate it from code (code first approach)
  2. Write it manually (document first approach)

This type provider is focused on second option when you already got existing documentation (e.g. from frontend developer) and you want to be 100% sure that your API follows it correctly, which mostly means two things: Validation of requests payload is in correct form (as described in API documentation) and creating responses. Both things can be quite tedious and error-prone. It is common in web API development that after some time that server behavior is not what is written in documentation. This type provider is here to help with that.

Goals of OpenAPITypeProvider

When I started to think about writing this type provider, I set few goals:

  1. Netstandard2.0 support
  2. Erased type
  3. Tightly connected to Newtonsoft.Json
  4. Based on the latest OpenAPI specification (no support for Swagger)

Version 2.0.0 change

In versions < 2.0, the Schemas were created based on simplified logic: If the schema is the same (having same structure), it is considered to be the same schema no matter name you use (actually the first parsed named is used for all others). This approach had good intentions - to minimize amount of created Schemas - however shown to be wrong for complex scenarios. This could easily lead to situation where change in one Schema breaks your other Schema, even if they were not directly linked using $ref.

Since version 2.0.0 the Schemas are created based on logic:

  1. Root inline schemas are always created as separated one
  2. Root $ref schemas are created as separated one but with structure "copied" from referenced schema
  3. Nested inline schemas are created as separated one
  4. Nested $ref schemas are linked to referenced schema

Instalation

First install NuGet package

Install-Package OpenAPITypeProvider

or using Paket

nuget OpenAPITypeProvider

How to use

First open correct namespace and create basic type based on your YAML documentation

openOpenAPITypeProvidertypePetStore= OpenAPIV3Provider<"PetStore.yaml">

Now you can use defined Schemas in your specification like F# types.

Parsing from JSON

Each Schema type can be created from JSON string using static method Parse.

letjson="""{"name":"Roman"}"""letpet= PetStore.Schemas.NewPet.Parse(json)letname= pet.Name // contains Romanlettag= pet.Tag // contains Option value None

Sometimes you need to use parse JSON with custom date format.

letjson="""{"date1":"31. 12. 2018 12:34:56","date2":"31. 12. 2017 12:34:56"}"""letcustomDateFormat="dd. MM. yyyy HH:mm:ss"lettwoDates= PetStore.Schemas.TwoDates.Parse(json, customDateFormat)

Method Parse throws an exception in case JSON does not fit to definition:

// fails with exception that property 'name' is not present,// but should be based on Schema definitionletjson="""{"notExistingProperty":"Roman"}"""letpet= PetStore.Schemas.NewPet.Parse(json)// fails with exception that property 'name' not convertible// to string valueletjson="""{"name":123456}"""letpet= PetStore.Schemas.NewPet.Parse(json)

Instantiation of Schema types

Schema types has constructors based on definition so you can instantiate them as you need.

letpet=new PetStore.Schemas.NewPet(name ="Roman")

Converting to JSON

Each Schema instance has method ToJson with few overloads.

letpet=new PetStore.Schemas.NewPet(name ="Roman")
pet.ToJson()// returns '{"name":"Roman"}' - no indenting
pet.ToJson(Newtonsoft.Json.Formatting.Indented)// return json with fancy formatting

Converting to JSON with custom serializer

Each ToJson method has overload with supporting JsonSerializerSettings as parameter.

letpet=new PetStore.Schemas.NewPet(name ="Roman")letsettings= JsonSerializerSettings()
settings.NullValueHandling <- NullValueHandling.Include
pet.ToJson(settings, Formatting.None)// returns '{"name":"Roman","tag":null}'

Converting to Newtonsoft JToken

If you need JToken instead of string with JSON, method ToJToken is here for you.

letpet=new PetStore.Schemas.NewPet(name ="Roman")letjtoken= pet.ToJToken()

Again, you can customize how to handle optional values.

letpet=new PetStore.Schemas.NewPet(name ="Roman")letjtoken= pet.ToJToken(NullValueHandling.Include)// this now contains JNull value inside JObject

Simple values

By specification you are allowed to have Schema types not an objects, but simple values like strings or integers. This type provider supports them as well.

SimpleString:
type: string SimpleArray:
type: arrayitems:
type: string
letsimpleString= PetStore.Schemas.SimpleString("ABC")
simpleString.Value // contains "ABC"letsimpleArray= PetStore.Schemas.SimpleArray(["A";"B"])
simpleArray.Values // contains List<string> ["A";"B"]

Requests & ResponseBodies

Using Schema types directly is quite handy and straightforward, but it doesn't say anything about routes, requests and responses. If you want to be 100% sure that you are fullfilling specification, go for Parse on Requests and ToJson / ToJToken methods on ResponseBodies.

letpetStoreAPI=new PetStore()// Note! Instance of PetStore type is needed here.letpet=new PetStore.Schemas.NewPet("Roman")// this route returns NewPet schema by definition so ToJson allows only NewPet schema as parameter
petStoreAPI.Paths.``/pets/{id}``.Get.Responses.``200``.``application/json``.ToJson(pet)// this route expects NewPet schema by definition so Parse method returns NewPetletparsedPet= petStoreAPI.Paths.``/pets``.Post.RequestBody.``application/json``.Parse(jsonFromRequest)

In case you have any doubts, you can always have a look at unit tests

Known issues & Limitations

  1. No support for OneOf and ManyOf since they are basically union types which is quite difficult (or maybe impossible) to generate from type provider

Contribution

You are more than welcome to send a pull request if you find some bug or missing functionality.

About

F# type provider for Open API specification

Topics

Resources

Stars

65 stars

Watchers

9 watching

Forks

Releases

Packages

Used by

Contributors

Languages