diff --git a/node.js/app-services.md b/node.js/app-services.md index 35aab64c2..da2f5c6c3 100644 --- a/node.js/app-services.md +++ b/node.js/app-services.md @@ -164,20 +164,23 @@ When CAP's generic handlers run a CRUD operation, the result follows a consisten | Operation | Return value | |-----------------------|-----------------------------------------------------------------------------------------------| | `READ` | Array of matching records, or a single record / `null` when read by key | -| `INSERT` / `CREATE` | Array with `.affected` (rows written); iterate to access the inserted rows' primary keys | -| `UPDATE` / `UPSERT` | Array with `.affected` (rows changed); populated with rows from a `RETURNING` clause | -| `DELETE` | Array with `.affected` (rows deleted); populated with rows from a `RETURNING` clause | +| `CREATE` | Array with `.affected` (rows created); iterate to access the created rows' generated keys | +| `UPDATE` | Array with `.affected` (rows changed); reserved for rows from a `RETURNING` clause | +| `UPSERT` | Array with `.affected` (rows written); reserved for rows from a `RETURNING` clause | +| `DELETE` | Array with `.affected` (rows deleted); reserved for rows from a `RETURNING` clause | -For `INSERT`s, the result is a lazy array: iterating it (`[...result]`, `for…of`, `JSON.stringify`) materializes the generated primary keys of the inserted rows. Direct index access works after the first iteration. +For `CREATE`, the array will be populated with rows from an SQL `RETURNING` clause once that is supported. Until then, the result is a lazy array that computes the created rows' generated primary keys on demand: iterating it (`[...result]`, `for…of`, `JSON.stringify`) populates those keys, avoiding the cost when you don't need them. + +> [!warning] Iterate before indexing +> Direct index access (`result[0]`) returns `undefined` until the array has been iterated at least once. Spread or loop over the result first. ```js -const inserted = await srv.create(Books).entries({title:'Catweazle'}) -inserted.affected // 1 -const [row] = [...inserted] // materializes — row holds the generated key -inserted[0] // same row (materialized above) +const created = await srv.create(Books).entries({title:'Catweazle'}) +created.affected // 1 +const [row] = [...created] // iterate first — row holds the generated key ``` -For `UPDATE`, `UPSERT`, and `DELETE`, the array is reserved for rows returned by a SQL `RETURNING` clause. But `RETURNING` is not yet supported, so the array currently is always empty: +For `UPDATE`, `UPSERT`, and `DELETE`, the array is reserved for rows returned by an SQL `RETURNING` clause. Unlike `CREATE`, there are no generated keys to synthesize client-side, so — with `RETURNING` not yet supported — the array is currently always empty: ```js const updated = await srv.update(Books).set({discount:'10%'}).where({stock:{'>':111}})