Skip to main content
Developers who know SQL or a document store tend to raise the same objections before they put agent context in an RDF graph. This guide answers three of them with examples you can run against a world.

Prerequisites

  • A Wazoo private beta account. Sign up at wazoo.dev/beta.
  • A world token (wzw_) with the data:read and data:write scopes, generated on the Console tokens page, exported as WORLDS_TOKEN.
  • A world ID, exported as WORLD_ID.
  • curl and jq for the API calls, and Node.js 22.18 or later, Deno, or Bun to run the SHACL validator.

Load the example data

The examples use two people. Ethan has an email address on record and Sandra does not. Save the following as people.ttl:
people.ttl
Import it into your world:
Response
Each query below is a .rq file sent with the same pattern:

Is a missing fact false?

No. A SQL table makes a closed-world assumption: if a row or column value is absent, the answer is no. RDF makes an open-world assumption: if a fact is absent, the answer is unknown. A world records what you asserted and nothing about what you did not. That difference matters for agents. An agent that reads a missing email address as “has no email” invents a fact. An agent that reads it as “no email on record” reports what it knows. Ask for every person and their email address, if any:
emails.rq
Response
Sandra’s row has no email binding. Read it as “not recorded”. When your application needs a definite negative, assert it as its own fact so a query can tell it apart from a gap.

Will a schemaless graph turn into a mess?

Not if you add constraints once the model settles. A world accepts any well-formed RDF, so you can start without a schema, the way you start a JavaScript project without types. When a shape stabilizes, describe it with SHACL shapes and validate data against them before import, the way you add TypeScript types to code that already runs. The example data above has no schema. The next section adds one without rewriting the existing facts. Worlds does not validate writes against SHACL shapes yet, so run validation in your pipeline before data reaches the world. Validation on every write, on by default with a per-world opt-out, is planned (worlds-sdk-ts#151).

How does a schema change work?

In SQL, splitting a name column into first and last name means a migration: alter the table, backfill every row, and deploy readers and writers in step. In a world, you add the new properties beside the old one and move readers and writers over at their own pace.
1

Add the new properties beside the old one

Assert schema:givenName and schema:familyName for Ethan. His schema:name stays in place, so existing readers keep working.
split-name.rq
Response
A SPARQL update requires the data:write scope.
2

Read either shape

Update readers to prefer the new properties and fall back to the old one. The CONCAT is unbound when either part is missing, so COALESCE falls back to schema:name.
display-names.rq
Response
Ethan’s display name now comes from the new properties and Sandra’s from schema:name. Neither record had to change for the query to work.
3

Require the new shape for new data

Save a shape that requires both new properties on every schema:Person as shapes.ttl:
shapes.ttl
A new record written in the old shape fails validation. Save it as gregory.ttl:
gregory.ttl
Validate it with rdf-validate-shacl, the SHACL library behind the Wiki CLI. Save the following as validate.ts:
validate.ts
Install the dependencies and run it with Node.js 22.18 or later, Deno, or Bun:
Output
The script exits with status 1, so a CI step or import script stops here. SHACL is a W3C standard, so any conforming processor, such as pySHACL, accepts the same shapes. Rewrite the record in the new shape:
gregory.ttl
Output
Import it with the same request as people.ttl. The display-name query returns “Gregory Hale” from the new properties.
4

Backfill and retire the old property when you choose

Records written before the change, like Sandra’s, still carry only schema:name. Validate only new data until you backfill them. Once every reader uses the new properties, you can stop writing schema:name. Nothing sets a deadline for that step.