Skip to content

Your First Stream

Let’s create your first event stream.

Serve

Unlike sqlite, which operates directly on the file system, xs requires a running process to manage access to the local store. This enables features like subscribing to real-time updates from the event stream.

Start an xs store in a dedicated window:

Terminal window
xs serve ./store
13:35:16.868 TRACE event src/main.rs:185 Starting server with path: "./store" xs:185
13:35:16.957 INFO read options=ReadOptions { follow: On, tail: false, last_id: None, limit: None } xs::store:174
13:35:16.957 INFO read options=ReadOptions { follow: On, tail: true, last_id: None, limit: None } xs::store:174
13:35:16.963 INFO 5ms insert_frame frame=Frame { id: "03d4por2p16i05i81fjy0fx8u", topic: "xs.start", hash: None, meta: None, ttl: None } xs::store:410
13:35:16.963 0fx8u xs.start
13:35:16.968 INFO read options=ReadOptions { follow: On, tail: false, last_id: None, limit: None } xs::store:174

For a long-running setup you might run xs serve ~/.local/share/cross.stream/store under a process supervisor. This is the default location used by xs.nu when $env.XS_ADDR isn’t set. Here we keep the demo scoped to ./store.

To point tools at another store, set XS_ADDR. This can be done temporarily with with-env:

Terminal window
with-env {XS_ADDR: "./store"} { .cat }

Client

append command

OK! Let’s append our first event:

Terminal window
null | .append notes --meta {text: "a quick note"}
───────┬─────────────────────────────
topic │ notes
id │ 03d4q1qhbiv09ovtuhokw5yxv
hash │
meta │ {text: "a quick note"}
ttl │ forever
───────┴─────────────────────────────

cat command

and then cat the stream:

Terminal window
.cat
─#─┬──topic───┬────────────id─────────────┬─hash─┬──────────meta──────────┬───ttl───
0 │ xs.start │ 03d4q1o70y6ek0ig8hwy9q00n │ │ │
1 │ notes │ 03d4q1qhbiv09ovtuhokw5yxv │ │ {text: "a quick note"} │ forever
───┴──────────┴───────────────────────────┴──────┴────────────────────────┴─────────

These are the raw frames on the stream. Structured data lives inline in the meta field. We have the full expressiveness of Nushell available to us — for example, we can read the note text directly:

Terminal window
.cat | last | $in.meta.text
a quick note

.last command

Let’s submit another note:

Terminal window
null | .append notes --meta {text: "submit TPS report"}
.cat
─#─┬──topic───┬────────────id─────────────┬─hash─┬──────────────meta──────────────┬───ttl───
0 │ xs.start │ 03d4q1o70y6ek0ig8hwy9q00n │ │ │
1 │ notes │ 03d4q1qhbiv09ovtuhokw5yxv │ │ {text: "a quick note"} │ forever
2 │ notes │ 03d4qbrxizqgav09m7hicksb0 │ │ {text: "submit TPS report"} │ forever
───┴──────────┴───────────────────────────┴──────┴────────────────────────────────┴─────────

We can get the most recent note on the stream using the .last command:

Terminal window
.last notes
───────┬─────────────────────────────────
topic │ notes
id │ 03d4qbrxizqgav09m7hicksb0
hash │
meta │ {text: "submit TPS report"}
ttl │ forever
───────┴─────────────────────────────────
Terminal window
.last notes | $in.meta.text
submit TPS report

riffing

Finally, let’s pull a list of all our notes.

We can filter by topic and then pull out the text of each note:

Terminal window
.cat | where topic == "notes" | each { $in.meta.text }
───┬───────────────────
0 │ a quick note
1 │ submit TPS report
───┴───────────────────

Fun!