Skip to main content

Getting Started

ConjureDB is schema-first. The only supported frontend contract is a .conjure schema file that declares tables, indexes, queries, mutations, commands, and extern functions. Generated MessagePack attributes are compiler output.

Install​

dotnet add package ConjureDB
dotnet add package MessagePack

Add schema files under schema/ beside the target project. The manual compiler uses that directory and Generated/ by default; --schema-root and --output provide explicit overrides.

Define Tables​

Create schema/game.conjure:

table Player(plural: Players, persistence: local, capacity: 1000, type_id: 1) {
Id: int @id
Name: string
Level: int
Score: int

@@index(fields: [Level], name: "PlayersByLevel", kind: sorted_set)
@@index(fields: [Score], name: "PlayersByScore", kind: sorted_set)
}

table Item(plural: Items, persistence: local, capacity: 10000, type_id: 2) {
Id: int @id
OwnerId: int
ItemType: string
Quantity: int

@@index(fields: [OwnerId], name: "ItemsByOwner", kind: lookup)
}

Persisted tables must declare type_id. Runtime-only tables may omit it only when persistence: none; the generator assigns a deterministic runtime-local id that is never used for durable snapshot identity.

Add Queries​

query GetHighLevelPlayers(minLevel: int) -> Player[] {
from Players
| filter Level > @minLevel
| select { Id, Name, Level }
}

query GetTopScorers(minScore: int, count: int) -> Player[] {
from Players
| filter Score > @minScore
| sort -Score
| take @count
}

Queries compile ahead of time into generated C# methods during the explicit manual compiler step. There is no runtime query parser on the hot path, and normal dotnet build only compiles the committed generated pack.

Note: This is the minimal teaching version of the top-N query and Player table. For the indexed, reactive version that stays current as scores change, see Reactive Queries.

Add Mutations​

mutation GrantItem(playerId: int, itemType: string, quantity: int) {
insert Items
| values {
OwnerId = @playerId,
ItemType = @itemType,
Quantity = @quantity
}
}

Note: This GrantItem is the simple insert-mutation form (itemType: string). A different upsert mutation also named GrantItem appears in Mutations, and a GrantItem command appears in Build Your First Game; they are distinct examples.

Use compiled mutations or compiled commands for write-side behavior. Normal game validation failures should be modeled as typed command outcomes.

Create a Context​

The manual compiler emits the requested context name.

using ConjureDB;

using var context = DbContextBuilder<GameDbContext>.Create()
.WithDataDirectory("./data")
.WithDefaultSnapshot()
.Build();

var players = context.Set<Player>();

context.BeginTransaction();
players.Add(new Player { Id = 1, Name = "Hero", Level = 10, Score = 500 });
context.Commit();

Update() takes the row that replaces the stored one — it does not take a mutation of it:

context.BeginTransaction();
var hero = players.FindById(1);
players.Update(hero with { Score = hero.Score + 250 }); // a NEW row carrying the new values
context.Commit();

If a class row is mutated in place and passed back, the update carries the same instance as its old row and its new row, the previous values no longer exist, and every index and materialized view would retract under the new key. The runtime refuses that change with an InvalidOperationException; see Transactions for the rule and the correct patterns. A struct or record struct row is copied on read and is never affected.

For temporary persistent-table tests or tools that should not touch disk, disable persistence at the context boundary:

var context = DbContextBuilder<GameDbContext>.Create()
.WithNoPersistence()
.Build();

WithNoPersistence() does not turn persistent tables into the frame-local ephemeral storage kind. For ECS-style non-transactional frame state, declare ephemeral table, event table, or singleton table and follow Ephemeral Tables and Frame Systems.

Manual Generation​

For deterministic local generation, CI diagnostics, or PGO-driven codegen:

dotnet run --project ConjureDB.Compiler -- generate MyGame.csproj \
--context GameDbContext --namespace MyGame --output Generated
dotnet run --project ConjureDB.Compiler -- verify MyGame.csproj

Generated files are compiler output. If generated C# is wrong, fix the schema parser, binder, planner, or emitter and regenerate.

Troubleshooting​

SymptomCheck
No generated contextRun ConjureDB.Compiler generate against the authoritative project and commit the complete Generated/ pack.
Missing persisted table idAdd type_id to every persistence: local or persistence: remote table.
Slow filtered queryDeclare a matching schema @@index and rerun generation.
Snapshot compatibility errorKeep type_id, field ordinals, and schema versions stable across releases.

See Schema Language, Compiled Queries, Mutations, and Indexing for detailed references.