TypeScript Client — Quickstart

Open inAnthropic

What you'll achieve By the end of this guide, you will have established a connection to TerminusDB using the JavaScript client.

This guide uses TypeScript. Also available in Python · Rust

Prerequisites

If you haven't run the curl quickstart yet, do that first — it takes 5 minutes and introduces the branch/diff/merge concepts this guide builds on.

TerminusDB must be running

This guide assumes TerminusDB is running on localhost:6363. If you have trouble connecting, see Troubleshooting Connection Failures and Authentication Errors.

Install the client

Example: Bash
mkdir my-terminusdb-app && cd my-terminusdb-app
npm init -y
npm install terminusdb

Create a file called index.ts. You will build it up step by step below, or copy the complete file at the end.

Connect to TerminusDB

Example: TypeScript
import TerminusClient from "terminusdb"

const client = new TerminusClient.WOQLClient("http://localhost:6363", {
  user: "admin",
  organization: "admin",
  key: "root",
})

// Verify the connection works
const info = await client.info()
console.log("Connected to TerminusDB", info)

What you should see: A server info object with version and storage details. If you see a connection error, check that Docker is running and TerminusDB is on port 6363.

Create a database and insert a document

No schema needed — insert any JSON document with raw_json: true and give it a human-readable ID:

Example: TypeScript
// Create a database (no schema required)
await client.createDatabase("MyDatabase", {
  label: "My Database",
  comment: "TypeScript quickstart",
  schema: false,
})

// Insert a document — choose your own ID, no schema to define
const result = await client.addDocument(
  { "@id": "terminusdb:///data/jane", name: "Jane Smith", email: "jane@example.com", age: 30 },
  { raw_json: true },
)

console.log("Document created:", result)

What you should see: ["terminusdb:///data/jane"]. The @id gives the document a stable, human-readable identifier you choose. Schema comes later when you are ready.

Create a branch

Just like git branch, this creates an isolated copy of your data:

Example: TypeScript
// Create a new branch from main
await client.branch("feature")

// Switch to it (like git checkout)
client.checkout("feature")

console.log("Now on branch:", client.checkout())

What you should see: Now on branch: feature

Everything you do now happens on feature — main is untouched.

Edit the document on the branch

Example: TypeScript
// Get the document we inserted earlier
const person = await client.getDocument({ id: "terminusdb:///data/jane", as_list: true })

console.log("Current document:", person)

// Update the email on this branch
await client.updateDocument(
  { "@id": "terminusdb:///data/jane", name: "Jane Smith", email: "jane.smith@company.com", age: 30 },
  { raw_json: true },
  "",
  "Updated Jane's email",
)

console.log("Document updated on feature branch")

What you should see: The document retrieved, then confirmation of the update. The change only exists on featuremain still has the original email.

See the diff

This is the moment. TerminusDB can show you exactly what changed between branches — structurally, field by field:

Example: TypeScript
// Compare main to feature — what changed?
const diff = await client.getVersionDiff("main", "feature")

console.log("Changes between main and feature:")
console.log(JSON.stringify(diff, null, 2))

What you should see:

Example: JSON
[
  {
    "@id": "terminusdb:///data/jane",
    "email": { "@op": "SwapValue", "@before": "jane@example.com", "@after": "jane.smith@company.com" }
  }
]

What just happened? TerminusDB computed a structural diff — not a line-by-line text diff, but semantic operations (SwapValue) that know exactly which fields changed, what the old values were, and what the new values are. This patch can be applied, reversed, or composed with other patches.

Merge the branch

Bring the changes back to main:

Example: TypeScript
// Switch back to main
client.checkout("main")

// Merge feature into main (like git merge)
await client.rebase({
  rebase_from: "admin/MyDatabase/local/branch/feature",
  message: "Merge feature: updated Jane's email",
})

console.log("Merged feature into main")

What you should see: Merged feature into main

Verify the merge

Confirm the changes are now on main:

Example: TypeScript
// Read the document from main
const updated = await client.getDocument({ id: "terminusdb:///data/jane", as_list: true })

console.log("Person on main after merge:", updated)

What you should see: Jane Smith with jane.smith@company.com — the changes from the feature branch are now on main.

What just happened?

You just used your database like a git repository:

  1. Branched — created an isolated workspace (feature) without copying data
  2. Edited — made changes that only exist on that branch
  3. Diffed — asked TerminusDB to show exactly what changed, field by field
  4. Merged — brought changes back to main cleanly

This is the core workflow of TerminusDB. Every change is a commit. Every prior state is recoverable. Multiple branches can exist simultaneously — for testing, staging, feature development, or collaboration.

