rest-call-from-json · git:20260821.ff81a24 · 2026-08-21 · sha256 ed68eb3a1f8c8a1a

rest-call-from-json git:20260821.ff81a24A

Immutable. This exact content is served forever at /api/v1/blob/ed68eb3a1f8c8a1a.

---
name: rest-call-from-json
description: "Generate the whole integration stack from a JSON payload: JSON structure, non-persistent entities, import mapping, and the REST CALL microflow. Use when starting from an example response and needing everything between it and a working microflow."
---

# REST Call from JSON Payload — End-to-End Skill

Use this skill to generate the full stack of Mendix integration artifacts from a JSON payload:
JSON Structure → Non-persistent entities → Import Mapping → microflow.

> **Two approaches**: This skill uses the **inline REST CALL** approach (good for one-off calls
> and quick prototyping). For structured APIs with reusable operations, use the **REST Client**
> approach instead — see [rest-client](../rest-client/SKILL.md) for `create rest client` + `send rest request`
> + optional `transform` with JSLT data transformers.

## Overview — Four Steps

1. **CREATE JSON STRUCTURE** — store the raw payload and derive the element tree
2. **CREATE ENTITY** (non-persistent) — one per JSON object type, with attributes per JSON field
3. **CREATE IMPORT MAPPING** — link JSON structure elements to entities and attributes
4. **CREATE MICROFLOW** — inline REST CALL that invokes the import mapping (or use REST Client + SEND REST REQUEST)

---

## Step 1 — JSON Structure

```sql
create json structure Module.JSON_MyStructure
  snippet '{"key": "value", "count": 1}';
```

- The executor **formats** the snippet (pretty-print) then **refreshes** (derives element tree) automatically.
- The snippet must be valid JSON; use single quotes around it in MDL.
- Escape single quotes inside the snippet by doubling them: `''`.
- The derived element tree must stay consistent with the snippet — the executor sorts JSON object keys alphabetically to match `json.MarshalIndent` output.

**Verify** after creation:
```sql
describe json structure Module.JSON_MyStructure;
-- Should show: element tree under "-- Element tree:" comment
```

---

## Step 2 — Non-Persistent Entities

Derive one entity per JSON object type. Name them after what they represent (not after JSON keys).

```sql
create non-persistent entity Module.MyRootObject (
  stringField   : string,
  intField      : integer,
  decimalField  : decimal,
  boolField     : boolean default false
);

create non-persistent entity Module.MyNestedObject (
  name : string,
  code : string
);

create association Module.MyRootObject_MyNestedObject
  from Module.MyRootObject
  to Module.MyNestedObject;
```

**Rules:**
- All string fields: bare `string` (no length — unlimited)
- All number fields: `integer`, `decimal`, or `long` — remove defaults for optional fields
- Boolean fields **require** `default true|false`
- `non-persistent` — these entities are not stored in the database. The keyword is
  **hyphenated and goes before `entity`**: `create non-persistent entity Mod.X (...)`.
  `NON_PERSISTENT`, and a `(NON_PERSISTENT)` inside the body, are both parse errors
- One association per parent→child relationship; name it `Parent_Child`

---

## Step 3 — Import Mapping

> **Full reference**: See [json-structures-and-mappings](../json-structures-and-mappings/SKILL.md) for complete import/export mapping syntax, domain model patterns, and common mistakes.

```sql
create import mapping Module.IMM_MyMapping
  with json structure Module.JSON_MyStructure
{
  create Module.MyRootObject {
    stringField = stringField,
    intField    = intField,
    create Module.MyRootObject_MyNestedObject/Module.MyNestedObject = nestedKey {
      name = name,
      code = code
    }
  }
};
```

**Syntax rules:**
- Root object: `create Module.Entity { ... }` — always starts with handling keyword
- Value mappings: `attributename = jsonFieldName` — entity attribute on the left, JSON field on the right
- Nested objects: `create association/entity = jsonKey { ... }` — association path + JSON key
- Object handling: `create` (default), `find` (requires KEY), `find or create`
- KEY marker: `attr = jsonField key` — marks the attribute as a matching key
- Value transforms: `attr = Module.Microflow(jsonField)` — call a microflow to transform the value

**Verify** after creation — check Schema elements are ticked in Studio Pro:
- Open the import mapping in Studio Pro
- All JSON structure elements should appear ticked in the Schema elements panel
- If not ticked: JsonPath mismatch between import mapping and JSON structure elements

---

## Step 4 — REST CALL Microflow

