Proxies in WebSharper, how, when, where!

Proxies in WebSharper, how, when, where!

When I first started to play with WebSharper, everything was nice and clean. I was coding in my own world/sandbox/project whatever you want to call it. But the thing is that most of the time, we don’t start from empty, blank project. What we do instead, is that we write code which integrates with an existing application. I was lucky enough to work on a project with a backend written in F# with a Domain library containing all the application domain written in F# as well. Being in this situation, the first think I tought of was:

Wouldn’t it be amazing if I could use the Domain library directly in my webapp?

Turns out it is absolutely possible! And we will see how in this post. There are multiple scenarios when referencing libraries and in this post we will address the following requirements:

  1. I want to use the record types defined in Domain library
  2. I want to use the record types with the functions attached to the record types from Domain library
  3. I want to use the functions from a module in Domain library

The fastest way to use a library is to reference WebSharperand add [<JavaScript>] to the main module or to every types and functions. If you are not willing to do that, you will need to proxy the types and functions depending on your needs. We will see how.

The full code sample is available on github.

I want to use the record types defined in Domain

If you just need to use the record types from Domain, you can reference WebSharper and add <WebSharperProject>Library</WebSharperProject> to fsproj. It indicates to WebSharper that this project needs to be compiled to JS.

If you can’t add WebSharper to the project and/or can’t add the configuration to fsproj, you will need to proxy the record type. You can find the official documentation here.

As an example, let’s pretend that your Domain library contains the follwing type:

type Dog =
    { Name: string
      Age : int }

If you want to proxy this type you will do the following in the project where you need to use that type:

[<JavaScript; Proxy(typeof<Dog>)>]
type Dog' =
    { Name: string
      Age: int }

In this example, I created a type Dog' which proxies the type in the Domain library. Thanks to that, in your client code, you can handle a Dog instance even though it won’t be compiled to JS.

[<JavaScript>]
module Client =  

    let dog: Dog =
        As({ Name = "dog"
             Age = 5 })
    
    let main =
        text dog.Name
        |> Doc.RunById "main"

We create a Dog' and then use As to cast it to a Dog. As is a very simple function which just take advantage of JS dynamic type to cast object. It is defined in WebSharper.JavaScript.Pervasives.

[<Inline "$x">]
let As<'T> (x: obj) = X<'T>

Thanks to that, we can now then call the members.

But what if Dog had some functions associated with it?

I want to use the record types with the functions attached to the record types from Domain library

Again, if you can, reference WebSharper to the library and add the configuration <WebSharperProject>Library</WebSharperProject> to fsproj. But this time on top of that, you have to decorate your type or module with [<JavaScript>].

[<JavaScript>]
type Dog =
    { Name: string
        Age : int }

    static member Walk (x: Dog) =
        x.Name + " is walking!"

This indicates to WebSharper that the functions need to be translated to JS. Without [<JavaScript>], the compilation will fail if you call Walk from your client code as WebSharper will not be able to find Walk.

If you can’t do that, then you will have to proxy the function. Just how we proxied the type earlier:

[<JavaScript; Proxy(typeof<Dog>)>]
type Dog' =
    { Name: string
      Age: int }

    static member Walk(x:Dog)  =
        x.Name + " is walking! - from proxy"

By adding the function to the proxy, we managed to proxy the type and its function. We can now call the function from our client side code.

[<JavaScript>]
module Client =  

    let dog: Dog =
        As({ Name = "dog"
             Age = 5 })
             
    let main =
        [ text (dog.Name)
          br [] :> Doc
          text (Dog.Walk dog) ]
        |> Doc.Concat
        |> Doc.RunById "main"

And the page will display dog is walking! - from proxy. It executes the content of the proxy instead of looking for the original. You can also proxy a function with some JS code. This is helpful because sometime things just don’t have a direct translation and you have to provide it manually. You can do that by using Direct or Inline.

[<JavaScript; Proxy(typeof<Dog>)>]
type Dog' =
    { Name: string
      Age: int }

    [<Inline "$x.Name + ' said hello instead of walking.'">]
    static member Walk(x:Dog)  = X<string>

If you never seen this attribute before, you can have a look here at a previous post I made on how to use external JS libraries.

I want to use the functions from a module in Domain library

Let’s say now you have a function in a module. It’s different than the previous examples as instead of having it attached to a type, it is directly defined in a module.

module Garden =
    open Animal

    let runInGarden (x: Cat) =
        x.Name + " is running in the garden!"

Again, the easiest way is to reference WebSharper and add the configuration <WebSharperProject>Library</WebSharperProject> to fsproj and [<JavaScript>] attribute to the module.

[<JavaScript>]
module Garden =
    open Animal

    let runInGarden (x: Cat) =
        x.Name + " is running in the garden!"

If you can’t do it, you will have to proxy the function. The difference here is that Garden is a module and not a type therefore we can’t do typeof<Garden>. What we need to do is specify that we wan’t to proxy the module by giving the full name of the module. For me the library is called Domain and the module Garden, so the proxy is defined like so:

[<JavaScript>]        
[<Proxy "Domain.Garden, Culture=neutral">]
module Garden' =

    let runInGarden (x: Cat) =
        x.Name + " run in second garden! - from proxy"

With that in place, you can call runInGarden and the proxy will be executed.

The full code sample is available on github.

Conclusion

We have seen most of the possibilities on how to reference a F# library with WebSharper. The main question that I wanted to answer here was whether it was possible to reference a library which itself has no reference to WebSharper and still be able to use the type and functions defined in it. The answer is yes. The advantage of proxying libraries is that you still use a set of familiar functions as those are your domain defined signatures. In fact, that’s how WebSharper give access to the .NET libraries. You can find all the proxies under stdlib/WebSharper.Main/Proxy here. I hope this post helped in demystifying how we can make our own proxy to use our own libraries! If you have any comments leave it here or hit me on Twitter @Kimserey_Lam. See you next time!

Comments

  1. Wow, that's pretty amazing. Thanks for posting about it.

    ReplyDelete
    Replies
    1. Hi Ben, thanks for reading! Happy it helped :)

      Delete

Post a Comment

Popular posts from this blog

A complete SignalR with ASP Net Core example with WSS, Authentication, Nginx

Verify dotnet SDK and runtime version installed

SDK-Style project and project.assets.json