Skip to content

Conversation

abnegate
Copy link
Member

@abnegate abnegate commented Sep 12, 2025

This PR contains updates to the Web SDK for version 21.2.0.

Summary by CodeRabbit

  • New Features

    • Added transaction support for Databases and TablesDB (create/list/get/update/delete transactions, staged operations) and optional transactionId for CRUD and attribute operations.
    • Added email verification endpoints (create/update) via the Account API and new query helper for random ordering.
  • Documentation

    • Added and updated examples for transactions, TablesDB, Databases, and email verification.
  • Refactor

    • Introduced Transaction and TransactionList types and new execution enums; tightened some query parameter types.
  • Chores

    • Bumped package/CDN SDK version to 21.2.0 and updated changelog/README.

Copy link

coderabbitai bot commented Sep 12, 2025

Walkthrough

Adds transaction support for Databases and TablesDB: new methods listTransactions, createTransaction, getTransaction, updateTransaction, deleteTransaction, and createOperations. Extends many CRUD and attribute methods to accept an optional transactionId and propagate it in requests. Introduces Models.Transaction and Models.TransactionList types. Narrows Query.equal/notEqual parameter types and adds Query.orderRandom. Adds ExecutionTrigger and ExecutionStatus enums and updates Execution types. Bumps SDK version to 21.2.0 and updates CDN/readme and x-sdk-version headers. Multiple documentation examples added/updated to show transactionId usage.

Possibly related PRs

  • feat: Web SDK update for version 21.0.0 #140 — Touches the same files and symbols (src/client.ts version header, src/enums/execution-trigger.ts, src/enums/execution-status.ts, and updates to src/models.ts Execution types), indicating a strong code-level overlap.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely indicates the version bump of the Web SDK to 21.2.0, which is a real and important aspect of the changeset, though it does not explicitly mention the new transaction features introduced.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-txn

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/models.ts (1)

1310-1316: Fix misdocumented field in Subscriber.

userId is documented as “Topic ID.” It should be “User ID.”

-        /**
-         * Topic ID.
-         */
-        userId: string;
+        /**
+         * User ID.
+         */
+        userId: string;
🧹 Nitpick comments (12)
docs/examples/databases/list-transactions.md (2)

1-1: Show a concrete queries usage for clarity.
Using Query helpers improves discoverability.

-import { Client, Databases } from "appwrite";
+import { Client, Databases, Query } from "appwrite";
@@
-const result = await databases.listTransactions({
-    queries: [] // optional
-});
+const result = await databases.listTransactions({
+    queries: [Query.limit(10)] // optional
+});

Also applies to: 9-11


9-13: Top‑level await caveat (nit).
Some setups lack top‑level await; consider wrapping in an async IIFE in docs.

docs/examples/tablesdb/update-row.md (1)

9-16: Top-level await requires ESM/modules.

If users paste this in non-module contexts, it will throw. Consider noting that the snippet must run in a JS module or wrap it in an async IIFE.

Example:

(async () => {
  const result = await tablesDB.updateRow({ /* ... */ });
  console.log(result);
})();
docs/examples/tablesdb/create-transaction.md (1)

9-14: Top-level await note (applies across new examples).

Add a brief note that examples require module context (or wrap in an async IIFE) to prevent runtime confusion.

docs/examples/tablesdb/list-transactions.md (1)

9-11: Show minimal call without empty queries.

Since queries is optional, consider omitting it to reduce noise.

Apply this diff:

-const result = await tablesDB.listTransactions({
-    queries: [] // optional
-});
+const result = await tablesDB.listTransactions();
docs/examples/databases/create-transaction.md (2)

9-11: Clarify top‑level await requirement.

These examples use top‑level await. Please add a brief comment indicating that the snippet must run in an ES module/bundler environment that supports top‑level await (or wrap in an async IIFE).

