natsql
v1.0 — MIT Licensed

Query your NATS JetStream
state with SQL

Zero infrastructure beyond NATS.

Define materialized views over JetStream streams, then query the resulting state with SELECT ... WHERE ... — no Postgres, no Redis, no Kafka. Just NATS.

How it works

Stream → Materialize → Query

natsql is a thin SQL query layer over NATS KV, fed by a configurable materializer that consumes your streams.

1. Define views

Specify source stream, key fields, and columns in YAML/JSON config

2. Publish events

The materializer consumes JetStream messages in order, automatically

3. KV storage

Materialized state is stored in a NATS KV bucket — PK lookups are O(1)

4. Query with SQL

Query via NATS, HTTP, or in-process Go — returns typed JSON

“Give a developer a stream, and they’ll build event-driven systems. Give them a materialized view engine, and they’ll query their state.”

Event → JetStream → Materializer → KV Bucket → SQL → JSON
Quick start

Try it in 60 seconds

No Docker, no databases, no config. Just Go.

main.go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/nats-io/nats.go/jetstream"
	"github.com/gacopys/natsql"
)

func main() {
	ctx := context.Background()
	cfg := &natsql.Config{
		Views: []natsql.ViewConfig{{
			Name: "users", SourceStream: "events",
			KeyFields: []string{"user_id"},
			Columns: []natsql.ColumnConfig{
				{Name: "user_id", From: "id", Type: natsql.ColumnTypeString, PrimaryKey: true},
				{Name: "name", From: "name", Type: natsql.ColumnTypeString},
			},
		}},
	}
	eng, _ := natsql.NewEmbedded(cfg)
	defer eng.Close()

	js, _ := jetstream.New(eng.NC())
	js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{
		Name: "events", Subjects: []string{"events.>"},
	})
	eng.Start(ctx)
	js.Publish(ctx, "events.user-created", []byte(`{"id": "abc", "name": "Alice"}`))
	time.Sleep(time.Second)

	fmt.Println(eng.Query(ctx, "SELECT name FROM users WHERE user_id = 'abc'").Results)
}
$ go mod init demo && go mod tidy && go run .

Browse all examples on GitHub →

Why natsql

Everything you need,
nothing you don’t

Queryable State

SELECT from your event streams without maintaining snapshots or bolting on a second database. PK lookups are O(1) KV gets.

Zero Infrastructure

No Postgres, no Redis, no Kafka. natsql runs entirely on NATS JetStream. Embedded NATS means zero dependencies for development.

SQL-Powered

Standard read-only SQL via the vitess sqlparser. Project columns, filter by PK, combine conditions with AND.

Event-Driven

Materialize any JetStream stream into a queryable KV snapshot. Malformed events go to a DLQ — your pipeline keeps running.

Perfect for

  • NATS developers who need simple queryable state without leaving the NATS ecosystem
  • Event-driven system builders who want to materialize a stream into a KV snapshot and query it
  • Team leads looking to reduce infrastructure surface area — one less Postgres or Redis cluster
  • Anyone prototyping — embedded NATS means you can be up in 60 seconds with zero dependencies