Skip to main content

Clients

Orisun exposes gRPC services. You can use the official typed clients, grpcurl, or generate bindings for another language from the protobuf files.

Official Clients

LanguagePackageRepository
Gogithub.com/oexza/orisun-client-goorisun-client-go
Node.js@orisun/eventstore-clientorisun-node-client
Javacom.orisunlabs:orisun-java-clientorisun-client-java

Install

go get github.com/oexza/orisun-client-go

Use the typed clients when you want request/response objects and subscription helpers. Use grpcurl for operational checks, debugging, and quick experiments.

Boundary prerequisite

Client event operations target an active boundary; a boundary name is not created implicitly by the first write. Before starting an application, define the storage with Admin/CreateBoundary—setting existed_before_catalog when adopting existing physical storage—then wait for Admin/GetBoundary to report BOUNDARY_LIFECYCLE_STATUS_ACTIVE. See the Admin boundary API for placements, lifecycle states, and errors.

The Node client exposes typed helpers:

import { AdminClient, BoundaryStatus } from '@orisun/eventstore-client';

const admin = new AdminClient({
host: 'localhost',
port: 5005,
username: 'admin',
password: 'changeit',
});

const {boundary: definition} = await admin.createBoundary({
name: 'accounts',
description: 'Account lifecycle events',
placement: {backend: 'postgres', namespace: 'public'},
});

let boundary = definition;
while (boundary.status === BoundaryStatus.PROVISIONING) {
await new Promise(resolve => setTimeout(resolve, 100));
({boundary} = await admin.getBoundary('accounts'));
}
if (boundary.status !== BoundaryStatus.ACTIVE) {
throw new Error(`Boundary provisioning failed: ${boundary.lastError}`);
}

const {boundaries} = await admin.listBoundaries();

Set existedBeforeCatalog: true on createBoundary(...) when the physical storage already exists. The command returns a definition in PROVISIONING; do not treat the response as readiness. In clustered deployments, retry an EventStore request that briefly returns FAILED_PRECONDITION after the shared catalog becomes ACTIVE; the selected node is still completing its local runtime installation.

First program

A complete command loop: connect, save an event with a Command Context Consistency check, read the latest carried state from one snapshot, handle a conflict, and subscribe a projector. The examples assume the accounts boundary has already reached ACTIVE. Pick your language once; the same groupId carries over to the EventStore API and Tutorial pages.

package main

import (
"context"
"errors"
"log"

orisun "github.com/oexza/orisun-client-go"
eventstore "github.com/oexza/orisun-client-go/eventstore"
)

func main() {
client, err := orisun.New(
"localhost:5005",
orisun.WithCredentials("admin", "changeit"),
orisun.WithInsecure(),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()

ctx := context.Background()
accountOpenedID := "018f2d5e-0001-7000-8000-000000000001"
accountRootQuery := &eventstore.Query{
Criteria: []*eventstore.Criterion{{
Tags: []*eventstore.Tag{
{Key: "eventType", Value: "AccountOpened"},
{Key: "accountOpenedId", Value: accountOpenedID},
},
}},
}
accountContextQuery := &eventstore.Query{
Criteria: []*eventstore.Criterion{
accountRootQuery.Criteria[0],
{Tags: []*eventstore.Tag{{Key: "scopes.accountOpenedId", Value: accountOpenedID}}},
},
}

// 1. Open the account (context must be empty).
_, err = client.SaveEvents(ctx, &eventstore.SaveEventsRequest{
Boundary: "accounts",
Query: &eventstore.SaveQuery{
ExpectedPosition: &eventstore.Position{CommitPosition: -1, PreparePosition: -1},
SubsetQuery: accountRootQuery,
},
Events: []*eventstore.EventToSave{{
EventId: accountOpenedID, EventType: "AccountOpened",
Data: `{"accountOpenedId":"018f2d5e-0001-7000-8000-000000000001","balance":0}`,
}},
})
if err != nil {
var conflict *orisun.OptimisticConcurrencyException
if errors.As(err, &conflict) {
log.Printf("conflict: expected=%v actual=%v", conflict.ExpectedVersion(), conflict.ActualVersion())
}
log.Fatal(err)
}

// 2. Read the latest carried state and its context position.
latest, err := client.GetLatestByCriteria(ctx, &eventstore.GetLatestByCriteriaRequest{
Boundary: "accounts",
Criteria: accountContextQuery.Criteria,
})
if err != nil {
log.Fatal(err)
}
expected := latest.ContextPosition // pass to the next save as ExpectedPosition

// 3. Subscribe a projector (catch-up then live).
handler := orisun.NewSimpleEventHandler().
WithOnEvent(func(e *eventstore.Event) error { return nil }).
WithOnError(func(e error) { log.Printf("subscription: %v", e) })
sub, err := client.SubscribeToEvents(ctx, &eventstore.CatchUpSubscribeToEventStoreRequest{
Boundary: "accounts",
SubscriberName: "balance-projector",
AfterPosition: expected,
}, handler)
if err != nil {
log.Fatal(err)
}
defer sub.Close()
}

Authenticating from a client

Every call needs credentials. The typed clients take a username and password at construction and reuse the session token the server returns on later calls, so you only supply Basic credentials once.

With grpcurl, send HTTP Basic in the authorization metadata header:

grpcurl -H 'Authorization: Basic YWRtaW46Y2hhbmdlaXQ=' localhost:5005 orisun.EventStore/Ping

Each authenticated response sets an x-auth-token header. A long-lived client can capture that token once and send it as x-auth-token on subsequent calls instead of re-sending Basic credentials; Orisun validates the token first and falls back to Basic. Read the full model in Security & Authorization.

Proto Files

The service definitions live in the main repository:

Generated Go bindings are kept in orisun/.

Generate stubs for another language

If no official client exists for your language, generate stubs directly from the protobuf files with protoc. For example, for Python:

python -m grpc_tools.protoc \
-I proto \
--python_out=. --grpc_python_out=. \
proto/eventstore.proto proto/admin.proto

Swap the *_out plugins for your target language. With gRPC reflection enabled (the default), you can also explore the API live:

grpcurl -H "$AUTH" localhost:5005 list
grpcurl -H "$AUTH" localhost:5005 describe orisun.EventStore

Compatibility

Client libraries are generated from the public protobuf definitions. When upgrading Orisun, regenerate or update clients if the protobuf files changed.