-const result = await databases.createTransaction({
+// Note: Requires ESM / top-level await support (or wrap in an async IIFE).
+const result = await databases.createTransaction({
     ttl: 60 // optional
 });

10-10: Specify TTL units.

Add units to prevent misuse (seconds vs. milliseconds).

-    ttl: 60 // optional
+    ttl: 60 // optional, seconds
docs/examples/databases/delete-transaction.md (1)

9-13: Avoid logging an undefined/empty result for delete.

Delete endpoints often return void/Success. Prefer awaiting the call and logging a success message; verify the actual return type in this SDK version.

-const result = await databases.deleteTransaction({
-    transactionId: '<TRANSACTION_ID>'
-});
-
-console.log(result);
+await databases.deleteTransaction({
+    transactionId: '<TRANSACTION_ID>'
+});
+console.log('Transaction deleted');

Would you confirm what databases.deleteTransaction returns in 20.1.0-rc.1?

docs/examples/tablesdb/delete-transaction.md (1)

9-13: Align delete example with expected return type.

Same concern as Databases: avoid capturing/printing a possibly undefined result; log a success message after awaiting.

-const result = await tablesDB.deleteTransaction({
-    transactionId: '<TRANSACTION_ID>'
-});
-
-console.log(result);
+await tablesDB.deleteTransaction({
+    transactionId: '<TRANSACTION_ID>'
+});
+console.log('Transaction deleted');

Also, please confirm the method’s return type for TablesDB in 20.1.0-rc.1.

docs/examples/tablesdb/create-operations.md (1)

9-21: Trim optional fields from the example and clarify operations/rowId

createOperations is declared with operations?: object[] (src/services/tables-db.ts) and the client only adds 'operations' to the payload if provided; it also does not validate per-operation rowId — remove the example's rowId (or comment it as optional) in docs/examples/tablesdb/create-operations.md and keep the operations note marked optional.

docs/examples/tablesdb/get-transaction.md (1)

9-11: Clarify top‑level await requirement (ESM).

Same as other examples: add a short note about ESM/top‑level await.

-const result = await tablesDB.getTransaction({
+// Note: Requires ESM / top-level await support (or wrap in an async IIFE).
+const result = await tablesDB.getTransaction({
     transactionId: '<TRANSACTION_ID>'
 });
src/models.ts (1)

1255-1284: Narrow Transaction.status to a string-literal union.

Gives stronger type-safety and IDE help.

 export type Transaction = {
@@
-        status: string;
+        status: 'pending' | 'committing' | 'committed' | 'rolled_back' | 'failed';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a5d716 and cc559f0.

📒 Files selected for processing (35)
  • README.md (1 hunks)
  • docs/examples/databases/create-document.md (1 hunks)
  • docs/examples/databases/create-operations.md (1 hunks)
  • docs/examples/databases/create-transaction.md (1 hunks)
  • docs/examples/databases/decrement-document-attribute.md (1 hunks)
  • docs/examples/databases/delete-document.md (1 hunks)
  • docs/examples/databases/delete-transaction.md (1 hunks)
  • docs/examples/databases/get-document.md (1 hunks)
  • docs/examples/databases/get-transaction.md (1 hunks)
  • docs/examples/databases/increment-document-attribute.md (1 hunks)
  • docs/examples/databases/list-documents.md (1 hunks)
  • docs/examples/databases/list-transactions.md (1 hunks)
  • docs/examples/databases/update-document.md (1 hunks)
  • docs/examples/databases/update-transaction.md (1 hunks)
  • docs/examples/databases/upsert-document.md (1 hunks)
  • docs/examples/tablesdb/create-operations.md (1 hunks)
  • docs/examples/tablesdb/create-row.md (1 hunks)
  • docs/examples/tablesdb/create-transaction.md (1 hunks)
  • docs/examples/tablesdb/decrement-row-column.md (1 hunks)
  • docs/examples/tablesdb/delete-row.md (1 hunks)
  • docs/examples/tablesdb/delete-transaction.md (1 hunks)
  • docs/examples/tablesdb/get-row.md (1 hunks)
  • docs/examples/tablesdb/get-transaction.md (1 hunks)
  • docs/examples/tablesdb/increment-row-column.md (1 hunks)
  • docs/examples/tablesdb/list-rows.md (1 hunks)
  • docs/examples/tablesdb/list-transactions.md (1 hunks)
  • docs/examples/tablesdb/update-row.md (1 hunks)
  • docs/examples/tablesdb/update-transaction.md (1 hunks)
  • docs/examples/tablesdb/upsert-row.md (1 hunks)
  • package.json (1 hunks)
  • src/client.ts (1 hunks)
  • src/models.ts (2 hunks)
  • src/query.ts (1 hunks)
  • src/services/databases.ts (26 hunks)
  • src/services/tables-db.ts (26 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/services/databases.ts (3)
src/client.ts (3)
  • Models (803-803)
  • Payload (803-803)
  • AppwriteException (801-801)
src/index.ts (3)
  • Models (19-19)
  • Payload (19-19)
  • AppwriteException (8-8)
src/models.ts (5)
  • TransactionList (221-230)
  • Transaction (1258-1283)
  • Document (274-303)
  • DefaultDocument (305-308)
  • DocumentList (25-34)
src/services/tables-db.ts (1)
src/models.ts (5)
  • TransactionList (221-230)
  • Transaction (1258-1283)
  • Row (235-264)
  • DefaultRow (266-269)
  • RowList (11-20)
🔇 Additional comments (22)
README.md (1)

36-36: CDN snippet version bump is consistent—LGTM.
Matches package.json and x-sdk header updates in src/client.ts.

src/client.ts (1)

319-321: x-sdk-version updated to 20.1.0-rc.1 — verified.
package.json:5, README.md:36, src/client.ts:319 all use 20.1.0-rc.1; no plain 20.1.0 or 20.0.0 occurrences found.

package.json (1)

5-5: Version bump looks good.
Aligned with README and client header.

src/query.ts (1)

55-59: Type narrowing for equal/notEqual is correct.
QueryTypes already unions array types via QueryTypesList, so removing “| any[]” is a no-op and simplifies the API surface.

Also applies to: 65-69

docs/examples/tablesdb/get-row.md (1)

13-15: LGTM.
Optional transactionId addition reads well.

docs/examples/tablesdb/create-transaction.md (1)

10-11: Clarify TTL units and bounds.

Specify whether ttl is in seconds and acceptable range (e.g., 1–600) to avoid ambiguity.

Do you want me to update the comment once you confirm units/range?

docs/examples/tablesdb/update-transaction.md (1)

11-12: Document commit vs. rollback behavior.

Clarify whether these flags are mutually exclusive and precedence if both are true.

Once confirmed, I can add an inline comment note to the example.

docs/examples/tablesdb/decrement-row-column.md (1)

15-16: LGTM — transactionId addition reads well.

No issues spotted with the new fields.

docs/examples/databases/increment-document-attribute.md (1)

15-16: LGTM — transactionId addition aligns with API surface.

No issues with the new parameters.

docs/examples/databases/update-transaction.md (1)

9-13: Show a decisive action: commit or rollback (avoid both false).

commit: false + rollback: false is a no‑op and will confuse readers — demonstrate either commit: true or rollback: true (do not set both). Please confirm whether commit and rollback are mutually exclusive in version 20.1.0-rc.1.

docs/examples/databases/list-documents.md (1)

12-13: LGTM: transactionId extension looks correct.

The added transactionId parameter and queries formatting align with the described API changes.

docs/examples/databases/get-transaction.md (1)

1-14: LGTM: clear, minimal getTransaction example.

Reads well and matches the new API surface.

docs/examples/tablesdb/list-rows.md (1)

12-13: LGTM: listRows now documents optional transactionId.

Matches the broader transaction-enabled surface.

docs/examples/tablesdb/delete-row.md (1)

12-13: LGTM: optional transactionId on deleteRow is documented properly.

docs/examples/databases/delete-document.md (1)

12-13: LGTM: deleteDocument shows optional transactionId correctly.

src/models.ts (2)

218-231: TransactionList type: LGTM.

Structure and naming align with existing list models.


1255-1284: No duplicate Transaction type remains.

Search found a single exported declaration at src/models.ts:1258 (export type Transaction = { ... }).

src/services/databases.ts (3)

13-62: Transactions API surface: LGTM.

Overloads, paths, headers, and payload wiring look consistent.

Also applies to: 64-115, 116-166, 234-285, 286-345


357-418: transactionId propagation across document APIs: LGTM.

Optional transactionId is correctly threaded and conditionally added to payloads.

Also applies to: 433-513, 527-594, 609-686, 701-775, 788-850, 866-946, 962-1042


64-115: Confirm TTL constraints for createTransaction.

Docs/examples (docs/examples/databases/create-transaction.md) show ttl is optional (example uses 60) but no explicit min/max in repo — if the API requires a positive integer or has a maximum, add client-side validation (Number.isInteger(ttl) && ttl > 0 and enforce server max) in src/services/databases.ts (lines 64–115).

src/services/tables-db.ts (2)

13-62: Transactions API surface (TablesDB): LGTM.

Parsers, paths, and payloads align with Databases service.

Also applies to: 64-115, 116-166, 234-285, 286-345


356-417: transactionId propagation across row APIs: LGTM.

Payload handling and overloads look consistent.

Also applies to: 431-511, 524-591, 605-679, 693-767, 779-841, 856-936, 951-1031

Comment on lines +20 to +21
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid string quoting in permissions example.

Nested double quotes break the string. Use single quotes outside.

Apply this diff:

-    permissions: ["read("any")"], // optional
+    permissions: ['read("any")'], // optional
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
permissions: ['read("any")'], // optional
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/databases/create-document.md around lines 20 to 21, the
permissions example uses nested double quotes ("read("any")") which breaks the
string; change the outer quotes to single quotes so the line reads permissions:
['read("any")'] (leave transactionId as-is) to fix the invalid string quoting.

"name": "Walter O'Brien"
}
}
] // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

operations is required; drop the “optional” comment.

Marking the operations array as optional is inaccurate for this endpoint.

Apply this diff:

-    ] // optional
+    ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
] // optional
]
🤖 Prompt for AI Agents
In docs/examples/databases/create-operations.md around line 21, the operations
array is incorrectly annotated as optional; remove the "optional" comment and
any wording suggesting it's optional so the documentation clearly states that
the operations array is required for this endpoint. Ensure the line now reflects
that operations must be provided (e.g., delete the " // optional" suffix and
update surrounding text if necessary to avoid implying optionality).

