@@ -10,6 +10,61 @@ This skill covers how to use OData services to share data between Mendix applica
1010- User asks about external entities, consumed/published OData services
1111- User wants to decouple modules or apps for independent deployment
1212- User asks about the view entity pattern for OData services
13+ - User asks about local metadata files or offline OData development
14+
15+ ## MetadataUrl Formats
16+
17+ ` CREATE ODATA CLIENT ` supports three formats for the ` MetadataUrl ` parameter:
18+
19+ | Format | Example | Stored In Model |
20+ | --------| ---------| -----------------|
21+ | ** HTTP(S) URL** | ` https://api.example.com/odata/v4/$metadata ` | Unchanged |
22+ | ** Absolute file:// URI** | ` file:///Users/team/contracts/service.xml ` | Unchanged |
23+ | ** Relative path** | ` ./metadata/service.xml ` or ` metadata/service.xml ` | ** Normalized to absolute ` file:// ` ** |
24+
25+ ** Path Normalization:**
26+ - Relative paths (with or without ` ./ ` ) are ** automatically converted** to absolute ` file:// ` URLs in the Mendix model
27+ - This ensures Studio Pro can properly detect local file vs HTTP metadata sources (radio button in UI)
28+ - Example: ` ./metadata/service.xml ` → ` file:///absolute/path/to/project/metadata/service.xml `
29+
30+ ** Path Resolution (before normalization):**
31+ - With project loaded (` -p ` flag or REPL): relative paths are resolved against the ` .mpr ` file's directory
32+ - Without project: relative paths are resolved against the current working directory
33+
34+ ** Use Cases for Local Metadata:**
35+ - ** Offline development** — no network access required
36+ - ** Testing and CI/CD** — reproducible builds with metadata snapshots
37+ - ** Version control** — commit metadata files alongside code
38+ - ** Pre-production** — test against upcoming API changes before deployment
39+ - ** Firewall-friendly** — works in locked-down corporate environments
40+
41+ ## ServiceUrl Must Be a Constant
42+
43+ ** IMPORTANT:** The ` ServiceUrl ` parameter ** must always be a constant reference** (prefixed with ` @ ` ). Direct URLs are not allowed.
44+
45+ ** Correct:**
46+ ``` sql
47+ CREATE CONSTANT ProductClient .ProductDataApiLocation
48+ TYPE String
49+ DEFAULT ' http://localhost:8080/odata/productdataapi/v1/' ;
50+
51+ CREATE ODATA CLIENT ProductClient .ProductDataApiClient (
52+ ODataVersion: OData4,
53+ MetadataUrl: ' https://api.example.com/$metadata' ,
54+ ServiceUrl: ' @ProductClient.ProductDataApiLocation' -- ✅ Constant reference
55+ );
56+ ```
57+
58+ ** Incorrect:**
59+ ``` sql
60+ CREATE ODATA CLIENT ProductClient .ProductDataApiClient (
61+ ODataVersion: OData4,
62+ MetadataUrl: ' https://api.example.com/$metadata' ,
63+ ServiceUrl: ' https://api.example.com/odata' -- ❌ Direct URL not allowed
64+ );
65+ ```
66+
67+ This enforces Mendix best practice of externalizing configuration values for different environments.
1368
1469## Architecture Overview
1570
@@ -234,6 +289,40 @@ create odata client ProductClient.ProductDataApiClient (
234289 HttpPassword: ' 1'
235290);
236291
292+ -- OData client with local file - relative path (offline development)
293+ -- Resolved relative to .mpr directory when project is loaded
294+ CREATE ODATA CLIENT ProductClient .ProductDataApiClient (
295+ ODataVersion: OData4,
296+ MetadataUrl: ' ./metadata/productdataapi.xml' ,
297+ Timeout: 300 ,
298+ ServiceUrl: ' @ProductClient.ProductDataApiLocation' ,
299+ UseAuthentication: Yes,
300+ HttpUsername: ' MxAdmin' ,
301+ HttpPassword: ' 1'
302+ );
303+
304+ -- OData client with local file - relative path without ./
305+ CREATE ODATA CLIENT ProductClient .ProductDataApiClient (
306+ ODataVersion: OData4,
307+ MetadataUrl: ' metadata/productdataapi.xml' ,
308+ Timeout: 300 ,
309+ ServiceUrl: ' @ProductClient.ProductDataApiLocation' ,
310+ UseAuthentication: Yes,
311+ HttpUsername: ' MxAdmin' ,
312+ HttpPassword: ' 1'
313+ );
314+
315+ -- OData client with local file - absolute file:// URI
316+ CREATE ODATA CLIENT ProductClient .ProductDataApiClient (
317+ ODataVersion: OData4,
318+ MetadataUrl: ' file:///Users/team/contracts/productdataapi.xml' ,
319+ Timeout: 300 ,
320+ ServiceUrl: ' @ProductClient.ProductDataApiLocation' ,
321+ UseAuthentication: Yes,
322+ HttpUsername: ' MxAdmin' ,
323+ HttpPassword: ' 1'
324+ );
325+
237326-- External entities (mapped from published service)
238327create external entity ProductClient .ProductsEE
239328from odata client ProductClient .ProductDataApiClient
@@ -460,13 +549,40 @@ authentication basic
460549
461550## Folder Organization
462551
463- Use the ` folder ` property to organize OData documents within modules:
552+ Use the ` Folder ` property to organize OData documents within modules.
553+
554+ ** MetadataUrl accepts three formats:**
555+ 1 . ** HTTP(S) URL** — fetches from remote service (production)
556+ 2 . ** file:///absolute/path** — reads from local absolute path
557+ 3 . ** ./path or path/file.xml** — reads from local relative path (resolved against .mpr directory)
464558
465559``` sql
560+ -- Format 1: HTTP(S) URL
466561create odata client ProductClient .ProductDataApiClient (
467562 ODataVersion: OData4,
468- MetadataUrl: ' http://localhost:8080/odata/productdataapi/v1/$metadata' ,
469- folder: ' Integration/ProductAPI'
563+ MetadataUrl: ' https://api.example.com/odata/v4/$metadata' ,
564+ Folder: ' Integration/ProductAPI'
565+ );
566+
567+ -- Format 2: Absolute file:// URI
568+ create odata client ProductClient .ProductDataApiClient (
569+ ODataVersion: OData4,
570+ MetadataUrl: ' file:///Users/team/contracts/productdataapi.xml' ,
571+ Folder: ' Integration/ProductAPI'
572+ );
573+
574+ -- Format 3a: Relative path with ./
575+ create odata client ProductClient .ProductDataApiClient (
576+ ODataVersion: OData4,
577+ MetadataUrl: ' ./metadata/productdataapi.xml' ,
578+ Folder: ' Integration/ProductAPI'
579+ );
580+
581+ -- Format 3b: Relative path without ./
582+ create odata client ProductClient .ProductDataApiClient (
583+ ODataVersion: OData4,
584+ MetadataUrl: ' metadata/productdataapi.xml' ,
585+ Folder: ' Integration/ProductAPI'
470586);
471587
472588create odata service ProductApi .ProductDataApi (
@@ -506,7 +622,11 @@ Before publishing:
506622
507623Before consuming:
508624- [ ] Location constant created for environment-specific URLs
509- - [ ] OData client points to ` $metadata ` URL and uses ` ServiceUrl: '@Module.Constant' `
625+ - [ ] OData client ` MetadataUrl ` points to either:
626+ - HTTP(S) URL: ` https://api.example.com/$metadata `
627+ - Local file (absolute): ` file:///path/to/metadata.xml `
628+ - Local file (relative): ` ./metadata/service.xml ` (resolved against ` .mpr ` directory)
629+ - [ ] OData client uses ` ServiceUrl: '@Module.Constant' ` for runtime endpoint
510630- [ ] External entities match the published exposed names and types
511631- [ ] Module role created and granted on external entities (READ, optionally CREATE/WRITE/DELETE)
512632
0 commit comments