The complete index.ts

Run with npx tsx index.ts:

Example: TypeScript
import TerminusClient from "terminusdb"

async function main() {
  // 1. Connect
  const client = new TerminusClient.WOQLClient("http://localhost:6363", {
    user: "admin",
    organization: "admin",
    key: "root",
  })

  // 2. Create database + insert document (no schema)
  await client.createDatabase("MyDatabase", {
    label: "My Database",
    comment: "TypeScript quickstart",
    schema: false,
  })

  const result = await client.addDocument(
    { "@id": "terminusdb:///data/jane", name: "Jane Smith", email: "jane@example.com", age: 30 },
    { raw_json: true },
  )
  console.log("Document created:", result)

  // 3. Branch
  await client.branch("feature")
  client.checkout("feature")
  console.log("Now on branch:", client.checkout())

  // 4. Edit on branch
  const person = await client.getDocument({ id: "terminusdb:///data/jane", as_list: true })

  await client.updateDocument(
    { "@id": "terminusdb:///data/jane", name: "Jane Smith", email: "jane.smith@company.com", age: 30 },
    { raw_json: true },
    "",
    "Updated Jane's email",
  )
  console.log("Document updated on feature branch")

  // 5. See the diff
  const diff = await client.getVersionDiff("main", "feature")
  console.log("\nChanges between main and feature:")
  console.log(JSON.stringify(diff, null, 2))

  // 6. Merge
  client.checkout("main")
  await client.rebase({
    rebase_from: "admin/MyDatabase/local/branch/feature",
    message: "Merge feature: updated Jane's email",
  })
  console.log("\nMerged feature into main")

  // 7. Verify
  const updated = await client.getDocument({ id: "terminusdb:///data/jane", as_list: true })
  console.log("\nPerson on main after merge:", updated)
}

main().catch(console.error)

If you've already run this quickstart, delete the database before running again:

Example: TypeScript
await client.deleteDatabase("MyDatabase")

Troubleshooting

Error: Connection refused

You see:

Example: Text
Error: connect ECONNREFUSED 127.0.0.1:6363

Cause: TerminusDB is not running, or another process is using port 6363.

Fix:

Example: Bash
# Check if the container is running
docker ps | grep terminusdb

# If not running, start it
docker start terminusdb

# If it doesn't exist, create it
docker run -d --name terminusdb -p 127.0.0.1:6363:6363 \
  -v terminusdb_storage:/app/terminusdb/storage \
  terminusdb/terminusdb-server

Troubleshooting Connection Failures →

Error: 401 Unauthorized

You see:

Example: Text
Error: 401 Unauthorized

Cause: Wrong username or password. The default local credentials are admin / root.

Fix:

# Verify credentials work
curl -u admin:root http://localhost:6363/api/info

If you set a custom password via TERMINUSDB_ADMIN_PASS when creating the container, use that value instead of root.

Troubleshooting Authentication Errors →

Error: Database does not exist (404)

You see:

Example: Text
Error: 404 - {"@type":"api:ErrorResponse","api:error":{"@type":"api:UnresolvableAbsoluteDescriptor"}}

Cause: You are trying to read/write a database that has not been created yet, or the path is wrong.

Fix:

# List existing databases
curl -u admin:root http://localhost:6363/api/db

# Create the database if missing
curl -u admin:root -X POST "http://localhost:6363/api/db/admin/MyDatabase" \
  -H "Content-Type: application/json" \
  -d '{"label": "MyDatabase", "comment": "My database"}'

Error: Cannot find module 'terminusdb'

You see:

Example: Text
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'terminusdb'

or

Example: Text
Cannot find module 'terminusdb' or its corresponding type declarations

Cause: The package is not installed, or you are running from the wrong directory.

Fix:

Example: Bash
# Make sure you are in the project directory
cd my-terminusdb-app

# Install the package
npm install terminusdb

Error: Cannot use import statement outside a module

You see:

Example: Text
SyntaxError: Cannot use import statement outside a module

Cause: You are running the file with node index.ts without ESM support.

Fix: Use npx tsx (recommended) which handles both TypeScript and ESM:

Example: Bash
npx tsx index.ts

Alternatively, add "type": "module" to your package.json if using plain JavaScript.

Error: Database already exists

You see:

Example: Text
Error: 400 - {"@type":"api:DbCreateErrorResponse","api:error":{"@type":"api:DatabaseAlreadyExists"}}

Cause: You ran this quickstart before and the database still exists.

Fix:

Example: TypeScript
await client.deleteDatabase("MyDatabase")

Next steps

Was this helpful?