Comment on lines 14 to +16
value: null, // optional
min: null // optional
min: null, // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use concrete numbers or omit fields instead of null for numeric options.

As above, null can be invalid for numeric parameters. Provide a typical value or remove the field.

-    value: null, // optional
-    min: null, // optional
+    value: 1, // optional; omit to use default decrement
+    // min: 0, // optional; omit if not bounding
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
value: null, // optional
min: null // optional
min: null, // optional
transactionId: '<TRANSACTION_ID>' // optional
value: 1, // optional; omit to use default decrement
// min: 0, // optional; omit if not bounding
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/databases/decrement-document-attribute.md around lines 14 to
16, the example sets numeric options to null which can be invalid; replace null
with concrete numeric examples (e.g., a decrement value and a minimum like 0) or
remove the optional numeric fields entirely, and for transactionId either omit
it or show a realistic placeholder string if needed for the example.

Comment on lines +13 to +14
queries: [], // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unsupported queries from getDocument example.

getDocument doesn’t accept a queries array; including it is misleading documentation.

Apply this diff:

-    queries: [], // optional
     transactionId: '<TRANSACTION_ID>' // optional
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
queries: [], // optional
transactionId: '<TRANSACTION_ID>' // optional
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/databases/get-document.md around lines 13 to 14, the example
includes an unsupported queries: [] parameter for getDocument; remove the
queries line entirely and leave only supported options (e.g., transactionId if
needed) so the example reflects the actual API signature.

