Client SDK (Go)¶
The Go client in network/pkg/client/
provides typed access to Database (RQLite), Pub/Sub, and Network information. This page shows setup, authentication, configuration, and end-to-end examples aligned with the current codebase. It also includes an in-depth guide to the ORM-like RQLite client in network/pkg/rqlite
for struct-mapped CRUD and a fluent query builder.
References:
- Code:
network/pkg/client/
- RQLite ORM-like:
network/pkg/rqlite
- Examples:
network/examples/basic_usage.go
Install & Import¶
import (
"context"
"github.com/DeBrosOfficial/network/pkg/client"
)
Authentication and Namespace¶
The client supports two credential types via client.ClientConfig
:
APIKey
: raw API key (formatak_<random>:<namespace>
) — namespace is parsed from the suffix.JWT
: short-lived JWT issued by the Gateway — preferred; namespace read from theNamespace
claim.
Exchange API key for JWT with the Gateway (recommended):
// POST http://localhost:6001/v1/auth/token with Authorization: Bearer <API_KEY>
// See docs/network/authentication.md for details.
Client namespace resolution at Connect()
(see deriveNamespace()
):
- If
JWT
is set, readNamespace
claim from JWT. - Else if
APIKey
is set, parse namespace fromak_...:<namespace>
. - Else fallback to
AppName
.
You can also override namespace per-call for storage and pubsub with client.WithNamespace(ctx, "my-ns")
. The client enforces namespace consistency between credentials and context.
Create and Connect a Client¶
cfg := client.DefaultClientConfig("my-app")
// This is optional bootstrap peers exist by default.
cfg.BootstrapPeers = []string{
"/ip4/127.0.0.1/tcp/4001/p2p/<PEER_ID>",
}
// Option A: Use API key directly (namespace from key)
// cfg.APIKey = "ak_xxx:my-app"
// Option B (recommended): Exchange API key for JWT, then set JWT
// cfg.JWT = "<your_jwt>"
cli, err := client.NewClient(cfg)
if err != nil { panic(err) }
if err := cli.Connect(); err != nil { panic(err) }
defer cli.Disconnect()
Tips:
- Bootstrap peers must be reachable libp2p multiaddrs.
- RQLite endpoints auto-resolve from defaults or config (see
DefaultDatabaseEndpoints()
), with retries.
Database API¶
Interface: client.DatabaseClient
in network/pkg/client/interface.go
Query(ctx, sql, args...) (*QueryResult, error)
Transaction(ctx, []string) error
CreateTable(ctx, schema string) error
DropTable(ctx, tableName string) error
GetSchema(ctx) (*SchemaInfo, error)
Examples:
ctx := context.Background()
db := cli.Database()
// Create or migrate schema
schema := `CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY,
content TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)`
if err := db.CreateTable(ctx, schema); err != nil { panic(err) }
// Parameterized write and read
_, err := db.Query(ctx, "INSERT INTO messages(content) VALUES (?)", "hello")
if err != nil { panic(err) }
res, err := db.Query(ctx, "SELECT id, content, timestamp FROM messages ORDER BY id DESC LIMIT ?", 10)
if err != nil { panic(err) }
_ = res // res.Columns, res.Rows, res.Count
// Atomic migration (multiple statements)
stmts := []string{
"ALTER TABLE messages ADD COLUMN author TEXT",
}
if err := db.Transaction(ctx, stmts); err != nil { panic(err) }
// Inspect schema
info, err := db.GetSchema(ctx)
if err != nil { panic(err) }
_ = info
Behavior:
- Writes and reads are routed using RQLite; client retries and re-establishes connections on failures (
gorqlite
under the hood). Transaction
runs statements sequentially; any failure aborts and triggers retry logic.
RQLite ORM-like (pkg/rqlite)¶
The network/pkg/rqlite
package provides an ORM-like layer over RQLite with struct mapping, a fluent query builder, repositories, and transactions.
Entities and mapping¶
type User struct {
ID int64 `db:"id,pk,auto"`
Email string `db:"email"`
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
CreatedAt time.Time `db:"created_at"`
}
func (User) TableName() string { return "users" }
Rules:
- If no db
tag is provided, the field name is used as the column (case-insensitive).
- A field named ID
is treated as the primary key by default; add auto
for autoincrement.
Save and Remove¶
u := &User{Email: "[email protected]", FirstName: "Alice", LastName: "A"}
_ = client.Save(ctx, u) // INSERT; sets u.ID if autoincrement
u.LastName = "Updated"
_ = client.Save(ctx, u) // UPDATE by primary key
_ = client.Remove(ctx, u) // DELETE by primary key
FindOneBy / FindBy¶
var one User
_ = client.FindOneBy(ctx, &one, "users", map[string]any{"email": "[email protected]"})
var many []User
_ = client.FindBy(ctx, &many, "users", map[string]any{"last_name": "A"},
rqlite.WithOrderBy("created_at DESC"), rqlite.WithLimit(50))
QueryBuilder¶
var results []User
qb := client.CreateQueryBuilder("users u").
InnerJoin("posts p", "p.user_id = u.id").
Where("u.email LIKE ?", "%@example.com").
AndWhere("p.created_at >= ?", "2024-01-01T00:00:00Z").
GroupBy("u.id").
OrderBy("u.created_at DESC").
Limit(20).
Offset(0)
_ = qb.GetMany(ctx, &results)
var one User
_ = qb.Limit(1).GetOne(ctx, &one)
Raw queries¶
var users []User
_ = client.Query(ctx, &users, "SELECT id, email FROM users WHERE email LIKE ?", "%@example.com")
var rows []map[string]any
_ = client.Query(ctx, &rows, "SELECT id, email FROM users WHERE id IN (?,?)", 1, 2)
Transactions¶
err := client.Tx(ctx, func(tx rqlite.Tx) error {
var me User
if err := tx.Query(ctx, &me, "SELECT * FROM users WHERE id = ?", 1); err != nil {
return err
}
me.LastName = "Updated"
if err := tx.Save(ctx, &me); err != nil {
return err
}
var recent []User
if err := tx.CreateQueryBuilder("users").
OrderBy("created_at DESC").
Limit(5).
GetMany(ctx, &recent); err != nil {
return err
}
return nil // commit
})
Repositories (generic)¶
repo := client.Repository[User]("users")
var many []User
_ = repo.Find(ctx, &many, map[string]any{"last_name": "A"}, rqlite.WithOrderBy("created_at DESC"), rqlite.WithLimit(10))
var one User
_ = repo.FindOne(ctx, &one, map[string]any{"email": "[email protected]"})
_ = repo.Save(ctx, &one)
_ = repo.Remove(ctx, &one)
Pub/Sub API¶
Interface: client.PubSubClient
Subscribe(ctx, topic string, handler MessageHandler) error
Publish(ctx, topic string, data []byte) error
Unsubscribe(ctx, topic string) error
ListTopics(ctx) ([]string, error)
Example:
ctx := context.Background()
ps := cli.PubSub()
handler := func(topic string, data []byte) error {
// handle message
return nil
}
if err := ps.Subscribe(ctx, "notifications", handler); err != nil { panic(err) }
if err := ps.Publish(ctx, "notifications", []byte("hi")); err != nil { panic(err) }
topics, _ := ps.ListTopics(ctx)
_ = topics
Notes:
- Uses libp2p GossipSub; client is a lightweight participant and connects only to bootstrap peers.
Network Info API¶
Interface: client.NetworkInfo
GetStatus(ctx) (*NetworkStatus, error)
GetPeers(ctx) ([]PeerInfo, error)
ConnectToPeer(ctx, addr string) error
DisconnectFromPeer(ctx, peerID string) error
ctx := context.Background()
n := cli.Network()
status, _ := n.GetStatus(ctx)
peers, _ := n.GetPeers(ctx)
_ = status; _ = peers
End-to-End: Auth (API Key → JWT) + Client¶
// 1) Exchange API key for JWT via Gateway
// POST http://localhost:6001/v1/auth/token with header: Authorization: Bearer <API_KEY>
// Response: { access_token, expires_in, namespace }
// 2) Configure client with JWT
cfg := client.DefaultClientConfig("my-app")
cfg.JWT = "<access_token>"
cfg.BootstrapPeers = []string{"/ip4/127.0.0.1/tcp/4001/p2p/<PEER_ID>"}
cli, _ := client.NewClient(cfg)
_ = cli.Connect()
defer cli.Disconnect()
Context Helpers¶
client.WithNamespace(ctx, ns)
— sets namespace override in the context (applies to supported operations).client.WithInternalAuth(ctx)
— bypasses auth checks for internal system operations (for gateway/node internals; not for regular app code).
Troubleshooting¶
- Ensure bootstrap peer multiaddrs are correct and reachable.
- RQLite leader election may momentarily cause write failures; the client retries automatically.
- If you pass a namespace in context that doesn’t match credentials,
requireAccess
will reject the call.
See also¶
- Full example:
network/examples/basic_usage.go
- Gateway: see
docs/network/gateway.md
for endpoints and examples - Migrations: see
docs/network/migrations.md