FsGrpc 1.0.6
dotnet add package FsGrpc --version 1.0.6
NuGet\Install-Package FsGrpc -Version 1.0.6
<PackageReference Include="FsGrpc" Version="1.0.6" />
paket add FsGrpc --version 1.0.6
#r "nuget: FsGrpc, 1.0.6"
// Install FsGrpc as a Cake Addin #addin nuget:?package=FsGrpc&version=1.0.6 // Install FsGrpc as a Cake Tool #tool nuget:?package=FsGrpc&version=1.0.6
FsGrpc
Idiomatic F# code generation for Protocol Buffers and gRPC
Generate idiomatic F# records from proto3 message definitions, complete with oneofs as discriminated unions, and serialize/deserialize to and from protocol buffer wire format.
Getting Started
There are a couple of approaches you can use to generate code from your protos.
Option 1: Use FsGrpc.Tools
dotnet add package FsGrpc.Tools
Include protos in .fsproj with Protobuf
. For example:
<ItemGroup>
<Protobuf Include="protos\**" />
</ItemGroup>
Option 2: Use the buf cli
buf.gen.yaml
version: v1
plugins:
- remote: buf.build/divisions-maintenance-group/plugins/fsharp
out: gen
strategy: all
Then run buf generate
Include generated protos in your .fsproj inside top level <project>
element:
<Import Project="gen/Protobuf.targets" />
Add fsgrpc
dotnet add package fsgrpc
Usage in F#
You can create a record by specifying all of the fields or using with
syntax as follows:
let message =
{ MyMessage.empty with
Name = "a name value"
Description = "some string here" }
You can serialize/deserialize from various formats:
Serializing to/from bytes
let encodedMessage = FsGrpc.Protobuf.encode message
let decodedMessage: MyMessage = FsGrpc.Protobuf.decode encodedMessage
Serializing to/from a CodedOutputStream
let decodedMessage = MyMessage.Proto.Value.Decode cis
MyMessage.Proto.Value.Encode cos decodedMessage
Serializing to/from json
let serializedMessage = FsGrpc.Json.serialize message
let deserializedMessage : Http = FsGrpc.Json.deserialize serializedMessage
Implementing a GRPC service
Starting from a protobuf service defined like the following:
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc SayHelloServerStreaming (HelloRequest) returns (stream HelloReply);
rpc SayHelloClientStreaming (stream HelloRequest) returns (HelloReply);
rpc SayHelloDuplexStreaming (stream HelloRequest) returns (stream HelloReply);
}
The implementation can then look like:
open FSharp.Control //from the FSharp.Control.AsyncSeq package
type GRPCService () =
inherit Greeter.ServiceBase()
override _.SayHello (request: HelloRequest) (context: ServerCallContext) =
task { return { Message = "Hello " + request.Name } }
override _.SayHelloServerStreaming (request: HelloRequest) (writer: IServerStreamWriter<HelloReply>) (context: ServerCallContext) =
task {
for i in [1..5] do
do! writer.WriteAsync { Message = "Hello " + request.Name }
}
override _.SayHelloClientStreaming (requestStream: IAsyncStreamReader<HelloRequest>) (context: ServerCallContext) =
task {
let! names =
AsyncSeq.ofAsyncEnum (requestStream.ReadAllAsync())
|> AsyncSeq.toListAsync
let names = List.map (fun x -> x.Name) names
return { Message = $"""Hello {String.concat ", " names}""" }
}
override _.SayHelloDuplexStreaming
(requestStream: IAsyncStreamReader<HelloRequest>)
(writer: IServerStreamWriter<HelloReply>)
(context: ServerCallContext)
=
AsyncSeq.ofAsyncEnum (requestStream.ReadAllAsync())
|> AsyncSeq.iterAsync (fun x ->
writer.WriteAsync { Message = "Hello " + x.Name }
|> Async.AwaitTask)
|> fun x -> task { do! x }
let builder = WebApplication.CreateBuilder()
do
builder.Services.AddGrpc() |> ignore
builder.WebHost
.ConfigureKestrel(fun serverOptions ->
serverOptions.ConfigureEndpointDefaults(fun listenOptions ->
listenOptions.Protocols <- Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2))
.UseShutdownTimeout(TimeSpan.FromSeconds(60))
|> ignore
let mutable app = Unchecked.defaultof<WebApplication>
app <- builder.Build()
app.MapGrpcService<GRPCService>() |> ignore
app.StartAsync().Wait()
Usage System Diagram
C4Context
Container_Boundary(workstation, "Developer Workstation", $link="https://github.com/plantuml-stdlib/C4-PlantUML") {
Container_Boundary(fsgrpc_repository, "FsGrpc Repository"){
Component(protoc_fsgrpc_plugin_local, "Protoc FsGrpc Plugin", "local protoc-gen-fsgrpc")
}
Container_Boundary(your_fsharp_project, "Your F# project"){
Component(projectfile, "Web Application fsproj")
Component(generatedcode, "Generated F# Protobuf Code", "F# representations of your protobuf schema")
}
}
Boundary(nuget, "Nuget"){
Component(fsgrpc_nuget, "FsGrpc Package", "FsGrpc as a nuget package")
}
Boundary(buf, "buf.build"){
Component(protoc_fsgrpc_plugin, "Protoc FsGrpc Plugin", "Hosted protoc-gen-fsgrpc as a service")
}
UpdateElementStyle(workstation, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(your_fsharp_project, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(protoc_component, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(fsgrpc_component, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(buf, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(nuget, $fontColor="blue", $borderColor="blue", $legendTest=" ")
Rel(protoc_fsgrpc_plugin, generatedcode, "generates (Buf)")
UpdateRelStyle(protoc_fsgrpc_plugin, generatedcode, $textColor="red", $lineColor="red")
Rel(projectfile, fsgrpc_nuget, "references")
UpdateRelStyle(projectfile, fsgrpc_nuget, $textColor="red", $lineColor="red")
Rel(protoc_fsgrpc_plugin_local, generatedcode, "generates (offline)")
UpdateRelStyle(protoc_fsgrpc_plugin_local, generatedcode, $textColor="red", $lineColor="red")
Status
We are using this for production and it is very stable. See below for status of individual features
The major features intended are:
- Protobuf Messages as immutable F# record types
- Oneofs as Discriminated Unions
- proto3 optional keyword support
- Support for optional wrapper types (e.g. google.protobuf.UInt32Val)
- Support for well-known types Duration and Timestamp (represented using NodaTime types)
- Automatic dependency-sorted inclusion of generated .fs files
- Buf.build integration
- Comment pass-through
- Protocol Buffer reflection
- Idiomatic functional implementation for gRPC endpoints
Contributing
Development System Diagram
C4Context
Container_Boundary(workstation, "Developer Workstation", $link="https://github.com/plantuml-stdlib/C4-PlantUML") {
Container_Boundary(your_fsharp_project, "Your F# project"){
Component(generatedcode, "Generated F# Protobuf Code", "F# representations of your protobuf schema")
Component(projectfile, "Web Application fsproj")
}
Component(protoc_fsgrpc_plugin_local, "Protoc FsGrpc Plugin", "local protoc-gen-fsgrpc")
}
Boundary(fsgrpc_repository, "FsGrpc Repository"){
Boundary(protoc_component, "protoc FsGrpc Plugin"){
Component(protoc_gen_fsgrpc,"protoc-gen-fsgrpc", "Generates F# representations of your protobuf schema")
Component(protoc_gen_fsgrpc_tests, "Tests")
}
Boundary(fsgrpc_component, "FsGrpc"){
Component(fsgrpc, "FsGrpc", "Support Library for generated code")
Component(fsgrpc_tests, "FsGrpc.Tests")
}
}
Boundary(buf, "buf.build"){
Component(protoc_fsgrpc_plugin, "Protoc FsGrpc Plugin", "Hosted protoc-gen-fsgrpc as a service")
}
Boundary(nuget, "Nuget"){
Component(fsgrpc_nuget, "FsGrpc Package", "FsGrpc as a nuget package")
}
UpdateElementStyle(workstation, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(your_fsharp_project, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(fsgrpc_repository, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(protoc_component, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(fsgrpc_component, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(buf, $fontColor="blue", $borderColor="blue", $legendTest=" ")
UpdateElementStyle(nuget, $fontColor="blue", $borderColor="blue", $legendTest=" ")
Rel(generatedcode, fsgrpc, "depends on")
UpdateRelStyle(generatedcode, fsgrpc, $textColor="red", $lineColor="red")
Rel(fsgrpc, fsgrpc_nuget, "publishes")
UpdateRelStyle(fsgrpc, fsgrpc_nuget, $textColor="red", $lineColor="red")
Rel(protoc_gen_fsgrpc, protoc_fsgrpc_plugin, "Publishes")
UpdateRelStyle(protoc_gen_fsgrpc, protoc_fsgrpc_plugin, $textColor="red", $lineColor="red")
Rel(protoc_gen_fsgrpc, protoc_fsgrpc_plugin_local, "publishes (for local-only builds)")
UpdateRelStyle(protoc_gen_fsgrpc, protoc_fsgrpc_plugin_local, $textColor="red", $lineColor="red")
Rel(protoc_fsgrpc_plugin, generatedcode, "generates (Buf)")
UpdateRelStyle(protoc_fsgrpc_plugin, generatedcode, $textColor="red", $lineColor="red")
Rel(projectfile, fsgrpc_nuget, "references")
UpdateRelStyle(projectfile, fsgrpc_nuget, $textColor="red", $lineColor="red")
Rel(protoc_fsgrpc_plugin_local, generatedcode, "generates (offline)")
UpdateRelStyle(protoc_fsgrpc_plugin_local, generatedcode, $textColor="red", $lineColor="red")
Rel(protoc_gen_fsgrpc, fsgrpc, "references")
UpdateRelStyle(protoc_gen_fsgrpc, fsgrpc, $textColor="red", $lineColor="red")
Rel(fsgrpc_tests, fsgrpc, "references")
UpdateRelStyle(fsgrpc_tests, fsgrpc, $textColor="red", $lineColor="red")
Rel(protoc_gen_fsgrpc_tests, protoc_gen_fsgrpc, "references")
UpdateRelStyle(protoc_gen_fsgrpc_tests, protoc_gen_fsgrpc, $textColor="red", $lineColor="red")
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- FSharp.Core (>= 6.0.0)
- Google.Protobuf (>= 3.26.1)
- NodaTime (>= 3.1.11)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FsGrpc:
Package | Downloads |
---|---|
FsGrpc.Tools
gRPC and Protocol Buffer compiler for F# projects |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.6 | 2,583 | 4/2/2024 |
1.0.5 | 6,383 | 5/12/2023 |
1.0.3 | 293 | 4/20/2023 |
1.0.2 | 998 | 3/9/2023 |
1.0.1 | 989 | 1/16/2023 |
1.0.0 | 770 | 11/18/2022 |
0.9.9 | 362 | 11/9/2022 |
0.9.8 | 444 | 10/7/2022 |
0.9.7 | 1,806 | 9/13/2022 |
0.9.6 | 427 | 9/1/2022 |
0.9.5 | 433 | 8/23/2022 |
0.9.4 | 421 | 8/22/2022 |
0.9.3 | 447 | 7/12/2022 |
0.9.2 | 470 | 7/8/2022 |
0.9.1-beta | 174 | 7/7/2022 |
0.9.0-alpha-2 | 207 | 3/21/2022 |
0.9.0-alpha-1 | 192 | 3/19/2022 |