A generic typed handlers framework for NATS messaging in Go.
- Type-safe messaging with Go 1.24+ generics
- Multi-format serialization (JSON, YAML, MessagePack, Protocol Buffers)
- Multiple messaging patterns: Publisher/Subscriber, Producer/Consumer, Request/Reply
- JetStream support for durable messaging and key-value storage
- Comprehensive integrations via contrib packages
- Production-ready with extensive test coverage and observability
Peanats provides a 3-layer architecture for NATS messaging:
Direct use of NATS and JetStream client libraries:
conn, _ := nats.Connect(nats.DefaultURL)
conn.Publish("subject", []byte("data"))Typed interfaces, middleware system, and transport adapter around NATS and JetStream:
tc, _ := transport.Wrap(nats.Connect(nats.DefaultURL))
tc.Publish(ctx, peanats.NewMsg(...)) // Takes peanats.Msg interfaceHigh-level typed APIs with automatic serialization and content-type detection:
pub := publisher.New(tc)
pub.Publish(ctx, "subject", MyStruct{}, publisher.WithContentType(codec.JSON))When to use each layer:
- Layer 3 (packages): Most applications - type-safe, automatic serialization
- Layer 2 (core): Performance-critical or when you need direct control over message handling
- Layer 1 (native): Direct NATS features not yet wrapped by peanats
publisher/- Type-safe message publishing with automatic serializationsubscriber/- Channel-based message consumption with configurable bufferingconsumer/- JetStream pull consumer implementation for durable processingrequester/- Request/reply pattern with support for streaming responsesbucket/- Typed key-value store wrapper around JetStream KeyValue
trace/- OpenTelemetry tracing and metrics integrationprom/- Prometheus metrics middleware for message processinglogging/- Structured logging with Go's slog packageacknak/- Message acknowledgment helpers for JetStreampond/- Worker pool integration using Alitto Pondmuxer/- Message routing and multiplexing utilities
package main
import (
"context"
"github.com/nats-io/nats.go"
"github.com/mikluko/peanats/publisher"
"github.com/mikluko/peanats/transport"
)
type MyMessage struct {
ID string `json:"id"`
Data string `json:"data"`
}
func main() {
// Connect to NATS
conn, _ := nats.Connect(nats.DefaultURL)
tc, _ := transport.Wrap(conn)
// Publisher
pub := publisher.New(tc)
pub.Publish(context.Background(), "events", MyMessage{
ID: "123", Data: "hello world",
})
}tc, _ := transport.Wrap(nats.Connect(nats.DefaultURL))
req := requester.New[MyRequest, MyResponse](tc)
resp, _ := req.Request(ctx, "service.endpoint", MyRequest{Query: "data"})
for r := range resp {
if r.Err != nil {
log.Printf("Error: %v", r.Err)
continue
}
println("Response:", r.Payload.Result)
}// JetStream KV bucket
kv := bucket.New[MyData](js, "my-bucket")
// Put value
entry, _ := kv.Put(ctx, "key1", MyData{Value: "test"})
// Get value
entry, _ = kv.Get(ctx, "key1")
// entry.Value() is typed as MyDataMiddleware wraps message handlers to add cross-cutting concerns. Use ChainMsgMiddleware
to compose them:
h := peanats.ChainMsgMiddleware(
peanats.MsgHandlerFromArgHandler[MyMessage](&myHandler{}),
logging.AccessLogMiddleware(logging.SlogLogger(slog.Default(), slog.LevelInfo)),
prom.Middleware(prom.MiddlewareNamespace("myapp")),
)
ch, _ := subscriber.SubscribeChan(ctx, h)
sub, _ := tc.SubscribeChan(ctx, "events.>", ch)// Add tracing to publisher
pub := trace.Publisher(publisher.New(tc))
// Add tracing to requester
req := trace.Requester(requester.New[MyReq, MyResp](tc))- Go 1.24+ (uses generics extensively)
- NATS Server 2.9+ (for JetStream features)
go get github.com/mikluko/peanats@latestSee examples/ for complete working examples:
Peanats automatically selects serialization format based on message content-type headers:
application/json- JSON (default)application/yaml- YAMLapplication/msgpack- MessagePackapplication/protobuf- Protocol Buffers
This project follows EffVer (Effort Versioning) rather than strict SemVer, using the MACRO.MESO.MICRO scheme:
- MACRO bumps signal fundamental rethinks — currently frozen at 0
- MESO bumps introduce new features and may include breaking changes
- MICRO bumps are bug fixes and non-breaking improvements
Peanats is on the 0.x track indefinitely. There are no plans for a 1.0 release — the API is stable enough for production use but reserves the right to evolve. When breaking changes occur at MESO boundaries, migration guidance is provided in UPGRADING.md.
This project is developed with substantial AI assistance (Claude Code). Every design decision, interface contract, and architectural trade-off is human-directed and human-reviewed. AI accelerates implementation; it does not drive it.
Contributions that use AI tooling are welcome — under the same standard. If you can't explain why the code exists, it doesn't belong here.
MIT License - see LICENSE file for details.