Verify with OQL Skill
SkillWeb & browsingLets your agent check that data changes in a running app happened as expected by querying its database.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Verify with OQL Skill skill
About this capability
Verify microflow side effects and data changes with OQL against a running app. Use after executing changes to confirm data was created, updated or deleted as expected, or to back a browser test with a data assertion.
What this skill tells your AI
The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/verify-with-oql/SKILL.md and read by ahel’s review.
This skill documents how to verify microflow side effects and data changes using OQL queries against a running Mendix app.
When to Use This Skill
Use this when:
- You've deployed changes and want to verify data was created/updated/deleted correctly
- You need to confirm a microflow produced the expected side effects
- You're combining Playwright UI tests with backend data verification
- You want a fast feedback loop: deploy, trigger, verify
The Pattern
The core verification workflow is:
- Deploy — apply MDL changes and rebuild
- Trigger — execute the action (via UI, microflow call, or API)
- Verify — query the database with OQL to confirm the result
# 1. Deploy
mxcli exec changes.mdl -p app.mpr
mxcli docker build -p app.mpr --skip-check
mxcli docker reload -p app.mpr # or: mxcli docker up -p app.mpr --fresh --wait
# 2. Trigger (example: call a microflow that creates test data)
# This could be via Playwright, a rest call, or manual interaction
# 3. Verify
mxcli oql -p app.mpr "select Name, Email from MyModule.Customer where Name = 'Jane Doe'"
OQL Verification Examples
Check data was created
# Verify a customer was created
mxcli oql -p app.mpr "select Name, Email from Sales.Customer where Name = 'Test Customer'"
# count records
mxcli oql -p app.mpr "select count(*) as Total from Sales.Order"
Check data was updated
# Verify status was changed
mxcli oql -p app.mpr "select OrderNumber, status from Sales.Order where OrderNumber = 'ORD-001'"
Check data was deleted
# Verify record no longer exists (should return empty)
mxcli oql -p app.mpr "select count(*) as Total from Sales.Customer where Name = 'Deleted Customer'"
Check associations
# Verify an association was set (join query)
mxcli oql -p app.mpr \
"select o.OrderNumber, c.Name from Sales.Order o join o/Sales.Order_Customer/Sales.Customer c where o.OrderNumber = 'ORD-001'"
JSON output for assertions
Use --json for structured output that's easy to parse in scripts:
# json output for piping to jq
mxcli oql -p app.mpr --json "SELECT Name FROM Sales.Customer" | jq '.[].Name'
# count check in a script
count=$(mxcli oql -p app.mpr --json "SELECT count(*) AS Total FROM Sales.Order" | jq -r '.[0].Total')
if [ "$count" -gt 0 ]; then
echo "Orders exist: $count"
fi
Combining with Playwright
The most powerful pattern: trigger actions through the UI with Playwright, then verify side effects with OQL.
Example: Create via UI, verify via OQL
// tests/verify-create.spec.ts
import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import { login } from './utils/login';
function oql(query: string): any[] {
const result = execSync(
`mxcli oql -p app.mpr --json "${query}"`,
{ encoding: 'utf-8' }
);
return JSON.parse(result);
}
test('creating a customer via UI persists correctly', async ({ page }) => {
await login(page);
await page.goto('/p/Customer_Edit');
// Fill the form
await page.locator('.mx-name-txtName input').fill('OQL Test Customer');
await page.locator('.mx-name-txtEmail input').fill('oql@test.com');
await page.locator('.mx-name-btnSave').click();
// wait for save to complete
await page.waitForTimeout(2000);
// Verify via OQL
const rows = oql("select Name, Email from Sales.Customer where Name = 'OQL Test Customer'");
expect(rows).toHaveLength(1);
expect(rows[0].Email).toBe('oql@test.com');
});
Example: Verify microflow side effects
test('approving an order updates status and creates audit log', async ({ page }) => {
await login(page);
await page.goto('/p/Order_Overview');
// Click approve on first order
await page.locator('.mx-name-btnApprove').first().click();
await page.waitForTimeout(2000);
// Verify order status changed
const orders = oql("select status from Sales.Order where OrderNumber = 'ORD-001'");
expect(orders[0].Status).toBe('Approved');
// Verify audit log was created
const logs = oql("select action from Sales.AuditLog where action = 'Order Approved'");
expect(logs.length).toBeGreaterThan(0);
});
Combining with Hot Reload
For the fastest iteration loop when developing microflow logic:
# 1. Edit microflow
mxcli exec fix-logic.mdl -p app.mpr
# 2. Rebuild (fast)
mxcli docker build -p app.mpr --skip-check
# 3. Hot reload (no restart, keeps data)
mxcli docker reload -p app.mpr
# 4. Trigger the microflow (via UI or test)
# 5. Verify the result
mxcli oql -p app.mpr "select status from Sales.Order where OrderNumber = 'ORD-001'"
# Repeat from step 1 until correct
This loop avoids container restarts and database resets, making each iteration take seconds instead of minutes.
Tips
- Use
--jsonfor scripted assertions — structured output is easier to parse than table format - OQL is read-only —
mxcli oqluses thepreview_execute_oqlaction which cannot modify data - OQL won't see rows you wrote directly into Postgres until the runtime reloads.
mxcli oqlexecutes through the running app's query layer (not a separate DB), so rows seeded with direct SQLINSERTs (see demo-data) are invisible until the runtime re-reads them. After seeding, runmxcli docker reload(or restart the app) before trusting an OQLcount(*)of0. To confirm the rows landed before a reload, query Postgres directly (mxcli sql …). - Check before and after — query the state before triggering an action to establish a baseline
- Common OQL patterns for testing:
count(*)to verify record countswhereclauses to find specific recordsjointo verify associations were setORDER by ... limit 1to check the most recent record
--directmode is faster when the admin port is reachable (afteradmin.addressesbuild patch)
Related Skills
- /test-app — Full Playwright test patterns and setup
- /runtime-admin-api — Admin API details and curl examples
- /docker-workflow — Build, run, and hot reload workflow
- /write-oql-queries — OQL syntax reference
Signals
- GitHub stars
- 122
- Forks
- 49
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
verify-with-oql- Source
- github.com/mendixlabs/mxcli