{"id":16485537,"url":"https://github.com/zaid-ajaj/fable.remoting","last_synced_at":"2025-05-15T19:02:49.827Z","repository":{"id":39042781,"uuid":"85477441","full_name":"Zaid-Ajaj/Fable.Remoting","owner":"Zaid-Ajaj","description":"Type-safe communication layer (RPC-style) for F# featuring Fable and .NET Apps","archived":false,"fork":false,"pushed_at":"2024-05-01T09:20:00.000Z","size":32769,"stargazers_count":272,"open_issues_count":30,"forks_count":54,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-05-15T20:44:27.739Z","etag":null,"topics":["ajax","client-server","fable","giraffe","rpc","saturn","suave","type-safe","webpart"],"latest_commit_sha":null,"homepage":"https://zaid-ajaj.github.io/Fable.Remoting/","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zaid-Ajaj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-19T13:19:57.000Z","updated_at":"2024-06-18T20:03:39.974Z","dependencies_parsed_at":"2024-03-16T21:23:40.491Z","dependency_job_id":"6ee7e1e2-192a-4680-b2ce-c3cae558e4ae","html_url":"https://github.com/Zaid-Ajaj/Fable.Remoting","commit_stats":{"total_commits":691,"total_committers":42,"mean_commits":"16.452380952380953","dds":"0.40520984081041966","last_synced_commit":"3dc9952ee33a6b1abfba0ba1db9e1d45527cbd88"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zaid-Ajaj%2FFable.Remoting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zaid-Ajaj%2FFable.Remoting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zaid-Ajaj%2FFable.Remoting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zaid-Ajaj%2FFable.Remoting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zaid-Ajaj","download_url":"https://codeload.github.com/Zaid-Ajaj/Fable.Remoting/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247744335,"owners_count":20988783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ajax","client-server","fable","giraffe","rpc","saturn","suave","type-safe","webpart"],"created_at":"2024-10-11T13:26:11.166Z","updated_at":"2025-04-07T23:12:16.482Z","avatar_url":"https://github.com/Zaid-Ajaj.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fable.Remoting\n\n[![Build Status](https://travis-ci.org/Zaid-Ajaj/Fable.Remoting.svg?branch=master)](https://travis-ci.org/Zaid-Ajaj/Fable.Remoting) [![Build status](https://ci.appveyor.com/api/projects/status/euhwktyycm2wvvi4?svg=true)](https://ci.appveyor.com/project/Zaid-Ajaj/fable-remoting)\n\nFable.Remoting is a [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) communication layer for Fable and .NET apps, it abstracts away Http and Json and lets you think of your client-server interactions only in terms of pure stateless functions that are statically checked at compile-time:\n\n### Define a shared interface\nThis interface is a record type where each field is either `Async\u003c'T\u003e` or a function that returns `Async\u003c'T\u003e`\n\n```fs\ntype IGreetingApi = {\n  greet : string -\u003e Async\u003cstring\u003e\n}\n```\n\n### Implement the interface on the *server*\n\n```fs\nlet greetingApi = {\n  greet = fun name -\u003e\n    async {\n      let greeting = sprintf \"Hello, %s\" name\n      return greeting\n    }\n}\n\n// Expose the implementation as a HTTP service\nlet webApp =\n  Remoting.createApi()\n  |\u003e Remoting.fromValue greetingApi\n```\n\n### Call the functions from the *client*\n```fs\n// get a typed-proxy for the service\nlet greetingApi =\n  Remoting.createApi()\n  |\u003e Remoting.buildProxy\u003cIGreetingApi\u003e\n\n// Start using the service\nasync {\n  let! message = greetingApi.greet \"World\"\n  printfn \"%s\" message // Hello, World\n}\n```\nThat's it, no HTTP, no JSON and it is all type-safe.\n\n### Applications using Remoting\n- [SAFE-TodoList](https://github.com/Zaid-Ajaj/SAFE-TodoList) A simple full-stack Todo list application (beginner)\n- [tabula-rasa](https://github.com/Zaid-Ajaj/tabula-rasa) a real-world-ish blogging platform (intermediate)\n- [Yobo](https://github.com/Dzoukr/Yobo) Yoga Class Booking System\nimplemented with Event Sourcing (advanced)\n\n### [Full Documentation](https://zaid-ajaj.github.io/Fable.Remoting/)\n\nThe library runs everywhere on the backend: As Suave `WebPart`, as Giraffe/Saturn `HttpHandler` or any other framework as Asp.NET Core middleware. Clients can be Fable or .NET application.\n\n\u003e \"Fable.Remoting solves the age-old problem of keeping your front-end code in sync with your backend code at compile time, and in a language as enjoyable to use as F#\" - [David Falkner](https://twitter.com/ardave2002)\n\n## Quick Start\nUse the [SAFE Simplified template](https://github.com/Zaid-Ajaj/SAFE.Simplified) where Fable.Remoting is already set up and ready to go\n\n## Available Packages:\n\n| Library                     | Version                                                                                                                                             |\n| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Fable.Remoting.MsgPack      | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.MsgPack.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.MsgPack)           |\n| Fable.Remoting.Client       | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.Client.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.Client)             |\n| Fable.Remoting.Json         | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.Json.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.Json)                 |\n| Fable.Remoting.Server       | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.Server.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.Server)             |\n| Fable.Remoting.Suave        | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.Suave.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.Suave)               |\n| Fable.Remoting.Giraffe      | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.Giraffe.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.Giraffe)           |\n| Fable.Remoting.AspNetCore   | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.AspNetCore.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.AspNetCore)     |\n| Fable.Remoting.DotnetClient | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.DotnetClient.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.DotnetClient) |\n| Fable.Remoting.AzureFunctions.Worker | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.AzureFunctions.Worker.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.AzureFunctions.Worker) |\n| Fable.Remoting.AwsLambda | [![Nuget](https://img.shields.io/nuget/v/Fable.Remoting.AwsLambda.svg?colorB=green)](https://www.nuget.org/packages/Fable.Remoting.AwsLambda) |\n\n## Scaffold from scratch - Suave\nCreate a new F# console app:\n```\ndotnet new console -lang F#\n```\nDefine the types you want to share between client and server:\n```fs\n// SharedTypes.fs\nmodule SharedTypes\n\ntype Student = {\n    Name : string\n    Age : int\n}\n\n// Shared specs between Server and Client\ntype IStudentApi = {\n    studentByName : string -\u003e Async\u003cStudent option\u003e\n    allStudents : unit -\u003e Async\u003clist\u003cStudent\u003e\u003e\n}\n```\nThe type `IStudentApi` is very important, this is the specification of the protocol between your server and client. `Fable.Remoting` expects such type to only have functions returning `Async` on the final result:\n```fs\nAsync\u003cA\u003e\nA -\u003e Async\u003cB\u003e\nA -\u003e B -\u003e Async\u003cC\u003e\n// etc...\n```\nTry to put such types in seperate files to reference these files later from the Client\n\nThen provide an implementation for `IStudentApi` on the server:\n```fs\nopen SharedTypes\n\nlet getStudents() =\n  async {\n    return [\n        { Name = \"Mike\";  Age = 23; }\n        { Name = \"John\";  Age = 22; }\n        { Name = \"Diana\"; Age = 22; }\n    ]\n  }\n\nlet findStudentByName name =\n  async {\n    let! students = getStudents()\n    let student = List.tryFind (fun student -\u003e student.Name = name) students\n    return student\n  }\n\nlet studentApi : IStudentApi = {\n    studentByName = findStudentByName\n    allStudents = getStudents\n}\n```\nNow that we have the implementation `studentApi`, you can expose it as a web service from different web frameworks. We start with [Suave](https://github.com/SuaveIO/suave)\n\n\nInstall the library from Nuget using Paket:\n\n```\npaket add Fable.Remoting.Suave --project /path/to/Project.fsproj\n```\nCreate a [WebPart](https://suave.io/composing.html) from the value `studentApi`\n```fs\nopen Suave\nopen Fable.Remoting.Server\nopen Fable.Remoting.Suave\n\nlet webApp : WebPart =\n    Remoting.createApi()\n    |\u003e Remoting.fromValue studentApi\n    |\u003e Remoting.buildWebPart\n\n// start the web server\nstartWebServer defaultConfig webApp\n```\nYes, it is that simple.\nYou can think of the `webApp` value as if it was the following in pseudo-code:\n```fs\nlet webApp =\n choose [\n  POST\n   \u003e=\u003e path \"/IStudentApi/studentByName\"\n   \u003e=\u003e (* deserialize request body (from json) *)\n   \u003e=\u003e (* invoke studentApi.getStudentByName with the deserialized input *)\n   \u003e=\u003e (* give client the output back serialized (to json) *)\n\n // other routes\n ]\n```\nYou can enable diagnostic logging from Fable.Remoting.Server (recommended) to see how the library is doing it's magic behind the scenes :)\n```fs\nlet webApp =\n    Remoting.createApi()\n    |\u003e Remoting.fromValue studentApi\n    |\u003e Remoting.withDiagnosticsLogger (printfn \"%s\")\n    |\u003e Remoting.buildWebPart\n```\n### AspNetCore Middleware\nInstall the package from Nuget using paket\n```\npaket add Fable.Remoting.AspNetCore --project /path/to/Project.fsproj\n```\nNow you can configure your remote handler as AspNetCore middleware\n```fs\nlet webApp =\n    Remoting.createApi()\n    |\u003e Remoting.fromValue studentApi\n\nlet configureApp (app : IApplicationBuilder) =\n    // Add Remoting handler to the ASP.NET Core pipeline\n    app.UseRemoting webApp\n\n[\u003cEntryPoint\u003e]\nlet main _ =\n    WebHostBuilder()\n        .UseKestrel()\n        .Configure(Action\u003cIApplicationBuilder\u003e configureApp)\n        .Build()\n        .Run()\n    0\n```\n\n### Giraffe\n\nYou can follow the Suave part up to the library installation, where it will become:\n```\npaket add Fable.Remoting.Giraffe --project /path/to/Project.fsproj\n```\n\nNow instead of a WebPart, by opening the `Fable.Remoting.Giraffe` namespace, you will get a [HttpHandler](https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUMENTATION.md#httphandler) from the value `server`:\n```fs\nopen Giraffe\nopen Fable.Remoting.Server\nopen Fable.Remoting.Giraffe\n\nlet webApp : HttpHandler =\n    Remoting.createApi()\n    |\u003e Remoting.fromValue studentApi\n    |\u003e Remoting.buildHttpHandler\n\nlet configureApp (app : IApplicationBuilder) =\n    // Add Giraffe to the ASP.NET Core pipeline\n    app.UseGiraffe webApp\n\nlet configureServices (services : IServiceCollection) =\n    // Add Giraffe dependencies\n    services.AddGiraffe() |\u003e ignore\n\n[\u003cEntryPoint\u003e]\nlet main _ =\n    WebHostBuilder()\n        .UseKestrel()\n        .Configure(Action\u003cIApplicationBuilder\u003e configureApp)\n        .ConfigureServices(configureServices)\n        .Build()\n        .Run()\n    0\n```\n\n### Saturn\n\nYou can use the same `webApp` generated by the Giraffe library.\n\n```fs\nopen Saturn\nopen Fable.Remoting.Server\nopen Fable.Remoting.Giraffe\n\nlet webApp : HttpHandler =\n    Remoting.createApi()\n    |\u003e Remoting.fromValue studentApi\n    |\u003e Remoting.buildHttpHandler\n\nlet app = application {\n    url \"http://127.0.0.1:8083/\"\n    use_router webApp\n}\n\nrun app\n```\n\n### Azure Functions (isolated)\n\nTo use Azure Functions in isolated mode with custom HttpTrigger as serverless remoting server, just install:\n```\ndotnet add package Fable.Remoting.AzureFunctions.Worker\n```\nor using paket\n```\npaket add Fable.Remoting.AzureFunctions.Worker --project /path/to/Project.fsproj\n```\n\nSince Azure Functions don't know anything about [HttpHandler](https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUMENTATION.md#httphandler) we need to use built-in `HttpRequestData` and `HttpResponseData` objects. Luckily we have `Remoting.buildRequestHandler` and `HttpResponseData.fromRequestHandler` functions to the rescue:\n```fs\nopen Fable.Remoting.Server\nopen Fable.Remoting.AzureFunctions.Worker\nopen Microsoft.Azure.Functions.Worker\nopen Microsoft.Azure.Functions.Worker.Http\nopen Microsoft.Extensions.Logging\n\ntype Functions(log:ILogger\u003cFunctions\u003e) =\n    \n    [\u003cFunction(\"Index\")\u003e]\n    member _.Index ([\u003cHttpTrigger(AuthorizationLevel.Anonymous, Route = \"{*any}\")\u003e] req: HttpRequestData, ctx: FunctionContext) =\n        Remoting.createApi()\n        |\u003e Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix\n        |\u003e Remoting.fromValue myImplementation\n        |\u003e Remoting.buildRequestHandler\n        |\u003e HttpResponseData.fromRequestHandler req\n```\n\nOf course, having one implementation per Function App is not ideal, so `HttpResponseData.fromRequestHandlers` is here to the rescue:\n\n```fs\ntype Functions(log:ILogger\u003cFunctions\u003e) =\n    \n    [\u003cFunction(\"Index\")\u003e]\n    member _.Index ([\u003cHttpTrigger(AuthorizationLevel.Anonymous, Route = \"{*any}\")\u003e] req: HttpRequestData, ctx: FunctionContext) =\n        let handlerOne =\n            Remoting.createApi()\n            |\u003e Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix\n            |\u003e Remoting.fromValue myImplementationOne\n            |\u003e Remoting.buildRequestHandler\n        \n        let handlerTwo =\n            Remoting.createApi()\n            |\u003e Remoting.withRouteBuilder FunctionsRouteBuilder.apiPrefix\n            |\u003e Remoting.fromValue myImplementationTwo\n            |\u003e Remoting.buildRequestHandler\n        \n        [ handlerOne; handlerTwo ] |\u003e HttpResponseData.fromRequestHandlers req\n```\n\n\n## Fable Client\nInstall `Fable.Remoting.Client` from nuget using Paket:\n```\npaket add Fable.Remoting.Client --project /path/to/Project.fsproj\n```\nReference the shared types to your client project\n```\n\u003cCompile Include=\"path/to/SharedTypes.fs\" /\u003e\n```\nStart using the library:\n```fs\nopen Fable.Remoting.Client\nopen SharedTypes\n\n// studentApi : IStudentApi\nlet studentApi =\n    Remoting.createApi()\n    |\u003e Remoting.buildProxy\u003cIStudentApi\u003e\n\nasync {\n  // students : Student[]\n  let! students = studentApi.allStudents()\n  for student in students do\n    // student : Student\n    printfn \"Student %s is %d years old\" student.Name student.Age\n}\n|\u003e Async.StartImmediate\n```\nFinally, when you are using `webpack-dev-server`, you have to change the config from this:\n```js\ndevServer: {\n  contentBase: resolve('./public'),\n  port: 8080\n}\n```\nto this:\n```js\ndevServer: {\n  contentBase: resolve('./public'),\n  port: 8080,\n  proxy: {\n    '/*': { // tell webpack-dev-server to re-route all requests from client to the server\n      target: \"http://localhost:5000\",// assuming the backend server is hosted on port 5000 during development\n      changeOrigin: true\n    }\n}\n```\nThat's it!\n\n## Dotnet Client\nYou can also use client functionality in non-fable projects, such as a console, desktop or mobile application.\n\nInstall `Fable.Remoting.DotnetClient` from nuget using Paket:\n```\npaket add Fable.Remoting.DotnetClient --project /path/to/Project.fsproj\n```\nReference the shared types in your client project\n```\n\u003cCompile Include=\"path/to/SharedTypes.fs\" /\u003e\n```\nStart using the library:\n```fs\nopen Fable.Remoting.DotnetClient\nopen SharedTypes\n\n// studentApi : IStudentApi\nlet studentApi =\n  Remoting.createApi \"http://localhost:8085\"\n  |\u003e Remoting.buildProxy\u003cIStudentApi\u003e\n\nasync {\n  // students : Student[]\n  let! students = studentApi.allStudents()\n  for student in students do\n    // student : Student\n    printfn \"Student %s is %d years old\" student.Name student.Age\n}\n|\u003e Async.StartImmediate\n```\nNotice here, that unlike the Fable client, you will need to provide the base Url of the backend because the dotnet client will be deployed separately from the backend.\n\n## Adding a new route\n - Add another record field function to `IStudentApi`\n - Implement that function\n - Restart server and client\n\nDone! You can now use that function from the client too.\n\n\nSee the following article if you are interested in how this library is implemented (a bit outdated but gives you an overview of the mechanism)\n[Statically Typed Client-Server Communication with F#: Proof of Concept](https://medium.com/@zaid.naom/statically-typed-client-server-communication-with-f-proof-of-concept-7e52cff4a625#.2ltqlajm4)\n\n### [In-depth Introduction (Blog)](https://medium.com/@zaid.naom/introducing-fable-remoting-automated-type-safe-client-server-communication-for-fable-apps-e567454d594c)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaid-ajaj%2Ffable.remoting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaid-ajaj%2Ffable.remoting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaid-ajaj%2Ffable.remoting/lists"}