Comment on lines +14 to +15
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid string quoting in permissions example.

Nested double quotes break the string. Use single quotes outside.

Apply this diff:

-    permissions: ["read("any")"], // optional
+    permissions: ['read("any")'], // optional
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
permissions: ['read("any")'], // optional
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/databases/update-document.md around lines 14-15, the
permissions example uses double quotes outside and also around the inner
argument (read("any")), which breaks the string; change the permissions array
entry to use single quotes outside so it reads permissions: ['read("any")'] and
keep the transactionId line as-is.

Comment on lines +14 to +15
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid string quoting in permissions example.

Nested double quotes break the string. Use single quotes outside.

Apply this diff:

-    permissions: ["read("any")"], // optional
+    permissions: ['read("any")'], // optional
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
permissions: ['read("any")'], // optional
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/tablesdb/update-row.md around lines 14 to 15, the permissions
example uses nested double quotes which break the string; change the outer
quotes to single quotes so the inner double quotes remain valid (e.g.,
permissions: ['read("any")']), ensuring the string is properly quoted and
remains a valid literal.

Comment on lines +14 to +15
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid permissions syntax; use Permission/Role helpers.

["read("any")"] is syntactically invalid (nested quotes) and likely not accepted by the SDK. Use the helpers instead.

Apply within the changed lines:

