Koko Data Access — Quickstart

Platform databases (MongoDB, Redis, Neo4j) run inside the Kubernetes cluster and are not reachable from localhost. The koko CLI proxies all operations through the Koko API — authenticated, read-safe, and type-preserving.

Setup

# Install
npm install -g koko-db

# Authenticate (shares your tawa login session — run tawa login first)
tawa login
koko --version   # should be 0.5.0+

The One Rule

Never use curl, kubectl port-forward, or raw API calls to access databases.

WrongRight
curl http://localhost:3001/api/db/...koko query users -s my-api -e prod
kubectl port-forward ... && mongoshkoko copy users --from prod --to sandbox
Direct MongoDB connection stringskoko export → koko import

The koko CLI handles auth, safety guards, EJSON type preservation, and pagination automatically.

Service Name

koko resolves your service name automatically from the project directory. You can also pass it explicitly:

# From inside a service directory (reads catalog-info.yaml or .tawa.yaml)
koko query products -e prod

# Explicit service name
koko query products -s fairshare-cms -e prod

Inspect

# List databases provisioned for a service
koko databases -s my-api

# Collection stats
koko stats -s my-api -e prod
koko collections -s my-api -e prod

# Sample a few documents (random)
koko sample orders -s my-api -e prod --limit 5

Query

# Find with filter
koko query users -s my-api -e prod \
  --filter '{"status":"active"}' \
  --limit 20 \
  --sort '{"createdAt":-1}'

# With projection
koko query products -s my-api -e prod \
  --filter '{"active":true}' \
  --projection '{"name":1,"slug":1,"tiers":1}'

# Aggregate
koko query orders -s my-api -e prod \
  --aggregate '[{"$group":{"_id":"$paymentStatus","count":{"$sum":1}}}]'

# JSON output (for piping/scripting)
koko query users -s my-api -e prod --filter '{"email":"[email protected]"}' --json

Export / Import — EJSON Format

koko export outputs EJSON (MongoDB Extended JSON). ObjectIds are serialized as {"$oid":"..."} and Dates as {"$date":"..."} — not plain strings. This means the NDJSON file can be re-imported with correct BSON types and no corruption.

# Export a collection to file
koko export products -s my-api -e prod -o /tmp/products.ndjson

# Export with a filter
koko export orders -s my-api -e prod \
  --filter '{"createdAt":{"$gte":"2026-01-01"}}' \
  -o /tmp/recent-orders.ndjson

Import via koko import — EJSON is handled natively, no flags needed:

koko import products -s my-api -e sandbox -i /tmp/products.ndjson --upsert

If you must use mongoimport (into local MongoDB), add --legacy so it recognises $oid/$date markers:

mongoimport --db my-api-dev --collection products \
  --file /tmp/products.ndjson --legacy

Prefer koko import over mongoimport — no flags to remember, round-trip is always correct.

Copy Between Environments

Server-side copy — no download, no EJSON conversion, fastest option:

# Preview count first
koko copy products -s my-api --from prod --to sandbox --dry-run

# Execute
koko copy products -s my-api --from prod --to sandbox

Sync Prod to Local — Full Workflow

When you need your local MongoDB to exactly match production:

# Step 1: Export all collections from prod
koko export products -s my-api -e prod -o /tmp/products.ndjson
koko export users    -s my-api -e prod -o /tmp/users.ndjson
koko export orders   -s my-api -e prod -o /tmp/orders.ndjson

# Step 2: Import into local MongoDB (--legacy handles $oid/$date markers)
mongoimport --db my-api-dev --collection products --file /tmp/products.ndjson --legacy --drop
mongoimport --db my-api-dev --collection users    --file /tmp/users.ndjson    --legacy --drop
mongoimport --db my-api-dev --collection orders   --file /tmp/orders.ndjson   --legacy --drop

# OR: sync to sandbox via koko (skip --legacy, it's handled server-side)
koko import products -s my-api -e sandbox -i /tmp/products.ndjson --upsert
koko import users    -s my-api -e sandbox -i /tmp/users.ndjson    --upsert

After importing to local, restart your dev server to clear any in-memory caches:

# If using Next.js
rm -rf .next && npm run dev

Seed Collections

For development seeds committed to the repo:

# Seeds live in ./seeds/<collection>.ndjson
koko seed -s my-api -e sandbox

# Single collection
koko seed -s my-api -e sandbox --collection products

# Dry-run
koko seed -s my-api -e sandbox --dry-run

Redis

koko keys      -s my-api -e prod --pattern 'session:*' --limit 100
koko get       -s my-api -e prod 'my-api:some-key'
koko redis-info -s my-api -e prod

Neo4j

koko cypher "MATCH (n) RETURN labels(n), count(n) LIMIT 10" -s my-api -e prod
koko neo4j-stats -s my-api -e prod --json

Safety Guards

All koko operations are read-only by default:

  • MongoDB: $out and $merge aggregate stages are blocked
  • Cypher: CREATE, MERGE, DELETE, SET, REMOVE, DROP are blocked
  • Max 1,000 documents per query response
  • 30-second query timeout

Write operations (koko import, koko copy, koko seed) require explicit --upsert or --allow-prod confirmation for production targets.

EJSON — Why It Matters

MongoDB stores ObjectIds and Dates as special BSON types (not plain strings). When exported as plain JSON, ObjectId("abc123") becomes the string "abc123" — which looks valid but fails as a MongoDB _id on re-import because the type is wrong.

EJSON preserves the type information:

{ "_id": {"$oid": "abc123..."}, "createdAt": {"$date": "2026-01-15T..."} }

koko export always outputs EJSON. koko import always deserializes it. The round-trip is type-safe by default — no manual conversion scripts needed.

Last updated: July 15, 2026