Ddd Aggregate
ruvnet/ruflo
Scaffold an aggregate root with entity, value objects, repository interface, domain events, and test stubs.
Write OQL for Mendix VIEW entities — joins, aggregates, calculated fields, and the syntax the runtime actually accepts.
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install mendixlabs/mxcli write-oql-queries --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .claude/skills/write-oql-queries && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .claude/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queriesType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install mendixlabs/mxcli write-oql-queries --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .agents/skills/write-oql-queries && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .agents/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install mendixlabs/mxcli write-oql-queries --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .cursor/skills/write-oql-queries && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .cursor/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/mendixlabs/mxcli.git --path .claude/skills/mendix/write-oql-queries--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install mendixlabs/mxcli write-oql-queries --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .gemini/skills/write-oql-queries && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .gemini/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install mendixlabs/mxcli write-oql-queriesInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .github/skills/write-oql-queries && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .github/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill write-oql-queries -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install mendixlabs/mxcli write-oql-queries --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/mendix/write-oql-queries .opencode/skills/write-oql-queries && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "write-oql-queries" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/write-oql-queries into .opencode/skills/write-oql-queries/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "write-oql-queries", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
write-oql-queriesWrite OQL for Mendix VIEW entities — joins, aggregates, calculated fields, and the syntax the runtime actually accepts.
Write Oql Queries is an agent skill from mendixlabs/mxcli. Write OQL for Mendix VIEW entities — joins, aggregates, calculated fields, and the syntax the runtime actually accepts. Use when creating a VIEW entity or building a report or analytics query.
Its SKILL.md is about 6.2k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `reference/patterns.md`).
The repository describes itself as: Mendix cli tool, a headless way to work with Mendix projects. Enables Mendix projects for use with 3rd party agentic coding tools like Claude Code and Copilot. Includes a… The licence is Apache-2.0.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit a924d11. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
jqFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
docs.mendix.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Write Oql Queries loads about 6.2k tokens when it runs. Until then it costs about 53 tokens; SKILL.md has 2,114 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
# basic query (reads .docker/.env for connection settings)Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from mendixlabs/mxcli at commit a924d11, republished under its Apache-2.0 licence (© mendixlabs). 2,114 words, ~6,205 tokens.
.claude/skills/write-oql-queries/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.reference/patterns.md — the recurring OQL shapes
(aggregation, joins across associations, date bucketing, ranking, filtered
counts) and a full worked query, to adapt rather than derive.Generate correct OQL (Object Query Language) queries for Mendix VIEW entities. This skill helps you create VIEW entities with proper OQL syntax that will execute successfully in Mendix runtime.
RULE 1: All SELECT columns MUST have explicit AS aliases
Every column in the SELECT clause must have an alias that matches the entity attribute name:
-- ❌ WRONG - Missing aliases
create view entity Finance.CashFlowProjection (
ProjectionDate: datetime,
ProjectedIncome: decimal,
ProjectedExpense: decimal
) as (
select
fl.ForecastDate, -- Missing AS alias
fl.ProjectedIncome, -- Missing AS alias
fl.ProjectedExpense -- Missing AS alias
from Finance.ForecastLine as fl
);mdl 1;
-- ✅ CORRECT - All columns have explicit aliases
create view entity Finance.CashFlowProjection (
ProjectionDate: datetime,
ProjectedIncome: decimal,
ProjectedExpense: decimal
) as (
select
fl.ForecastDate as ProjectionDate,
fl.ProjectedIncome as ProjectedIncome,
fl.ProjectedExpense as ProjectedExpense
from Finance.ForecastLine as fl
);RULE 2: ORDER BY requires a LIMIT — prefer letting the consuming query sort
ORDER BY alone is rejected (mxcli check → MDL030; mx check → CE0174).
ORDER BY with a LIMIT is valid and builds clean — that is exactly how you
express a top-N view. As a default, prefer no ORDER BY/LIMIT so the UI
component or microflow can sort and paginate the same view differently; reach for
ORDER BY … LIMIT only when the view is intrinsically a top-N.
-- ❌ WRONG - ORDER BY without LIMIT (MDL030 / CE0174)
create view entity Finance.TopCustomers (...) as (
select c.Name as CustomerName, sum(o.Amount) as TotalSpent
from Finance.Customer as c
inner join Finance.Order_Customer/Finance.Order as o
GROUP by c.Name
ORDER by TotalSpent desc -- needs a LIMIT
);
-- ✅ CORRECT (preferred) - let the consuming page/microflow sort
create view entity Finance.CustomerTotals (...) as (
select c.Name as CustomerName, sum(o.Amount) as TotalSpent
from Finance.Customer as c
inner join Finance.Order_Customer/Finance.Order as o
GROUP by c.Name
);
-- ✅ ALSO VALID - an intrinsic top-N view (ORDER BY paired with LIMIT)
create view entity Finance.TopCustomers (...) as (
select c.Name as CustomerName, sum(o.Amount) as TotalSpent
from Finance.Customer as c
inner join Finance.Order_Customer/Finance.Order as o
GROUP by c.Name
ORDER by TotalSpent desc
LIMIT 100
);Why these rules matter:
ORDER BY must be paired with LIMIT (MDL030)UNION / UNION ALL are supported in view-entity OQL and round-trip cleanly —
use them to combine multiple row kinds in one view (e.g. category rows plus group
subtotals). Column count and types must line up across branches; ORDER BY (with
its LIMIT) applies to the whole unioned result, not a single branch.
mdl 1;
create or modify view entity Ledger.CategoryAndSubtotals (
Label: string(100), Amount: decimal
) as (
select c.Name as Label, sum(t.Amount) as Amount
from Ledger.Category as c
left join Ledger.Transaction_Category/Ledger.Transaction as t
group by c.Name
union all
select 'TOTAL' as Label, sum(t.Amount) as Amount
from Ledger.Transaction as t
);-- ❌ WRONG - Uppercase will fail
sum(o.Amount)
avg(o.Amount)
max(o.OrderDate)
min(o.Amount)
-- ✅ CORRECT - Lowercase
sum(o.Amount)
avg(o.Amount)
max(o.OrderDate)
min(o.Amount)-- ❌ WRONG - count(*) not supported in Mendix OQL
count(*)
-- ✅ CORRECT - Count by ID or entity
count(t.ID) -- Count by ID attribute
count(t) -- Count entity instancesCounting rows of a view entity: a view entity has no ID, so count(v.ID)
over a view does not work. Count a column that is never empty instead —
count(v.Name) for a column every row fills. The same goes for a literal: count(1) / sum(1) look harmless but
fail on HSQLDB, Studio Pro's default database (see View columns and HSQLDB
below).
Measured on mxbuild 11.13.0 and run --local against HSQLDB and PostgreSQL
(ako/mxcli#981). mxcli check reports each:
| Write | Not | Why |
|---|---|---|
case when r.A = r.B then true else false end as Same | r.A = r.B as Same | a comparison is not a select expression: CE0174 (MDL033) |
count(r.Name) next to group by r.Season | count(r.Season) next to group by r.Season | aggregating a grouped column (also through datepart(…) or r.Season + 1) is CE0174 (MDL034) |
| every plain column in the GROUP BY, or aggregated | r.Name next to group by r.Season (or group by r.ID) | CE0174 (MDL035) |
the select expression equal to a GROUP BY expression (group by datepart(YEAR, r.D) → select datepart(YEAR, r.D)) | datepart(MONTH, r.D) next to group by datepart(YEAR, r.D) | builds, then the database refuses it when the view is read — PostgreSQL 42803, HSQLDB 42574 (MDL036) |
count(r.Name), sum(cast(1 as Integer)), sum(case when … then 1 else 0 end) | sum(1), count(1), max(0), count('x'), count(true), sum(0.0), sum(1.5) | Mendix sends the literal as an untyped parameter; HSQLDB refuses with 42567 "data type cast needed" (MDL037 warning) |
cast(1 as Integer) as One, cast('Label' as String) as Kind | 1 as One | the view reads fine, but on HSQLDB v.One + 1 returns 11 (string concatenation; PostgreSQL returns 2) and aggregating the column fails (MDL038 note) |
r.Name + ' x' as S declared string(200) | declared string or string(100) | string concatenation is a derived String(200) whatever its operands; anything else is CE6770 (MDL031) |
a view attribute over an AutoNumber column declared long | declared autonumber | CE6770; refused under mdl 1;, a warning without the header (MDL-V1-VIEWAUTONUMBER) |
A decimal literal as a column (0.0 as Amount) is sent with a cast and is fine; inside an aggregate (sum(0.0)) it fails like the others. avg(1) runs. A bare
string label such as 'TOTAL' as Label only draws the MDL038 note — it reads
fine as long as nothing aggregates it.
| Function | Input Type | Returns | MDL Declaration |
|---|---|---|---|
count(expr) | any | Integer | attr: integer |
sum(expr) | Integer | Integer | attr: integer |
sum(expr) | Decimal | Decimal | attr: decimal |
avg(expr) | any numeric | Decimal | attr: decimal |
max(expr) / min(expr) | Integer | Integer | attr: integer |
max(expr) / min(expr) | Decimal | Decimal | attr: decimal |
max(expr) / min(expr) | DateTime | DateTime | attr: datetime |
datepart(part, expr) | DateTime | Integer | attr: integer |
length(expr) | String | Integer | attr: integer |
Key rule: count() and avg() have fixed return types. sum(), min(), max() preserve the input type.
-- ✅ CORRECT - Use comma syntax
datepart(YEAR, t.TransactionDate)
datepart(MONTH, t.TransactionDate)
datepart(QUARTER, t.TransactionDate)
datepart(WEEK, t.TransactionDate)
datepart(DAY, t.TransactionDate)
-- ❌ WRONG - FROM syntax not supported
DATEPART(YEAR from t.TransactionDate)-- ❌ WRONG - Qualified enum names
t.TransactionType = Finance.TransactionType.INCOME
t.Status != Finance.TransactionStatus.VOID
-- ✅ CORRECT - Use string literals
t.TransactionType = 'INCOME'
t.Status != 'VOID'-- ❌ WRONG - Using / causes parsing errors
select amount / quantity as price
select (total - discount) * 100.0 / total as percentage
-- ✅ CORRECT - Use : for division
select amount : quantity as price
select (total - discount) * 100.0 : total as percentage-- ❌ WRONG - Using expressions in ORDER BY
ORDER by datepart(YEAR, t.TransactionDate) desc
-- ✅ CORRECT - Use column aliases
select
datepart(YEAR, t.TransactionDate) as OrderYear
from Finance.Transaction as t
ORDER by OrderYear descORDER BY <attribute> DESC does not give you the newest rows when some rows
leave that attribute empty. Mendix emits the ordering with no null placement, so
the database default applies — on PostgreSQL, DESC means NULLS FIRST.
-- ❌ MISLEADING - the empty rows come back first, so a top-N is not the top N
select g.Label as Label from Sudoku.Game as g
order by g.DealtAt desc
limit 5
-- ✅ CORRECT when the attribute is optional
select g.Label as Label from Sudoku.Game as g
order by g.DealtAt desc nulls last
limit 5This is worth knowing because it does not look like a null problem. The result is
stable across runs, so it reads as "the ordering is being ignored" rather than
"the sort key is empty for some rows" — and the natural next step, falling back to
order by id desc, answers a different question (insertion order, which only
matches recency when nothing backdates a row).
Check before concluding anything:
select count(*) as n from Sudoku.Game where DealtAt = empty.
Measured on Mendix 11.13.0 / PostgreSQL: with values present, ORDER BY on a
DateTime is emitted to the database correctly and orders correctly. Null placement
is database-specific (SQL Server and Oracle differ), so being explicit is also the
portable choice.
-- ❌ WRONG - <> causes errors in Mendix
where t.Status <> 'VOIDED'
-- ✅ CORRECT - Use !=
where t.Status != 'VOIDED'Note: Both != and <> are valid in standard SQL, but Mendix OQL only accepts !=.
-- ✅ IN with value list
where t.Status in ('ACTIVE', 'PENDING', 'REVIEW')
-- ✅ IN with subquery
where t.CustomerId in (
select c.CustomerId from Shop.Customer as c where c.IsVIP = true
)
-- ✅ Enumeration values use identifiers, not captions
where t.Priority in ('HIGH', 'CRITICAL') -- Not 'High', 'Critical'-- ✅ Scalar subquery in SELECT (returns single value)
select
p.Name as ProductName,
p.Price - (select avg(p2.Price) from Shop.Product as p2) as DiffFromAvg
from Shop.Product as p
-- ✅ Scalar subquery in WHERE
where p.Price > (select avg(p2.Price) from Shop.Product as p2)
-- ✅ Correlated subquery (references outer query by attribute)
select
o.OrderNumber as OrderNumber,
(select count(o2.OrderId) from Shop.Order as o2 where o2.CustomerId = o.CustomerId) as CustomerOrderCount
from Shop.Order as o
-- ✅ Correlated subquery via association (compare to .ID)
select
p.Name as ProductName,
(select pr.PriceInEuro from Shop.Price as pr
where pr/Shop.Price_Product = p.ID
ORDER by pr.StartDate desc limit 1) as LatestPrice
from Shop.Product as p
-- ❌ WRONG - bare alias without .ID
where pr/Shop.Price_Product = p -- Doesn't resolve
-- ✅ CORRECT - compare to entity .ID
where pr/Shop.Price_Product = p.ID-- Association paths in OQL use '/' not '.'
-- ✅ CORRECT - slash prefix for association traversal
where l/Library.Loan_Member = m.ID
join l/Library.Loan_Book/Library.Book as b
-- ❌ WRONG - dot instead of slash
where l.Library.Loan_Member = m.ID -- Error: does not resolveMendix OQL supports both association traversal and SQL-style JOIN ON:
-- ✅ Association traversal (uses Mendix association path)
from Shop.Order as o
inner join o/Shop.Order_Customer/Shop.Customer as c
-- ✅ JOIN ON clause (SQL-style, for any condition)
from Shop.Order as o
inner join Shop.Customer as c on o.CustomerId = c.CustomerId
-- ✅ LEFT OUTER JOIN with ON clause
from Shop.Product as p
left outer join Shop.CompetitorProduct as cp on p.ProductCode = cp.ProductCodeWhen to use each approach:
alias/Module.Association/entity): When joining on a Mendix-defined associationjoin entity on condition): When joining on arbitrary conditions or non-association fieldsAlways include @Position annotation:
/**
* View entity description
*
* @since 1.0.0
*/
@position(300, 500)
create view entity Module.ViewName (
Attribute1: type,
Attribute2: type,
-- ... more attributes
) as (
-- OQL query goes here
);Mendix OQL accepts the select list in either position, and mxcli reads both:
-- Select-first. Write new views this way; the rest of this skill assumes it.
select c.Name as Name, count(o.ID) as Orders
from Shop.Customer as c
group by c.Name
-- From-first. Same query. This is what STUDIO PRO STORES, so it is what
-- `describe entity` gives you back — copy it, edit it, exec it unchanged.
from Shop.Customer as c
group by c.Name
select c.Name as Name, count(o.ID) as OrdersNote where group by sits: in the from-first order every clause except
order by / limit comes before the select list, and the grammar enforces
that. from … select … group by … is a parse error, not a variant.
Do not rewrite a described view into select-first just to make it look familiar — the stored text is what MxBuild validates against, and a needless rewrite is a diff for nothing.
Selecting a persistent entity's ID under an alias gives the view entity an
association to that entity. The alias becomes the association's name, and the
column is not one of the view entity's attributes — so do not declare one
for it:
mdl 1;
create view entity Sales.OrdersVE (
order_date: DateTime -- one attribute…
) as (
from Sales."Order" as o
select o.ID as persistent_order -- …but two columns
, o.OrderDate as order_date
);mxcli creates the association member from that column. There is no separate
statement for it, and create association with a view entity at either end is
refused — Mendix rejects it (CE6771), because the association needs an
OqlViewAssociationSource that a plain one does not have.
Two rules:
as meter beside an entity called Meter fails — name it MeterRef.join r/Trends.Reading_Meter/Trends.Meter as m … select m.ID as MeterRef.MeterRef: Trends.Meter (or
Trends.Meter.ID) in the attribute list parses — a bare qualified name is how
MDL spells an enumeration type — and would be stored as an enumeration naming
an entity: CE1613 at build, or mx check failing to load the project. mxcli
refuses it (MDL080). The attribute list holds only the non-id columns.Consider the flat alternative first. An association costs a second query at
runtime — the view returns the foreign key, and the client then fetches the
referenced objects in a batched IN (...) per page, materialising real objects
in its state. Selecting a string copy instead is one statement, one join, no
second retrieve, and the id is still there to look the object up with:
select cast(m.ID as string) as MeterId, m.MeterCode as MeterCode, …Use the association when you want to bind widgets over it (MeterRef/MeterCode);
use the cast when you just need the value.
sum(), avg(), count()count(entity.ID) not count(*) — over a view entity (no ID), count a non-null columnsum(1), count(1)): it fails on HSQLDB — count a column, or sum(cast(1 as Integer)): for division operationsEntity_Association/TargetEntity-- Association join syntax
inner join Shop.Order_Customer/Shop.Customer as c
left join Shop.Product_Category/Shop.Category as cat'value'=, !=, >, <, >=, <=datepart())mxcli check view.mdl -p app.mpr --referencesThis catches type mismatches (e.g., declaring long for a count() column that returns integer), missing module references, and OQL syntax errors — before they become MxBuild errors like CE6770 ("View Entity is out of sync with the OQL Query").
-- WRONG
select sum(amount) from ...
-- CORRECT
select sum(amount) from ...-- WRONG
select count(*) from Finance.Transaction
-- CORRECT
select count(t.ID) from Finance.Transaction as t
-- CORRECT over a view entity, which has no ID: count a non-null column
select count(v.Name) from Finance.TransactionSummary as v-- WRONG
where t.Status = Finance.Status.ACTIVE
-- CORRECT
where t.Status = 'ACTIVE'-- WRONG
select total / count as average
-- CORRECT
select total : count as average-- WRONG
select
fl.ForecastDate,
fl.ProjectedIncome
from Finance.ForecastLine as fl
-- CORRECT
select
fl.ForecastDate as ProjectionDate,
fl.ProjectedIncome as ProjectedIncome
from Finance.ForecastLine as fl-- WRONG - dot notation for association
where l.Library.Loan_Member = m.ID
-- CORRECT - slash notation
where l/Library.Loan_Member = m.ID-- WRONG - comparing association to bare entity alias
where pr/Shop.Price_Product = p
-- CORRECT - compare to entity .ID
where pr/Shop.Price_Product = p.ID-- WRONG - ORDER BY alone (MDL030 / CE0174)
create view entity Finance.TopItems (...) as (
select ...
ORDER by Amount desc -- needs a LIMIT
);
-- CORRECT (preferred) - let the UI sort/paginate
create view entity Finance.ItemTotals (...) as (
select ...
-- no ORDER BY / LIMIT
);
-- ALSO VALID - an intrinsic top-N view
create view entity Finance.TopItems (...) as (
select ...
ORDER by Amount desc
LIMIT 100
);Use mxcli oql to test queries against a running Mendix runtime (read-only preview mode):
# basic query (reads .docker/.env for connection settings)
mxcli oql -p app.mpr "select Name, Email from MyModule.Customer"
# json output for piping to jq
mxcli oql -p app.mpr --json "SELECT count(c.ID) FROM MyModule.Order AS c" | jq '.[0]'
# Explicit connection (no project file needed)
mxcli oql --host localhost --port 8090 --token 'AdminPassword1!' "SELECT 1"
# Test a view entity query before embedding it
mxcli oql -p app.mpr "select datepart(YEAR, o.OrderDate) as Year, sum(o.Total) as Revenue from Sales.Order as o GROUP by datepart(YEAR, o.OrderDate)"The app must be running first: mxcli docker run -p app.mpr --wait
Troubleshooting: If you get "Action not found: preview_execute_oql", the Docker stack needs the
-Dmendix.live-preview=enabledJVM flag. Re-initialize with:mxcli docker init -p app.mpr --force, then restart withmxcli docker run -p app.mpr --wait.
mxcli oql -p app.mpr "select ..."mxcli check view.mdl -p app.mpr --references to catch type mismatches (e.g., long vs integer for count())mxcli exec view.mdl -p app.mpr && mxcli docker run -p app.mpr --fresh --waitThe MDL linter checks for common OQL issues:
Rule: consistency/oql-syntax
How to Fix Linter Errors:
# lint a file
mendix> lint file 'path/to/file.mdl';
# Common error: ORDER by without limit
# error: view entity X: ORDER by requires limit or OFFSET. Studio Pro error: CE0174
# Fix: add limit clausepackages/mendix-repl/docs/syntax-proposals/OQL_SYNTAX_GUIDE.mdpackages/mendix-repl/examples/VIEW_ENTITY_VALIDATION.mdWhen writing OQL queries for VIEW entities, always verify:
sum, avg, count, max, min)count(entity.ID) not count(*) (a view entity has no ID: count a non-null column)sum(1), count(1)) and no bare 1 as X column a consumer will add to — cast it (HSQLDB)datepart(YEAR, field)'HIGH' not 'High'in ('VAL1', 'VAL2') or in (select ...)amount : quantity!= not <>/ not .: alias/Module.Assoc not alias.Module.Assoc.ID: pr/Shop.Price_Product = p.ID not = pEntity_Assoc/Target as aliason a.Field = b.Fieldmxcli check script.mdl -p app.mpr --references to catch type mismatchesFollowing these rules ensures your OQL queries will parse and execute correctly in Mendix runtime.
© mendixlabs, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in .claude/skills/mendix/write-oql-queries of mendixlabs/mxcli.
Open the folder on GitHubat commit a924d11
Write Oql Queries next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Write Oql Queries this skillmendixlabs/mxcli | 128 | — | ~6.2k | Automated safety check: Notes | Apache-2.0 | |
| Ddd Aggregateruvnet/ruflo | 74k | — | ~774 | Automated safety check: Notes | MIT | |
| ClickHouse Query Performance Validationcomet-ml/opik | 22k | — | ~2.1k | Automated safety check: Pass | Apache-2.0 | |
| Frontend Query Mutationlangflow-ai/langflow | 156k | — | ~979 | Automated safety check: Pass | MIT | |
| Agentdb Queryruvnet/ruflo | 74k | — | ~947 | Automated safety check: Notes | MIT | |
| Container Queriesthedaviddias/Front-End-Checklist | 74k | — | ~526 | Automated safety check: Pass | MIT |
ruvnet/ruflo
Scaffold an aggregate root with entity, value objects, repository interface, domain events, and test stubs.
comet-ml/opik
Measures what a ClickHouse query change actually costs, turning a suspicion that a query is slow into numbers a reviewer can act on before it merges.
langflow-ai/langflow
Guide for implementing Langflow frontend query and mutation patterns with Axios and TanStack React Query v5.
ruvnet/ruflo
Query AgentDB through the controller bridge -- semantic routing, hierarchical recall, causal graphs, context synthesis, pattern store/search
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing stylesheets, component styles, and responsive behavior related to Use container queries for component-level responsiveness.
netdata/netdata
Query or explain direct Netdata Agent APIs and Functions; review direct-query recipes or helpers; troubleshoot bearer authentication.
mendixlabs/mxcli
Push OData query options into the SQL of a Mendix resource served by a read microflow, so $filter, $orderby, $top, $skip, $count and the key lookup reach the database instead of being silently…
mendixlabs/mxcli
Chart a Mendix app with Vega-Lite through a pluggable widget that takes the specification and the data as separate properties, so the model emits rows and never assembles a chart payload.
mendixlabs/mxcli
Author Mendix AI agent documents in MDL — Model, Knowledge Base, Consumed MCP Service and Agent, with variables, tools and multi-line prompts.
mendixlabs/mxcli
Run set-based INSERT, UPDATE and DELETE against Mendix entities through OQL statements, which the runtime supports and Studio Pro cannot author.
mendixlabs/mxcli
Stand up an HTTP endpoint you control instead of a live third-party API, and point the Mendix app at it — Prism from an OpenAPI contract, a constant swap, or a forward proxy.
mendixlabs/mxcli
Call external REST APIs from Mendix — the three approaches (inline REST CALL, consumed REST client document, generated from OpenAPI) and how to choose.
Write OQL for Mendix VIEW entities — joins, aggregates, calculated fields, and the syntax the runtime actually accepts. Write Oql Queries is an agent skill from mendixlabs/mxcli. Write OQL for Mendix VIEW entities — joins, aggregates, calculated fields, and the syntax the runtime actually accepts.
Write Oql Queries fits situations like: creating a VIEW entity; building a report; analytics query.
Run `npx skills add mendixlabs/mxcli --skill write-oql-queries -a claude-code`. Or copy the skill folder (.claude/skills/mendix/write-oql-queries in mendixlabs/mxcli) into .claude/skills/write-oql-queries in your project. Claude Code loads it when a task matches its description.
Run `npx skills add mendixlabs/mxcli --skill write-oql-queries -a codex`. Or copy the skill folder (.claude/skills/mendix/write-oql-queries in mendixlabs/mxcli) into .agents/skills/write-oql-queries in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add mendixlabs/mxcli --skill write-oql-queries -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/write-oql-queries, .gemini/skills/write-oql-queries, .github/skills/write-oql-queries and .opencode/skills/write-oql-queries in your project.
Going by SKILL.md and its folder, Write Oql Queries needs the command-line tools its instructions call (jq).
SKILL.md names 1 domain. As links in the text: docs.mendix.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Write Oql Queries is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.2k tokens (SKILL.md is roughly 25k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Write Oql Queries: Ddd Aggregate (ruvnet/ruflo, 74k stars), ClickHouse Query Performance Validation (comet-ml/opik, 22k stars), Frontend Query Mutation (langflow-ai/langflow, 156k stars) and Agentdb Query (ruvnet/ruflo, 74k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
mendixlabs (a GitHub organization) maintains it in mendixlabs/mxcli, which has 128 GitHub stars. The repository holds 75 skills in this directory. The repository was last updated on October 7, 2026.
Source: mendixlabs/mxcli on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.