-    permissions: ["read("any")"], // optional
+    permissions: [Permission.read(Role.any())], // optional

Also add the necessary imports at the top of this file (outside the changed range):

-import { Client, TablesDB } from "appwrite";
+import { Client, TablesDB, Permission, Role } from "appwrite";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
import { Client, TablesDB, Permission, Role } from "appwrite";
Suggested change
permissions: ["read("any")"], // optional
transactionId: '<TRANSACTION_ID>' // optional
permissions: [Permission.read(Role.any())], // optional
transactionId: '<TRANSACTION_ID>' // optional
🤖 Prompt for AI Agents
In docs/examples/tablesdb/upsert-row.md around lines 14 to 15, the permissions
value uses invalid string syntax ["read("any")"]; replace it with the SDK
permission/role helper (e.g., Permission.read("any") or Role.read("any") per
your SDK) and pass that helper result (or an array of helpers) as the
permissions field, and also add the corresponding import statement(s) at the top
of the file (outside the changed range) for Permission/Role so the example
compiles.

Comment on lines +167 to +233
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} params.transactionId - Transaction ID.
* @param {boolean} params.commit - Commit transaction?
* @param {boolean} params.rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
*/
updateTransaction(params: { transactionId: string, commit?: boolean, rollback?: boolean }): Promise<Models.Transaction>;
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} transactionId - Transaction ID.
* @param {boolean} commit - Commit transaction?
* @param {boolean} rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
* @deprecated Use the object parameter style method for a better developer experience.
*/
updateTransaction(transactionId: string, commit?: boolean, rollback?: boolean): Promise<Models.Transaction>;
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };

if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}

const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;

if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}

const apiPath = '/databases/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);

const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}

return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enforce commit XOR rollback in updateTransaction.

Prevent ambiguous client calls by requiring exactly one of commit/rollback.

         const transactionId = params.transactionId;
         const commit = params.commit;
         const rollback = params.rollback;

