traverse¶
A declarative OData v2/v4 client for Go
traverse is a declarative OData v2/v4 client for Go, built on top of relay for HTTP transport. It handles all OData protocol details - pagination, CSRF tokens, ETag concurrency, delta sync, batch requests, async long-running operations - so you can focus on the data.
The name comes from a deliberate design choice: to traverse means to walk through something large one step at a time, without loading it all into memory. That is the library's founding principle - OData collections are paths to walk, not payloads to download. Read the full philosophy →
Why traverse?¶
-
Fluent Query Builder
Compose OData queries with a type-safe builder:
$filter,$select,$expand,$orderby,$top,$skip,$count,$search,$apply. -
Full CRUD + ETags
Create, Read, Update (PATCH/PUT), Delete with full ETag-aware variants for optimistic concurrency control.
-
:material-track-changes: Entity Change Tracking
Track field-level changes, emit PATCH bodies with only the dirty fields, and roll back with Discard.
-
Typed Pagination
Generic
Paginator[T]follows@odata.nextLinkand$skiptokenautomatically. -
Streaming
Channel-based streaming for large datasets. Process millions of records at constant memory.
-
Batch Requests
Compose multi-operation
$batchrequests with atomic changesets. -
Delta Sync
Incremental updates via OData delta links - sync only what changed since last run.
-
Code Generation
Generate typed Go structs and query builders from any
$metadataEDMX document.
Comparison¶
| Feature | traverse | go-odata | raw http.Client |
|---|---|---|---|
| OData v2 | manual | ||
| OData v4 | partial | manual | |
| Fluent query builder | limited | manual | |
| ETag concurrency | manual | ||
| Entity change tracking | |||
| Generic paginator | manual | ||
| Streaming (channels) | manual | ||
| Batch + changesets | manual | ||
| Delta sync | manual | ||
| Async op polling | manual | ||
| Lambda filters | manual | ||
| Deep insert | manual | ||
| CSRF token mgmt | manual | ||
| Code generation | |||
| SAP compatibility | partial | manual |
30-Second Example¶
package main
import (
"context"
"fmt"
"log"
"github.com/jhonsferg/traverse"
)
type Product struct {
ID int `json:"ProductID"`
Name string `json:"ProductName"`
Price float64 `json:"UnitPrice"`
Category string `json:"CategoryName"`
}
func main() {
client, err := traverse.New(
traverse.WithBaseURL("https://services.odata.org/V4/Northwind/Northwind.svc"),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
var products []Product
err = client.From("Products").
Filter("UnitPrice gt 20").
Select("ProductID", "ProductName", "UnitPrice").
OrderBy("UnitPrice desc").
Top(10).
Into(ctx, &products)
if err != nil {
log.Fatal(err)
}
for _, p := range products {
fmt.Printf("%s: $%.2f\n", p.Name, p.Price)
}
}
Documentation¶
- Installation - Get started in under a minute
- Quick Start - Full working examples from connect to delete
- OData Primer - OData concepts every Go developer needs to know
- Guides - Deep dives into every feature
- Code Generation - Generate typed clients from
$metadata - Extensions - SAP, OAuth2, Prometheus, OpenTelemetry, Cache, GraphQL, Azure Event Grid, Offline Store, Dataverse, OpenAPI Export, Audit Trail
- CSDL JSON - Parse OData metadata from JSON endpoints
- Vocabularies - Core and Validation vocabulary annotations
- traverse-tui - Interactive terminal OData query builder
- Reference - Full API reference