Place the microflow in the `[pages]/Operations/` folder or `Private/` depending on whether it is public.

```sql
create microflow Module.GET_MyData ()
begin
  @position(-5, 200)
  declare $baseUrl string = 'https://api.example.com';
  @position(185, 200)
  declare $endpoint string = $baseUrl + '/path';
  @position(375, 200)
  $Result = rest call get '{1}' with ({1} = $endpoint)
    header 'Accept' = 'application/json'
    timeout 300
    returns mapping Module.IMM_MyMapping as Module.MyRootObject on error rollback;
  @position(565, 200)
  log info node 'Integration' 'Retrieved result' with ();
end;
/
```

**Key points:**
- `@position` annotations control the canvas layout — StartEvent is auto-placed 150px to the left of the first annotated activity
- The output variable name is **automatically derived** from the entity name in `as Module.MyEntity` — do NOT hardcode it on the left side; the executor overrides it
- Single vs list result is **automatically detected**: if the JSON structure's root element is an Object, the variable type is `ObjectType` (single); if Array, `ListType` (list)
- `on error rollback` — standard error handling for integration calls

**For list responses** (JSON root is an array):
```sql
  $Results = rest call get '{1}' with ({1} = $endpoint)
    header 'Accept' = 'application/json'
    timeout 300
    returns mapping Module.IMM_MyMapping as Module.MyItem on error rollback;
  @position(565, 200)
  $count = count($MyItem);
```

---

## Step 5 — Import/Export Mapping in Microflows (Optional)

Instead of using `returns mapping` on a REST CALL, you can use standalone import/export mapping actions. This is useful when you already have a JSON string and want to map it to entities, or when you want to serialize entities back to JSON.

### Import from mapping

Applies an import mapping to a string variable (JSON content) to produce entity objects:

```sql
-- With assignment (non-persistent entities, need the result in the flow)
$PetResponse = import from mapping Module.IMM_Pet($JsonContent);

-- Without assignment (persistent entities, just stores to DB)
import from mapping Module.IMM_Pet($JsonContent);
```

### Export to mapping

Applies an export mapping to an entity object to produce a JSON string:

```sql
$JsonOutput = export to mapping Module.EMM_Pet($PetResponse);
```

### Complete import → process → export microflow

```sql
create microflow Module.ProcessPetData ()
begin
  declare $ResponseContent string = $latestHttpResponse/content;
  $PetResponse = import from mapping Module.IMM_Pet($ResponseContent);
  -- Process the imported data...
  $JsonOutput = export to mapping Module.EMM_Pet($PetResponse);
  log info node 'Integration' 'Exported: ' + $JsonOutput;
end;
/
```

---

## Step 6 — Sending a Request Body (Optional)

Everything above receives data. To send it, an inline `REST CALL` takes one of
four body forms:

```sql
-- 1. String template with placeholders
body '{{"name": "{1}", "qty": {2}}' with ({1} = $Name, {2} = toString($Qty))

-- 2. An expression that already yields the payload
body $JsonPayload

-- 3. An export mapping (entity -> JSON)
body mapping Module.EMM_Item from $Item

-- 4. Raw bytes — a file document's CONTENTS member
body binary $Doc/Contents
```

### Uploading a file

The expression is the file document's `Contents` **member**, not the document,
and the content type goes on a header — the body clause carries only the bytes:

```sql
create or modify microflow Module.POST_Document_Upload (
  $Doc: Module.UploadedFile
)
returns boolean as $Ok
begin
  declare $Ok boolean = false;
  $Response = rest call post 'https://api.example.com/documents'
    header 'ContentType' = 'application/pdf'
    body binary $Doc/Contents
    timeout 300
    returns response;
  set $Ok = $Response/StatusCode = 200;
  return $Ok;
end;
/
```

`$Doc` must be a specialization of `System.FileDocument`. Downloading is the
mirror image — `returns Module.UploadedFile` stores the response body in a new
file document.

**A consumed REST CLIENT document cannot do this.** Its body is one of
`Rest$JsonBody`, `Rest$StringBody` or `Rest$ImplicitMappingBody` — all textual —
so `Body: file from $Doc` in a `create rest client` operation is refused as
**MDL-REST02**. Binary uploads belong in a microflow. (`Response: file as $Doc`
on an operation is fine; downloads work either way.)

---

## Complete Example — Bible Verse API