+        // Exactly one of commit or rollback must be true
+        if ((commit ? 1 : 0) + (rollback ? 1 : 0) !== 1) {
+            throw new AppwriteException('Provide exactly one of "commit" or "rollback".');
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} params.transactionId - Transaction ID.
* @param {boolean} params.commit - Commit transaction?
* @param {boolean} params.rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
*/
updateTransaction(params: { transactionId: string, commit?: boolean, rollback?: boolean }): Promise<Models.Transaction>;
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} transactionId - Transaction ID.
* @param {boolean} commit - Commit transaction?
* @param {boolean} rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
* @deprecated Use the object parameter style method for a better developer experience.
*/
updateTransaction(transactionId: string, commit?: boolean, rollback?: boolean): Promise<Models.Transaction>;
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };
if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}
const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;
if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}
const apiPath = '/databases/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);
const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}
return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} params.transactionId - Transaction ID.
* @param {boolean} params.commit - Commit transaction?
* @param {boolean} params.rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
*/
updateTransaction(params: { transactionId: string, commit?: boolean, rollback?: boolean }): Promise<Models.Transaction>;
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} transactionId - Transaction ID.
* @param {boolean} commit - Commit transaction?
* @param {boolean} rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
* @deprecated Use the object parameter style method for a better developer experience.
*/
updateTransaction(transactionId: string, commit?: boolean, rollback?: boolean): Promise<Models.Transaction>;
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };
if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}
const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;
// Exactly one of commit or rollback must be true
if ((commit ? 1 : 0) + (rollback ? 1 : 0) !== 1) {
throw new AppwriteException('Provide exactly one of "commit" or "rollback".');
}
if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}
const apiPath = '/databases/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);
const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}
return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}
🤖 Prompt for AI Agents
In src/services/databases.ts around lines 167 to 233, the updateTransaction
implementation must enforce that exactly one of commit or rollback is provided
(XOR) to avoid ambiguous calls; add a validation after extracting commit and
rollback that checks (Boolean(commit) === Boolean(rollback)) and if true throw
new AppwriteException('Exactly one of "commit" or "rollback" must be
specified'); keep the existing payload building but only proceed when validation
passes so callers must supply exactly one of the two flags.

Comment on lines +167 to +233
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} params.transactionId - Transaction ID.
* @param {boolean} params.commit - Commit transaction?
* @param {boolean} params.rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
*/
updateTransaction(params: { transactionId: string, commit?: boolean, rollback?: boolean }): Promise<Models.Transaction>;
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} transactionId - Transaction ID.
* @param {boolean} commit - Commit transaction?
* @param {boolean} rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
* @deprecated Use the object parameter style method for a better developer experience.
*/
updateTransaction(transactionId: string, commit?: boolean, rollback?: boolean): Promise<Models.Transaction>;
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };

if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}

const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;

if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}

const apiPath = '/tablesdb/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);

const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}

return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enforce commit XOR rollback in updateTransaction.

Mirror the Databases guard here.

         const transactionId = params.transactionId;
         const commit = params.commit;
         const rollback = params.rollback;

