Explore the order-state demo
Change MySQL orders and PostgreSQL shipments, then watch one assembled MongoDB document stay current
Use the running playground to see the problem tapstate solves: one operational
object can depend on records owned by different databases. The sample combines
an order in MySQL with its shipments in PostgreSQL and keeps the assembled
views.order_state document current when either source changes.
Complete the Quickstart first. Run every
command on this page from the generated tapstate-demo directory.
Understand the sample
The playground creates this workspace:
work/
├── source/
│ ├── orders_db.tap.yml
│ └── fulfillment_db.tap.yml
└── pipeline/
└── order_pipeline.tap.ymlorders_dbcaptures the MySQLorderstable.fulfillment_dbcaptures the PostgreSQLshipmentstable.order_pipelineusesnestto embed matching shipments under each order.- The inline
viewmaterializes the result asviews.order_statein the playground's managed MongoDB store. It does not need aserveblock.
The sources use mode: cdc, and the pipeline uses
read_mode: snapshot_and_cdc. The initial rows arrive through Snapshot; later
inserts, updates, and deletes arrive through CDC.
Watch an order
Start a CLI session:
./tapstate -w workConnect, sign in with the password from .env, and start a live view:
connect http://127.0.0.1:8080
login admin
watch views.order_state {id:1}Keep this terminal open. Press Ctrl+C when you want to stop watching; the pipeline continues to run.
Add a shipment in PostgreSQL
Open a second terminal in tapstate-demo. Add a shipment to order 1:
docker compose exec postgres psql -U postgres -d appdb \
-c "INSERT INTO shipments VALUES (7, 1, 'ups', 'pending');"The watched order should gain a third item in its shipments array. To verify
the result directly with a bounded wait:
uri='mongodb://mongo:27017/views?directConnection=true'
i=0
until docker compose exec -T mongo mongosh --quiet "$uri" \
--eval 'quit((db.order_state.findOne({id:1})?.shipments?.length ?? 0) >= 3 ? 0 : 1)'; do
i=$((i+1))
[ "$i" -lt 60 ] || { echo "shipment did not appear within 60 seconds"; break; }
sleep 1
done
[ "$i" -lt 60 ]Change the order in MySQL
Update the parent record:
docker compose exec mysql mysql -uroot -psecret appdb \
-e "UPDATE orders SET customer='alicia' WHERE id=1;"Wait for the customer field to change without losing the shipment array:
uri='mongodb://mongo:27017/views?directConnection=true'
i=0
until docker compose exec -T mongo mongosh --quiet "$uri" \
--eval 'quit(db.order_state.findOne({id:1})?.customer=="alicia" ? 0 : 1)'; do
i=$((i+1))
[ "$i" -lt 60 ] || { echo "order did not update within 60 seconds"; break; }
sleep 1
done
[ "$i" -lt 60 ]
docker compose exec mongo mongosh --quiet "$uri" \
--eval 'db.order_state.find({id:1}).pretty()'The same document now reflects the MySQL update and still contains the PostgreSQL shipments. Tapstate maintains this object from two independent change streams; it does not run a cross-database SQL join for each read.
Remove the shipment
Delete the shipment you added:
docker compose exec postgres psql -U postgres -d appdb \
-c "DELETE FROM shipments WHERE id=7;"Wait until the shipments array returns to two elements:
uri='mongodb://mongo:27017/views?directConnection=true'
i=0
until docker compose exec -T mongo mongosh --quiet "$uri" \
--eval 'quit((db.order_state.findOne({id:1})?.shipments?.length ?? 0) <= 2 ? 0 : 1)'; do
i=$((i+1))
[ "$i" -lt 60 ] || { echo "shipment was not removed within 60 seconds"; break; }
sleep 1
done
[ "$i" -lt 60 ]The playground configures
REPLICA IDENTITY FULL for this table so PostgreSQL deletes carry the fields
needed to locate the embedded child. When you prepare your own PostgreSQL
source, follow the PostgreSQL CDC requirements.
Inspect and reset the environment
From the connected CLI, inspect the pipeline:
status order_pipeline
metrics order_pipeline
logs order_pipelineTo reset every sample database and rerun the Quickstart, remain in
tapstate-demo and run:
docker compose down -v
curl -fsSL https://install.tapstate.dev | shThe managed MongoDB instance is part of this disposable playground. It runs without database authentication and also stores tapstate control-plane data. Do not treat it as a tenant or user security boundary, and do not use sensitive or production data in this environment.
Next steps
- Create your own workspace with the installed CLI.
- Learn how nest assembles documents.
- Review view and serve to distinguish managed materialization from external delivery.
- Use Troubleshooting for quickstart, connector, or pipeline failures.