```sql
-- Step 1: JSON Structure
create json structure Integrations.JSON_BibleVerse
  snippet '{"translation":{"identifier":"web","name":"World English Bible","language":"English","language_code":"eng","license":"Public Domain"},"random_verse":{"book_id":"1SA","book":"1 Samuel","chapter":17,"verse":49,"text":"David put his hand in his bag, took a stone, and slung it."}}';

-- Step 2: Entities
create non-persistent entity Integrations.BibleApiResponse ();

create non-persistent entity Integrations.BibleTranslation (
  identifier    : string,
  name          : string,
  language      : string,
  language_code : string,
  license       : string
);

create non-persistent entity Integrations.BibleVerse (
  book_id : string,
  book    : string,
  chapter : integer,
  verse   : integer,
  text    : string
);

create association Integrations.BibleApiResponse_BibleTranslation
  from Integrations.BibleApiResponse
  to Integrations.BibleTranslation;

create association Integrations.BibleApiResponse_BibleVerse
  from Integrations.BibleApiResponse
  to Integrations.BibleVerse;

-- Step 3: Import Mapping
create import mapping Integrations.IMM_BibleVerse
  with json structure Integrations.JSON_BibleVerse
{
  create Integrations.BibleApiResponse {
    create Integrations.BibleApiResponse_BibleTranslation/Integrations.BibleTranslation = translation {
      identifier    = identifier,
      language      = language,
      language_code = language_code,
      license       = license,
      name          = name
    },
    create Integrations.BibleApiResponse_BibleVerse/Integrations.BibleVerse = random_verse {
      book    = book,
      book_id = book_id,
      chapter = chapter,
      text    = text,
      verse   = verse
    }
  }
};

-- Step 4: Microflow
create microflow Integrations.GET_BibleVerse_Random ()
begin
  @position(-5, 200)
  declare $baseUrl string = 'https://bible-api.com';
  @position(185, 200)
  declare $endpoint string = $baseUrl + '/data/web/random';
  @position(375, 200)
  $Result = rest call get '{1}' with ({1} = $endpoint)
    header 'Accept' = 'application/json'
    timeout 300
    returns mapping Integrations.IMM_BibleVerse as Integrations.BibleApiResponse on error rollback;
  @position(565, 200)
  log info node 'Integration' 'Retrieved Bible verse' with ();
end;
/
```

---

## Gotchas and Common Errors

| Symptom | Cause | Fix |
|---------|-------|-----|
| Studio Pro "not consistent with snippet" | JSON element tree keys not in alphabetical order | Executor sorts keys; re-derive from snippet |
| Schema elements not ticked in import mapping | JsonPath mismatch | Named object elements use `(object)\|key`, NOT `(object)\|key\|(object)` |
| Import mapping not linked in REST call | Wrong BSON field name | Use `ReturnValueMapping`, not `mapping` |
| Studio Pro shows "List of X" but mapping returns single X | `ForceSingleOccurrence` not set | Executor auto-detects from JSON structure root element type |
| StartEvent behind first activities | Default posX=200 vs @position(-5,...) | Fixed: executor pre-scans for first @position and shifts StartEvent left |
| `TypeCacheUnknownTypeException` | Wrong BSON `$type` names | `ImportMappings$ObjectMappingElement` / `ImportMappings$ValueMappingElement` (no `import` prefix) |
| Attribute not found in Studio Pro | Attribute not fully qualified | Must be `Module.Entity.AttributeName` in the BSON |
| `CE0117 "Error(s) in expression."` at the end event after a REST call | `returns response` binds a `System.HttpResponse`, so returning it from a `returns string` microflow is a type error | Match the microflow's return type to what you do with the response — e.g. `returns boolean` and `set $Ok = $Response/StatusCode = 200` |
| Upload returns HTTP 200 but the server received a few bytes | `Body: file from $Doc` on a REST CLIENT document used to be written as the literal text `$Doc` | Now refused as MDL-REST02 — upload from a microflow with `body binary $Doc/Contents` |

---

## Naming Conventions (MES)

| Artifact | Pattern | Example |
|----------|---------|---------|
| JSON Structure | `JSON_<ApiName>` | `JSON_BibleVerse` |
| Import Mapping | `IMM_<ApiName>` | `IMM_BibleVerse` |
| Root entity | Describes the API response | `BibleApiResponse` |
| Nested entities | Describes the domain concept | `BibleVerse`, `BibleTranslation` |
| Microflow | `METHOD_Resource_Operation` | `GET_BibleVerse_Random` |
| Folder | `Private/` for mappings/structures, `Operations/` for public microflows | — |