+        // Exactly one of commit or rollback must be true
+        if ((commit ? 1 : 0) + (rollback ? 1 : 0) !== 1) {
+            throw new AppwriteException('Provide exactly one of "commit" or "rollback".');
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} params.transactionId - Transaction ID.
* @param {boolean} params.commit - Commit transaction?
* @param {boolean} params.rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
*/
updateTransaction(params: { transactionId: string, commit?: boolean, rollback?: boolean }): Promise<Models.Transaction>;
/**
* Update a transaction, to either commit or roll back its operations.
*
* @param {string} transactionId - Transaction ID.
* @param {boolean} commit - Commit transaction?
* @param {boolean} rollback - Rollback transaction?
* @throws {AppwriteException}
* @returns {Promise<Models.Transaction>}
* @deprecated Use the object parameter style method for a better developer experience.
*/
updateTransaction(transactionId: string, commit?: boolean, rollback?: boolean): Promise<Models.Transaction>;
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };
if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}
const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;
if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}
const apiPath = '/tablesdb/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);
const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}
return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}
updateTransaction(
paramsOrFirst: { transactionId: string, commit?: boolean, rollback?: boolean } | string,
...rest: [(boolean)?, (boolean)?]
): Promise<Models.Transaction> {
let params: { transactionId: string, commit?: boolean, rollback?: boolean };
if ((paramsOrFirst && typeof paramsOrFirst === 'object' && !Array.isArray(paramsOrFirst))) {
params = (paramsOrFirst || {}) as { transactionId: string, commit?: boolean, rollback?: boolean };
} else {
params = {
transactionId: paramsOrFirst as string,
commit: rest[0] as boolean,
rollback: rest[1] as boolean
};
}
const transactionId = params.transactionId;
const commit = params.commit;
const rollback = params.rollback;
// Exactly one of commit or rollback must be true
if ((commit ? 1 : 0) + (rollback ? 1 : 0) !== 1) {
throw new AppwriteException('Provide exactly one of "commit" or "rollback".');
}
if (typeof transactionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "transactionId"');
}
const apiPath = '/tablesdb/transactions/{transactionId}'.replace('{transactionId}', transactionId);
const payload: Payload = {};
if (typeof commit !== 'undefined') {
payload['commit'] = commit;
}
if (typeof rollback !== 'undefined') {
payload['rollback'] = rollback;
}
const uri = new URL(this.client.config.endpoint + apiPath);
const apiHeaders: { [header: string]: string } = {
'content-type': 'application/json',
}
return this.client.call(
'patch',
uri,
apiHeaders,
payload
);
}
🤖 Prompt for AI Agents
In src/services/tables-db.ts around lines 167 to 233, the updateTransaction
method must enforce that exactly one of commit or rollback is provided (XOR)
like the Databases guard; add validation after extracting commit and rollback
that throws an AppwriteException when both are undefined or both are defined
(i.e., if (! (typeof commit !== 'undefined' ^ typeof rollback !== 'undefined'))
throw new AppwriteException('Either "commit" or "rollback" must be provided, but
not both'); ensuring the rest of the payload building remains unchanged.

@abnegate abnegate changed the title feat: Web SDK update for version 20.1.0-rc.1 feat: Web SDK update for version 21.2.0 Oct 9, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/services/tables-db.ts (1)

167-233: Enforce commit XOR rollback validation.

This is a duplicate of a previous review comment. The method currently accepts both commit and rollback as optional parameters without validating that exactly one must be provided. This allows invalid scenarios where both are specified or neither is specified, which could lead to ambiguous transaction states or API errors.

Apply this diff to enforce the XOR constraint:

         const transactionId = params.transactionId;
         const commit = params.commit;
         const rollback = params.rollback;

         if (typeof transactionId === 'undefined') {
             throw new AppwriteException('Missing required parameter: "transactionId"');
         }
+
+        // Exactly one of commit or rollback must be true
+        if ((commit ? 1 : 0) + (rollback ? 1 : 0) !== 1) {
+            throw new AppwriteException('Provide exactly one of "commit" or "rollback".');
+        }
🧹 Nitpick comments (4)
docs/examples/account/update-email-verification.md (1)

3-14: Wrap top‑level await for broader compatibility

Some environments don’t support top‑level await. Wrap in an async IIFE and add basic error handling.

-const client = new Client()
-    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
-    .setProject('<YOUR_PROJECT_ID>'); // Your project ID
-
-const account = new Account(client);
-
-const result = await account.updateEmailVerification({
-    userId: '<USER_ID>',
-    secret: '<SECRET>'
-});
-
-console.log(result);
+;(async () => {
+  const client = new Client()
+    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
+    .setProject('<YOUR_PROJECT_ID>') // Your project ID
+
+  const account = new Account(client)
+
+  const result = await account.updateEmailVerification({
+    userId: '<USER_ID>',
+    secret: '<SECRET>',
+  })
+
+  console.log(result)
+})().catch(console.error)
docs/examples/account/create-email-verification.md (1)

3-13: Wrap top‑level await and add basic error handling

Use an async IIFE for wider runtime compatibility.

-const client = new Client()
-    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
-    .setProject('<YOUR_PROJECT_ID>'); // Your project ID
-
-const account = new Account(client);
-
-const result = await account.createEmailVerification({
-    url: 'https://example.com'
-});
-
-console.log(result);
+;(async () => {
+  const client = new Client()
+    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
+    .setProject('<YOUR_PROJECT_ID>') // Your project ID
+
+  const account = new Account(client)
+
+  const result = await account.createEmailVerification({
+    url: 'https://example.com',
+  })
+
+  console.log(result)
+})().catch(console.error)
src/models.ts (1)

1258-1286: Narrow Transaction.status type

Expose exact states to catch typos at compile time.

-        /**
-         * Current status of the transaction. One of: pending, committing, committed, rolled_back, failed.
-         */
-        status: string;
+        /**
+         * Current status of the transaction.
+         */
+        status: 'pending' | 'committing' | 'committed' | 'rolled_back' | 'failed';
src/services/account.ts (1)

2735-2784: Email/phone verification updates LGTM; consider endpoint constants

Overloads, validation, and new paths look correct. Optional: extract shared constants to reduce duplication/typo risk:

  • EMAIL_VERIFICATION_PATH = '/account/verifications/email'
  • PHONE_VERIFICATION_PATH = '/account/verifications/phone'

Then reuse across create/update methods.

Also applies to: 2786-2797, 2829-2829, 2856-2912, 2914-2923, 2959-2959, 2989-2989, 3049-3049

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bccadf4 and f484717.

📒 Files selected for processing (11)
  • CHANGELOG.md (1 hunks)
  • README.md (1 hunks)
  • docs/examples/account/create-email-verification.md (1 hunks)
  • docs/examples/account/update-email-verification.md (1 hunks)
  • package.json (1 hunks)
  • src/client.ts (1 hunks)
  • src/enums/execution-status.ts (1 hunks)
  • src/enums/execution-trigger.ts (1 hunks)
  • src/models.ts (5 hunks)
  • src/services/account.ts (6 hunks)
  • src/services/tables-db.ts (24 hunks)
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/client.ts
  • README.md
🧰 Additional context used
🧬 Code graph analysis (2)
src/services/account.ts (1)
src/models.ts (1)
  • Token (777-802)
src/services/tables-db.ts (1)
src/models.ts (5)
  • TransactionList (224-233)
  • Transaction (1261-1286)
  • Row (238-267)
  • DefaultRow (269-272)
  • RowList (14-23)
🔇 Additional comments (12)
src/enums/execution-trigger.ts (1)

1-5: Enum addition LGTM

Values and casing are consistent with usage in Models.Execution.

src/enums/execution-status.ts (1)

1-6: Enum addition LGTM

Names/values align with execution lifecycle and updated model typing.

src/models.ts (4)

1-2: Imports LGTM

Enums are correctly imported for typed Execution fields.


221-233: TransactionList shape LGTM

Consistent with other *List types (total + items array).


1031-1031: Typed trigger LGTM

Switch to ExecutionTrigger improves safety and DX.


1035-1035: Typed status LGTM

Using ExecutionStatus narrows the domain and prevents invalid strings.

src/services/tables-db.ts (6)

13-62: LGTM!

The listTransactions method is well-implemented with proper overloading for both object-style and deprecated parameter-style calling conventions. The payload construction and routing are correct.


64-114: LGTM!

The createTransaction method correctly implements transaction creation with an optional TTL parameter. The method signature and payload handling are appropriate.


116-165: LGTM!

The getTransaction method properly validates the required transactionId parameter and routes to the correct endpoint.


234-284: LGTM!

The deleteTransaction method correctly implements transaction deletion with proper parameter validation.


286-344: LGTM!

The createOperations method is correctly implemented for creating multiple operations within a transaction. The required parameter validation and payload construction are appropriate.


346-417: LGTM! Consistent transactionId integration across row operations.

All row operation methods (listRows, createRow, getRow, upsertRow, updateRow, deleteRow, decrementRowColumn, incrementRowColumn) have been correctly extended to accept an optional transactionId parameter. The implementation is consistent across all methods:

  • Proper overloading for both object-style and deprecated parameter-style signatures
  • Correct parameter extraction and type definitions
  • Consistent payload construction with conditional transactionId inclusion
  • Existing validation and error handling preserved

Also applies to: 419-511, 513-591, 593-679, 681-767, 769-841, 843-936, 938-1031

requestPath: string;
/**
* HTTP response headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
* HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Doc wording mismatches type (array vs object)

Comment says “key‑value object” but the type is Headers[]. Align the wording.

-         * HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
+         * HTTP request headers as an array of name/value pairs. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* HTTP request headers as a key-value object. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
* HTTP request headers as an array of name/value pairs. This will return only whitelisted headers. All headers are returned if execution is created as synchronous.
🤖 Prompt for AI Agents
In src/models.ts around line 1045, the doc comment incorrectly calls the value a
"key-value object" while the type is Headers[]; update the comment wording to
say it returns an array of header objects (Headers[]) — for example: "HTTP
request headers as an array of header objects (Headers[]). This will return only
whitelisted headers; all headers are returned if execution is created as
synchronous." Ensure the updated text matches the exact shape and semantics of
Headers[].

@abnegate abnegate closed this Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant