API Runnables
- App Marché metadata defines the API route and its Request and Response contracts.
- The Nenu source is the executable body of an implicit typed function.
- Every successful path must return a value compatible with Response.
The Contract Comes From Metadata
An API runnable is an implicit function from a metadata-defined Request to a metadata-defined Response. The editor contains the function body only. Do not recreate either contract with type Request or type Response in source.
| App Marché metadata owns | Nenu source owns |
|---|---|
| Route and API node identity | Validation and control flow |
| Authentication setting | Entity reads and writes |
| Request fields and optional markers | Reads from request |
| Response fields and types | return Response { ... } |
Create An Expense
Assume Request metadata contains room_id, payer_id, title, amount, and optional note. Response metadata contains expense: Expense. Required entities are loaded first, then the new value is constructed, saved, and returned.
1need {2 room = Room(request.room_id)3 payer = User(request.payer_id)4}56if request.title.trim() == "" {7 throw Validation("Expense title is required")8}910if request.amount <= 0 {11 throw Validation("Expense amount must be positive")12}1314expense = Expense {15 room_id: room.id16 payer17 title: request.title.trim()18 amount: request.amount19 note: request.note20 status: ExpenseStatus.Open21}2223save(expense)24return Response { expense }Same-name shorthand keeps the return concise: return Response { expense } means the Response field named expense receives the local binding named expense. The metadata contract must actually contain that field.
Read, Update, And Delete Patterns
1need expenses = Expense[]({2 room_id == request.room_id3}) | sort(createdAt, desc) | limit(100)45return Response { expenses }- Choose a required, optional, array, map, or join-row Response field that matches the real result.
- Use
withwhen an optional Request field means “change this only when supplied.” - Return a useful success value or explicit acknowledgement that exists in Response metadata.
- A successful fetch or save does not excuse a missing or incompatible Response return.
Success Paths And Failure Paths
Every reachable successful path of a response-bearing API must return a compatible Response value. A throw ends a failing path, so that path does not also return a success value.
1need expense = Expense?(request.expense_id)23without expense {4 throw NotFound("Expense was not found")5}67return Response { expense }Calling The API From Page Script
1use api/expenses/create as CreateExpenseApi23fn createExpense() {4 created = CreateExpenseApi({5 room_id6 payer_id7 title8 amount9 note10 })1112 expenses = expenses + [created.expense]13}- Import the root-qualified
api/...route and give it an alias. - Call
Alias()when Request has no required fields. - Pass one map payload when Request expects fields.
- The result type comes from Response metadata; access only declared fields.
Common Mistakes
| Wrong | Correct |
|---|---|
api CreateExpense { ... } | Write the executable statements directly. |
runnable CreateExpense { ... } | The API node already supplies the runnable context. |
Source-authored type Request | Define Request fields in API metadata. |
respond { expense } | return Response { expense } |
| Return an undeclared field | Make the value match the exact Response contract. |
