XDB is a CLI & a library that provides a tuple-based abstraction for modeling, storing, and querying data across multiple databases. Rather than writing database-specific schemas, queries, and migrations, XDB allows developers to model their domain once and use it with one or more databases.
Read about the motivation behind XDB in Introducing XDB.
The XDB data model can be visualized as a tree of Namespaces, Schemas, Records, and Tuples.
┌─────────────────────────────────┐
│ Namespace │
└────────────────┬────────────────┘
↓
┌─────────────────────────────────┐
│ Schema │
└────────────────┬────────────────┘
↓
┌─────────────────────────────────┐
│ Record │
└────────────────┬────────────────┘
↓
┌─────────────────────────────────┐
│ Tuple │
├─────────────────────────────────┤
│ ID | Attr | Value | Options │
└─────────────────────────────────┘
A Tuple is the fundamental building block in XDB. It combines:
- ID: a string that uniquely identifies the record
- Attr: a string that identifies the attribute. It supports dot-separated nesting.
- Value: The attribute's value
- Options: Key-value pairs for metadata
One or more Tuples, with the same ID, make up a Record. Records are similar to objects, structs, or rows in a database. Records typically represent a single entity or object of domain data.
A Namespace (NS) groups one or more Schemas. Namespaces are typically used to organize schemas by domain, application, or tenant.
A Schema defines the structure of records and groups them together. Schemas can be "strict" or "flexible". Strict schemas enforce a predefined structure on the data, while flexible schemas allow for arbitrary data. Each schema is uniquely identified by its name within a namespace.
XDB URIs are valid Uniform Resource Identifiers (URI) according to RFC 3986. URIs are used to uniquely identify resources in XDB.
The general format of a URI is:
[SCHEME]://[DOMAIN] [ "/" PATH] [ "?" QUERY] [ "#" FRAGMENT]
XDB URIs follow the following format:
xdb:// NS [ "/" SCHEMA ] [ "/" ID ] [ "#" ATTRIBUTE ]
xdb://com.example/posts/123-456-789#author.id
└─┬──┘└────┬────┘└──┬─┘└─────┬─────┘└─────┬─────┘
scheme NS SCHEMA ID ATTRIBUTE
└───────────┬───────────┘
path
The components of the URI are:
- NS: The namespace.
- SCHEMA: The schema name.
- ID: The unique identifier of the record.
- ATTRIBUTE: The name of the attribute.
- path: NS, SCHEMA, and ID combined uniquely identify a record (URI without xdb://)
Valid examples:
Namespace: xdb://com.example
Schema: xdb://com.example/posts
Record: xdb://com.example/posts/123-456-789
Attribute: xdb://com.example/posts/123-456-789#author.id
| Type | PostgreSQL | SQLite | Description |
|---|---|---|---|
| String | TEXT | TEXT | UTF-8 string |
| Integer | BIGINT | INTEGER | 64-bit signed integer |
| Float | DOUBLE PRECISION | REAL | 64-bit floating point number |
| Boolean | BOOLEAN | INTEGER | True or False |
| Timestamp | TIMESTAMPZ | INTEGER | Date and time in UTC |
| JSON | JSONB | TEXT | JSON data type |
| Bytes | BYTEA | BLOB | Binary data |
| Array | []T | TEXT | Array of T |
go install github.com/xdb-dev/xdb/cmd/xdb@latestxdb --version# Create schema
xdb make-schema xdb://com.example/posts --schema posts.json
# List schemas
xdb ls xdb://com.example
# Get schema
xdb get xdb://com.example/posts
# Remove schema
xdb rm xdb://com.example/posts
# Put record
xdb put xdb://com.example/posts/post-123 --file post.json
# Get record
xdb get xdb://com.example/posts/post-123
# List records
xdb ls xdb://com.example/posts
# Filter records
xdb ls xdb://com.example/posts --filter "title~Hello"
# Remove record
xdb rm xdb://com.example/posts/post-123# Create schema from definition file
xdb make-schema xdb://com.example/posts --schema ./posts.json
# Create flexible schema (no definition file)
xdb make-schema xdb://com.example/usersmake-schema creates or updates a schema at the given URI. The URI must include both NS and Schema components (e.g., xdb://com.example/posts).
If no schema definition file is provided, a flexible schema is created. Flexible schemas allow arbitrary data without validation. Schema definitions enforce structure and types on all Tuple and Record operations.
Note: You can update a schema by calling make-schema again with the same URI and a new schema definition.
# List schemas in namespace
xdb ls xdb://com.example
# List with pagination
xdb ls xdb://com.example/users --limit 10
xdb ls xdb://com.example/users --limit 10 --offset 20
# List all (default limit is 100)
xdb ls xdb://com.example
# Filter records by attribute
xdb ls xdb://com.example/users --filter "age>30"
xdb ls xdb://com.example/users --filter "name=alice"
# Multiple filters (AND logic)
xdb ls xdb://com.example/users --filter "age>=25" --filter "status!=inactive"
# Contains filter
xdb ls xdb://com.example/posts --filter "title~hello"
# Combine filters with pagination
xdb ls xdb://com.example/users --filter "age>30" --limit 10ls lists namespaces, schemas, or records depending on the URI. If no URI is provided, it lists all namespaces. Filters are only applied when listing records.
Flags:
--limit N: Maximum number of results to return (default: 100)--offset N: Number of results to skip (default: 0)--filter EXPR: Filter records by attribute (repeatable). Supported operators:=,!=,>,>=,<,<=,~(contains). Multiple filters are combined with AND logic. Filtering is applied before pagination.
# Get Namespace
xdb get xdb://com.example
# Get Schema
xdb get xdb://com.example/posts
# Get Record
xdb get xdb://com.example/posts/post-123
# Get Attribute
xdb get xdb://com.example/posts/post-123#titleget retrieves a Namespace, Schema, Record, or Attribute from the given URI.
# Put Record from JSON file
xdb put xdb://com.example/posts/post-123 --file post.json
# Put Record from stdin (JSON)
echo '{"title":"Hello","content":"World"}' | xdb put xdb://com.example/posts/post-123
# Put Record from YAML file
xdb put xdb://com.example/posts/post-123 --file post.yaml --format yaml
# Put with explicit format
echo 'title: Hello
content: World' | xdb put xdb://com.example/posts/post-123 --format yamlput creates or updates a Record at the given URI from a JSON or YAML file, or from stdin.
Flags:
--file,-f: Path to file (reads from stdin if omitted)--format: Input format: json (default) or yaml
# Remove record (with confirmation prompt)
xdb remove xdb://com.example/posts/post-123
# Remove without confirmation
xdb rm xdb://com.example/posts/post-123 --force
# Remove schema
xdb rm xdb://com.example/posts --forceremove (aliases: rm, delete) deletes a Record, Attribute, or Schema at the given URI.
Flags:
--force,-f: Skip confirmation prompt
# Start the daemon
xdb daemon start
# Check daemon status
xdb daemon status
# Stop the daemon
xdb daemon stop
# Force stop the daemon
xdb daemon stop --force
# Restart the daemon
xdb daemon restartThe daemon runs an HTTP server that handles all XDB operations. By default it listens on localhost:8147 and a Unix socket at ~/.xdb/xdb.sock.
These flags are available for all commands:
--output,-o: Output format (json, table, yaml). Auto-detected by default (table for TTY, JSON for pipes)--config,-c: Path to config file (defaults to~/.xdb/config.json)--verbose,-v: Enable verbose logging (INFO level)--debug: Enable debug logging with source locations
By default, XDB automatically selects the appropriate format:
- Table format: When output is a terminal (TTY) - human-readable tables
- JSON format: When output is piped or redirected - machine-parseable
json: Indented JSON output (machine-readable)table: Human-readable tables with bordersyaml: YAML output
# Uses table format (interactive terminal)
xdb get xdb://com.example/users/123
# Uses JSON format (piped to jq)
xdb get xdb://com.example/users/123 | jq '.name'
# Force JSON output even in terminal
xdb get xdb://com.example/users/123 --output json
# Force table output even when piping
xdb ls xdb://com.example/users --output tableXDB commands work well with Unix pipes:
# Get record and pipe to jq
xdb get xdb://com.example/posts/post-123 | jq '.title'
# List schemas and count them
xdb ls xdb://com.example | jq '. | length'
# Export records to file
xdb get xdb://com.example/posts/post-123 --output json > backup.json
# Import from file
cat backup.json | xdb put xdb://com.example/posts/post-123XDB uses a JSON config file at ~/.xdb/config.json. A default config is created automatically on first run.
{
"dir": "~/.xdb",
"daemon": {
"addr": "localhost:8147",
"socket": "xdb.sock"
},
"log_level": "info",
"store": {
"backend": "memory"
}
}- memory (default): In-memory store, data is lost on restart
- sqlite: SQLite database, stored in
<dir>/data/ - redis: Redis server, requires
addrto be configured - fs: Filesystem store, stored in
<dir>/data/by default
Example with SQLite:
{
"store": {
"backend": "sqlite",
"sqlite": {
"dir": "",
"name": "xdb.db"
}
}
}Example with Redis:
{
"store": {
"backend": "redis",
"redis": {
"addr": "localhost:6379"
}
}
}See xdb.example.yaml for a full reference of all configuration options.
