Frodo
A code generator that turns plain old Go services into RPC-enabled (micro)services with HTTP APIs, event-driven workflows, and multi-language support.
Install / Use
/learn @bridgekit-io/FrodoREADME
Frodo
That's just, like, your opinion, man!
Frod is an opinionated code generator and runtime library that helps you write (micro) services/APIs that supports, both, RPC/HTTP and Event-Driven invocation. It parses the interfaces/structs/comments in your code service code to generate all of the client, server, gateway, and pub-sub communication code automatically.
You write business logic. Frodo generates the annoying copy/paste boilerplate needed to expose your service as an HTTP API as well as Pub/Sub code to create event-driven workflows across your services.
This is the spiritual successor to my original project of the same name in a different org: Frodo. It supports the RPC/HTTP related features of the original, but it addresses many shortcomings in the architecture/approach and adds Event-Driven communication with almost no extra code on your part.
Getting Started
go install github.com/bridgekit-io/frodo@latest
go get -u github.com/bridgekit-io/frodo
# You may this if you get messages about invalid dependencies...
go mod tidy
This will fetch the frodo code generation executable then add the
runtime libraries that allow your services and clients to
communicate with each other as a dependency to your project.
Basic Example
We're going to write a simple CalculatorService that
lets you either add or subtract two numbers.
Step 1: Describe Your Service
Your first step is to write a .go file that just defines the contract for your service; the interface as well as the inputs/outputs.
// calc/calculator_service.go
package calc
import (
"context"
)
type CalculatorService interface {
Add(context.Context, *AddRequest) (*AddResponse, error)
Sub(context.Context, *SubRequest) (*SubResponse, error)
}
type AddRequest struct {
A int
B int
}
type AddResponse struct {
Result int
}
type SubRequest struct {
A int
B int
}
type SubResponse struct {
Result int
}
One important detail is that the interface name ends with the suffix "Service". This tells Frodo that this is an actual service interface and not just some random abstraction in your code.
At this point you haven't actually defined how this service gets this work done; just which operations are available.
Step 2: Implement Your Service Logic
We actually have enough for Frodo to generate your RPC/API/Event code already, but we'll hold off for a moment. Frodo frees you up to focus on building features, so let's actually implement service - no networking, no marshaling, no status stuff, no pub/sub - just business logic to make your service behave properly.
// calc/calculator_service_handler.go
package calc
import (
"context"
)
type CalculatorServiceHandler struct {}
func (svc CalculatorServiceHandler) Add(ctx context.Context, req *AddRequest) (*AddResponse, error) {
result := req.A + req.B
return &AddResponse{Result: result}, nil
}
func (svc CalculatorServiceHandler) Sub(ctx context.Context, req *SubRequest) (*SubResponse, error) {
result := req.A - req.B
return &SubResponse{Result: result}, nil
}
Step 3: Generate Your RPC Client and Server Code
At this point, you've just written the same code that you (hopefully) would have written even if you weren't using Frodo. Next, we want to auto-generate two things:
- The "server" bits that allow an instance of your CalculatorService to listen for incoming requests from an either HTTP API or a published event. (We'll look at events later...)
- A "client" struct that communicates with that API to get work done.
Just run these two commands in a terminal:
# Feed it the service interface file, not the handler.
frodo server calculator_service.go
frodo client calculator_service.go
Step 4: Run Your Calculator API
Let's fire up an HTTP server on port 9000 that makes your service available for consumption.
package main
import (
"github.com/bridgekit-io/frodo/services"
"github.com/bridgekit-io/frodo/services/gateways/apis"
"github.com/your/project/calc"
calcgen "github.com/your/project/calc/gen"
)
func main() {
// Create your logic-only handler, then wrap it in service
// communication bits that let it interact with the Frodo runtime.
calcHandler := calc.CalculatorServiceHandler{}
calcService := calcgen.CalculatorServiceServer(calcHandler)
// Fire up a server that will manage our service and listen to
// API calls on port 9000.
server := services.NewServer(
services.Listen(apis.NewGateway(":9000")),
services.Register(calcService),
)
server.Run(context.Background())
}
Seriously. That's the whole program.
Compile and run it, and your service/API is now ready to be consumed. We'll use the Go client we generated in just a moment, but you can try this out right now by simply using curl:
curl -d '{"A":5, "B":2}' http://localhost:9000/CalculatorService.Add
# {"Result":7}
curl -d '{"A":5, "B":2}' http://localhost:9000/CalculatorService.Sub
# {"Result":3}
Step 5: Interact With Your Calculator Service
While you can use raw HTTP to communicate with the service, let's use our auto-generated client to hide the gory details of JSON marshaling, status code translation, and other noise.
The client actually implements CalculatorService just like the server/handler does. As a result the RPC-style call will "feel" like you're executing the service work locally, when in reality the client is actually making API calls to the server running on port 9000.
package main
import (
"context"
"fmt"
"github.com/your/project/calc"
calcgen "github.com/your/project/calc/gen"
)
func main() {
ctx := context.Background()
client := calcgen.CalculatorServiceClient("http://localhost:9000")
add, err := client.Add(ctx, &calc.AddRequest{A:5, B:2})
if err != nil {
// handle error
}
fmt.Println("5 + 2 =", add.Result)
sub, err := client.Sub(ctx, &calc.SubRequest{A:5, B:2})
if err != nil {
// handle error
}
fmt.Println("5 - 2 =", sub.Result)
}
Compile/run this program, and you should see the following output:
5 + 2 = 7
5 - 2 = 3
That's it!
Creating a JavaScript Client
The frodo tool can actually generate a JS client that you
can add to your frontend code (or React Native mobile code)
to hide the complexity of making API calls to your backend
service. Without any plugins or fuss, we can create a JS client of the same
CalculatorService from earlier...
frodo client calc/calculator_service.go --language=js
or
frodo client calc/calculator_service.go --language=node
This will create the file calculator_service.gen.client.js
which you can include with your frontend codebase. Using it
should look similar to the Go client we saw earlier:
import {CalculatorService} from 'lib/calculator_service.gen.client';
// The service client is a class that exposes all of the
// operations as 'async' functions that resolve with the
// result of the service call.
//
// All of the operations on the Go service are exposed here
// as well. The arguments to these functions are the same as
// the request struct in the Go code, and the return value
// will match the response struct from your Go service.
const service = new CalculatorService('http://localhost:9000');
const add = await service.Add({A:5, B:2});
const sub = await service.Sub({A:5, B:2});
// Should print:
// Add(5, 2) = 7
// Sub(5, 2) = 3
console.info('Add(5, 2) = ' + add.Result)
console.info('Sub(5, 2) = ' + sub.Result)
Another subtle benefit of using the generated client is that your service/method documentation follows you in the generated code. It's included in the file as JSDoc comments so your documentation should be available to your IDE even when writing your frontend code.
Node Support
Frodo uses the fetch function to make the actual HTTP requests,
so if you are using Node 18+, you shouldn't need to do anything
special as fetch is now in the global scope. If that's the
case, ignore the next paragraph and subsequent sample code.
If you're using an older version of node or just really prefer
to use the classic node-fetch package, you can supply the
fetch implementation to use when constructing your client:
const fetch = require('node-fetch');
const service = new CalculatorService('http://localhost:9000', {fetch});
const add = await service.Add({A:5, B:2});
const sub = await service.Sub({A:5, B:2});
Creating a Dart/Flutter Client
Just like the JS client, Frodo can create a Dart client that you can embed in your Flutter apps so mobile frontends can consume your service.
frodo client calc/calculator_service.go --language=dart
or
frodo client calc/calculator_service.go --language=flutter
This will create the file calculator_service.gen.client.dart. Add it
to your Flutter codebase, and it behaves very similarly to the JS client.
The
HttpClientfrom the standarddart:iopackage is NOT supported in Flutter web applications. To support Flutter mobile as well as web, Frodo clients uses the http package to make requests to the backend API. You'll need to add that to your pubspec for the following code to work:
import 'lib/calculator_service.gen.client.dart';
var service = CalculatorServiceClient("http://localhost:9000");
var add = await service.Add(AddRequest(A:5, B:2));
var sub = await service.Sub(SubRequest(A:5, B:2));
// Should print:
// Add(5, 2) = 7
// Sub(5, 2) = 3
print('Add(5, 2) = ${add.Result}');
print('Sub(5, 2) = ${sub.Result}');
For more examples of how to write services that let Frodo take care of the RPC/API boilerplate, take a look
Related Skills
openhue
347.6kControl Philips Hue lights and scenes via the OpenHue CLI.
sag
347.6kElevenLabs text-to-speech with mac-style say UX.
weather
347.6kGet current weather and forecasts via wttr.in or Open-Meteo
tradingview-mcp
609Advanced TradingView MCP Server for AI-powered market analysis. Real-time crypto & stock screening, technical indicators, Bollinger Band intelligence, and candlestick patterns. Works with Claude Desktop & AI assistants. Multi-exchange support (Binance, KuCoin, Bybit+). Open source trading toolkit.
