diff --git a/.optimize-cache.json b/.optimize-cache.json index fa2881382e..367d49bcdc 100644 --- a/.optimize-cache.json +++ b/.optimize-cache.json @@ -254,11 +254,11 @@ "images/blog/appwrite-pricing-update/one-subscription.png": "70f5e8590a9fa96afb90ec24a20bd94fed8acecd07c2c342e08e0215a7c15773", "images/blog/appwrite-pricing-update/project.png": "862597aa4be7cb9f4dedebc924432882708ff227ae7c2f73d74c1ef60f60049d", "images/blog/appwrite-query-api/cover.png": "487c4f1c2d7a4e6c06a66afd5f87e63b5bfaaff34cc091e8ca4d6cc03d0bdbb3", - "images/blog/appwrite-realtime-with-flutter/1.png": "15165041f76b8d59f2f4313519a23d9e1a3820d8e1760b6394971babaa8b9709", - "images/blog/appwrite-realtime-with-flutter/2.png": "44740ca35567eb456c922c1af4a4a44a7e22ff3cd5c53e38e83e32518326561a", - "images/blog/appwrite-realtime-with-flutter/3.png": "c4304f0fa8c92e8a6b473e684139034df94ab2dc7732d1c9dccf9240a712f4f1", - "images/blog/appwrite-realtime-with-flutter/4.png": "ea7d6dd933e62fdbd3b1913ce50de91ef3ddc4173915425d5d4db56cb77aaa70", - "images/blog/appwrite-realtime-with-flutter/5.png": "49fe7599941b7f5702c310047d96ac6f664b498001cdd66a5ac335be96f580c0", + "images/blog/appwrite-realtime-with-flutter/1.png": "33959f454fc5bc7aea12e5eabb1ad909b82f65e4c3fbe8fb53846b4f6363bcd4", + "images/blog/appwrite-realtime-with-flutter/2.png": "eddfd14bcb6ce51fa4fcc402fc0116cd9655ae19f73473af101b74357a1fd454", + "images/blog/appwrite-realtime-with-flutter/3.png": "7c62e1212b7306240cb5b2162b8d323297329d4235a20b2dd7e387a6fef5c966", + "images/blog/appwrite-realtime-with-flutter/4.png": "54c571e40c365197d5cfbfff2231a25f524ed54b5dc3a28649101cfb2fe44b3b", + "images/blog/appwrite-realtime-with-flutter/5.png": "3317eff1dda44327d2eaaaf902d9bd9360576aa46298cc913a78e120f61bb3a7", "images/blog/appwrite-realtime-with-flutter/cover.png": "99376d2cf9983874f7e9238dee186f5098c9b7a23d6f8ea3550d518580c8bb6e", "images/blog/appwrite-realtime/cover.png": "db1c1110935b9ad2a5675e80242914ec38e919004011b0e1060c4d8bc93db5d5", "images/blog/appwrite-server-sdk-vs-client-sdk/cover.png": "3b674e35218dc6f203824b8ee53eb367e79c33755f64a5456f290768ad5db96f", @@ -321,6 +321,7 @@ "images/blog/build-fullstack-svelte-appwrite/cover.png": "8546e5be90d6c7492366135c26594c57a9b0164b5cb410a3b6c28b3a5c58ff7a", "images/blog/build-fullstack-svelte-appwrite/expense-app-final-look.png": "d375e8e6093e58a4d3e687f877c4536e5b753d0b16f5542524501366a65e2564", "images/blog/build-fullstack-svelte-appwrite/permissions-document-security.png": "2921c151824fe91450f0bf2ac821b90a0ee7b533b6e6dc37d032cea68bcf5fef", + "images/blog/build-fullstack-svelte-appwrite/permissions-row-security.png": "3adc9f04c21a5adc60df15f3b557673f258597b2cea902159f6c147395fd64c8", "images/blog/build-internal-tools-quickly/cover.png": "e04d776ed2b9e74c485ecef8f77920d9ae92cd4bc6875641b6ccc24e96712130", "images/blog/build-personal-crm-sveltekit/cover.png": "7249afe9a63373551b51fdcd241fc448a8de30ddf86eb0ca48fc614e1877e074", "images/blog/build-personal-crm-sveltekit/personal-crm-demo.png": "5cb8dd79c440cec12d28a6637a2e6ac1a8ac8ede790facbdab308fee10c01f9f", diff --git a/src/routes/(marketing)/(components)/(ai-animations)/skills.svelte b/src/routes/(marketing)/(components)/(ai-animations)/skills.svelte index 0b125c5da7..a9345b00d4 100644 --- a/src/routes/(marketing)/(components)/(ai-animations)/skills.svelte +++ b/src/routes/(marketing)/(components)/(ai-animations)/skills.svelte @@ -18,7 +18,7 @@ const QUERY = "Store the user's profile avatar image"; const skills = [ - 'createDocument', + 'createRow', 'uploadFile', 'getUser', 'listFiles', diff --git a/src/routes/blog/post/ai-crystal-ball/+page.markdoc b/src/routes/blog/post/ai-crystal-ball/+page.markdoc index 3e87af1f64..8e90fce524 100644 --- a/src/routes/blog/post/ai-crystal-ball/+page.markdoc +++ b/src/routes/blog/post/ai-crystal-ball/+page.markdoc @@ -7,6 +7,7 @@ cover: /images/blog/ai-crystal-ball/cover.png timeToRead: 9 author: aditya-oberai category: tutorial +lastUpdated: 2026-04-07 --- Have you ever wondered what you would be doing as a developer 5 years from now? I, for sure, have, which is why recently I developed an AI Crystal Ball to use information from my GitHub account and predict what my destiny as a developer would look like. This project picked up a lot more attention than anticipated as well as a number of requests asking how this project was developed. @@ -20,7 +21,7 @@ In order to build this application, we have a few prerequisites. We must set up - OpenAI API key - GitHub OAuth app - Appwrite OAuth adapter for GitHub -- Appwrite collections to store GitHub data and destinies +- Appwrite tables to store GitHub data and destinies ## OpenAI @@ -47,7 +48,7 @@ In this app, we primarily need Appwrite for the following: - Managing GitHub OAuth login - Saving the information we get from the GitHub API, so we don’t need to call it repeatedly -- Saving developer destinies if a user wants to create a shareable link +- Saving developer destinies in a table if a user wants to create a shareable link The only prerequisite here was creating an [Appwrite Cloud account](https://cloud.appwrite.io/), followed by creating a new project and adding your hostname as a web app to the project. @@ -59,35 +60,35 @@ To implement GitHub OAuth, we must visit the **Auth** page on the Appwrite proje ## Appwrite Database -We must create a database with the ID `crystalball` and two collections with the IDs `githubData` and `destiny` in the Appwrite project with the following details: +We must create a database with the ID `crystalball` and two tables with the IDs `githubData` and `destiny` in the Appwrite project with the following details: -#### The `githubData` collection +#### The `githubData` table -Create the collection and add the following attributes: +Create the table and add the following columns: | Key | Type | Size | Required | Array | | --- | --- | --- | --- | --- | -| languages | String | 2000 | - | Yes | +| languages | varchar | 2000 | - | Yes | | followers | Integer | - | Yes | - | | following | Integer | - | Yes | - | -| username | String | 255 | Yes | - | +| username | varchar | 255 | Yes | - | -Visit the collection settings, enable **Document security,** and set the following (collection-level) **Permissions**: +Visit the table settings, enable **Row security,** and set the following (table-level) **Permissions**: | Role | Create | Read | Update | Delete | | --- | --- | --- | --- | --- | | Users | Yes | - | - | - | -#### The `destiny` collection +#### The `destiny` table -Create the collection and add the following attributes: +Create the table and add the following columns: | Key | Type | Size | Required | | --- | --- | --- | --- | -| destiny | String | 25000 | Yes | -| username | String | 255 | Yes | +| destiny | text | 25000 | Yes | +| username | varchar | 255 | Yes | -Visit the collection settings, enable **Document security,** and set the following (collection-level) **Permissions**: +Visit the table settings, enable **Row security,** and set the following (table-level) **Permissions**: | Role | Create | Read | Update | Delete | | --- | --- | --- | --- | --- | @@ -123,15 +124,15 @@ Lastly, we must create a `.env` file at the root of the directory and add the fo PUBLIC_APPWRITE_ENDPOINT= PUBLIC_APPWRITE_PROJECT_ID= PUBLIC_APPWRITE_DATABASE_ID= -PUBLIC_APPWRITE_COLLECTION_ID_GITHUBDATA= -PUBLIC_APPWRITE_COLLECTION_ID_DESTINY= +PUBLIC_APPWRITE_TABLE_ID_GITHUBDATA= +PUBLIC_APPWRITE_TABLE_ID_DESTINY= SECRET_OPENAI_API_KEY= ``` After the environment variables are created, we can set up the Appwrite SDK by creating a file `./src/lib/appwrite.js` and adding the following: ```js -import { Client, Account, Databases, OAuthProvider } from 'appwrite'; +import { Client, Account, TablesDB, OAuthProvider } from 'appwrite'; import { env } from '$env/dynamic/public'; const client = new Client() @@ -139,7 +140,7 @@ const client = new Client() .setProject(env.PUBLIC_APPWRITE_PROJECT_ID); export const account = new Account(client); -export const databases = new Databases(client); +export const tablesDB = new TablesDB(client); export { OAuthProvider }; ``` @@ -272,19 +273,19 @@ At this point, we also want to create our Database library using the Appwrite SD ```js import { Permission, Role, ID } from 'appwrite'; -import { databases } from './appwrite'; +import { tablesDB } from './appwrite'; import { env } from '$env/dynamic/public'; const databaseId = env.PUBLIC_APPWRITE_DATABASE_ID; -const githubDataCollectionId = env.PUBLIC_APPWRITE_COLLECTION_ID_GITHUBDATA; +const githubDataTableId = env.PUBLIC_APPWRITE_TABLE_ID_GITHUBDATA; export const db = { - getUserData: async(documentId) => { + getUserData: async(rowId) => { try{ - return await databases.getDocument({ + return await tablesDB.getRow({ databaseId, - collectionId: githubDataCollectionId, - documentId + tableId: githubDataTableId, + rowId }); } catch(err){ return false; @@ -292,10 +293,10 @@ export const db = { }, addUserData: async(userId, username, followers, following, languages) => { - return await databases.createDocument({ + return await tablesDB.createRow({ databaseId, - collectionId: githubDataCollectionId, - documentId: userId, + tableId: githubDataTableId, + rowId: userId, data: { username, followers, @@ -504,17 +505,17 @@ Lastly, to share our destiny with the rest of the world, we must create an addit . . . -const destinyCollectionId = env.PUBLIC_APPWRITE_COLLECTION_ID_DESTINY; +const destinyTableId = env.PUBLIC_APPWRITE_TABLE_ID_DESTINY; export const db = {** . . . addDestiny: async(username, destiny) => { - return await databases.createDocument({ + return await tablesDB.createRow({ databaseId, - collectionId: destinyCollectionId, - documentId: ID.unique(), + tableId: destinyTableId, + rowId: ID.unique(), data: { username, destiny @@ -522,12 +523,12 @@ export const db = {** }) }, - getDestiny: async(documentId) => { + getDestiny: async(rowId) => { try{ - return await databases.getDocument({ + return await tablesDB.getRow({ databaseId, - collectionId: destinyCollectionId, - documentId + tableId: destinyTableId, + rowId }); } catch(err){ return { diff --git a/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc b/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc index 2b9106c339..776120c8ed 100644 --- a/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc +++ b/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc @@ -1,8 +1,9 @@ --- layout: post -title: "Announcing Auto-increment support: Built-in numeric sequencing for your documents" -description: Get reliable, sequential ordering across your collections with fast, indexed auto-increment IDs. +title: "Announcing Auto-increment support: Built-in numeric sequencing for your rows" +description: Get reliable, sequential ordering across your tables with fast, indexed auto-increment IDs. date: 2025-07-15 +lastUpdated: 2026-04-07 cover: /images/blog/announcing-auto-increment-support/cover.png timeToRead: 5 author: jake-barnby @@ -14,11 +15,11 @@ Managing ordered data can often be complex and error-prone, especially when it r To tackle this issue, we're introducing **Auto-increment support.** -This new feature automatically handles a `$sequence` column within your documents, incrementing reliably with each new insertion. This ensures your data remains ordered and clear without additional manual overhead. +This new feature automatically handles a `$sequence` column within your rows, incrementing reliably with each new insertion. This ensures your data remains ordered and clear without additional manual overhead. # Automatic, predictable ordering -Previously, maintaining consistent insertion order and generating numeric identifiers required either manual increments, complex logic, or dependence on timestamps, which weren't always accurate or reliable. Appwrite’s new Auto-increment support feature solves these issues seamlessly, providing a built-in numeric identifier that increases predictably with every new document added. +Previously, maintaining consistent insertion order and generating numeric identifiers required either manual increments, complex logic, or dependence on timestamps, which weren’t always accurate or reliable. Appwrite’s new Auto-increment support feature solves these issues seamlessly, providing a built-in numeric identifier that increases predictably with every new row added. Whether you're creating paginated data sets, managing invoice numbers, logging activities, or building real-time feeds, Appwrite's Auto-increment support feature offers effortless numeric sequencing. This means less manual work, fewer bugs, and significantly improved reliability. @@ -37,21 +38,21 @@ Integrating Auto-increment support into your Appwrite Databases makes your backe # How it works -For numeric ordering based on insertion order, you can use the `$sequence` field, which Appwrite automatically adds to all documents. This field increments with each new insert. +For numeric ordering based on insertion order, you can use the `$sequence` field, which Appwrite automatically adds to all rows. This field increments with each new insert. ```client-web -import { Client, Databases, Query } from "appwrite"; +import { Client, TablesDB, Query } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') .setProject(''); -const databases = new Databases(client); +const tablesDB = new TablesDB(client); -databases.listDocuments({ +tablesDB.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.orderAsc('$sequence'), ] diff --git a/src/routes/blog/post/announcing-bulk-api/+page.markdoc b/src/routes/blog/post/announcing-bulk-api/+page.markdoc index 25f6f9800a..54199f8af1 100644 --- a/src/routes/blog/post/announcing-bulk-api/+page.markdoc +++ b/src/routes/blog/post/announcing-bulk-api/+page.markdoc @@ -3,6 +3,7 @@ layout: post title: "Announcing Bulk API: Handle heavy data workloads with ease" description: Perform multiple database operations in a single API call. Enjoy faster writes and better performance for heavy server-side workloads. date: 2025-07-03 +lastUpdated: 2026-04-07 cover: /images/blog/announcing-bulk-api/cover.png timeToRead: 5 author: jake-barnby @@ -14,9 +15,9 @@ We're excited to introduce another Appwrite Databases feature, **Bulk API**. Exp # Faster development with bulk actions -Previously, writing or modifying large amounts of data in Appwrite Databases required sending one request per document. This method was inefficient, slow, and resource-intensive, especially when dealing with thousands of records. +Previously, writing or modifying large amounts of data in Appwrite Databases required sending one request per row. This method was inefficient, slow, and resource-intensive, especially when dealing with thousands of records. -With the new Bulk API, you can create, update, or delete multiple documents in one go, vastly speeding up your workflows and reducing network overhead. +With the new Bulk API, you can create, update, or delete multiple rows in one go, vastly speeding up your workflows and reducing network overhead. # Optimized for server-side workloads @@ -36,12 +37,12 @@ Bulk operations can only be performed via the server-side SDKs. The client-side Utilizing the Bulk API is straightforward. You can use it to: -- Create multiple documents in a single request using the `createDocuments` method -- Update multiple documents in a single request using the `updateDocuments` method -- Delete multiple documents in a single request using the `deleteDocuments` method -- Upsert multiple documents in a single request using the `upsertDocuments` method +- Create multiple rows in a single request using the `createRows` method +- Update multiple rows in a single request using the `updateRows` method +- Delete multiple rows in a single request using the `deleteRows` method +- Upsert multiple rows in a single request using the `upsertRows` method -Here is a code example for creating multiple documents in a single request: +Here is a code example for creating multiple rows in a single request: ```server-nodejs @@ -52,22 +53,22 @@ const client = new sdk.Client() .setProject('') .setKey(''); -const databases = new sdk.Databases(client); +const tablesDB = new sdk.TablesDB(client); -const result = await databases.createDocuments( - '', - '', - [ +const result = await tablesDB.createRows({ + databaseId: '', + tableId: '', + rows: [ { $id: sdk.ID.unique(), - name: 'Document 1', + name: 'Row 1', }, { $id: sdk.ID.unique(), - name: 'Document 2', + name: 'Row 2', } ] -); +}); ``` diff --git a/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc b/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc index 2abdf79f04..8fcf91d8e5 100644 --- a/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc +++ b/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc @@ -3,6 +3,7 @@ layout: post title: Announcing Database Reads and Writes pricing description: To ensure Appwrite Cloud's sustainability, we are introducing pricing for database read and write operations, effective April 10th, 2025. date: 2025-03-13 +lastUpdated: 2026-04-07 cover: /images/blog/announcing-database-reads-and-writes-pricing/cover.png timeToRead: 6 author: eldad-fux @@ -40,16 +41,16 @@ Database operations in Appwrite are categorized into two types: ## Read operations Any action that retrieves data from your database, including: -- Fetching documents with `getDocument` or `listDocuments`. +- Fetching rows with `getRow` or `listRows`. ## Write operations Any action that modifies data in your database, including: -- Creating documents with `createDocument`. -- Updating documents with `updateDocument`. -- Deleting documents with `deleteDocument`. +- Creating rows with `createRow`. +- Updating rows with `updateRow`. +- Deleting rows with `deleteRow`. -Most operations are counted based on the number of documents affected. For example, if you fetch a collection of 50 documents with a single API call, this counts as 50 read operations, not as a single operation. However, if your query returns no documents, it will count as a single operation. +Most operations are counted based on the number of rows affected. For example, if you fetch a table of 50 rows with a single API call, this counts as 50 read operations, not as a single operation. However, if your query returns no rows, it will count as a single operation. # Your usage diff --git a/src/routes/blog/post/appwrite-1.5-now-available-on-cloud/+page.markdoc b/src/routes/blog/post/appwrite-1.5-now-available-on-cloud/+page.markdoc index 0eb08f9130..ae72abb5d7 100644 --- a/src/routes/blog/post/appwrite-1.5-now-available-on-cloud/+page.markdoc +++ b/src/routes/blog/post/appwrite-1.5-now-available-on-cloud/+page.markdoc @@ -8,6 +8,7 @@ timeToRead: 8 author: eldad-fux category: product, announcement featured: false +lastUpdated: 2026-04-07 --- After announcing many new products and features during [Init](/init) as part of the 1.5 release, we saw great excitement within the community to get started with the newly presented Appwrite 1.5. Although already available on self-hosted, the new Appwrite products and features were yet to be released to Cloud. Today, we are excited to say the wait is finally over as we announce the release of Appwrite 1.5 to the Cloud. @@ -206,9 +207,9 @@ New Database operators `contains` and `or`, providing greater control and flexib The contains operator is a great addition to the existing text search operators such as startsWith & endsWith, and can be used in combination with these two. ```js -db.listDocuments({ +db.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.contains('content', ['happy', 'love']) ] @@ -218,9 +219,9 @@ db.listDocuments({ To use the OR operator pass Query.or([...]) to the queries array and provide at least two queries within the nested array. ```js -db.listDocuments({ +db.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.or([ Query.contains('name','ivy'), diff --git a/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc b/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc index 54d6dffad5..a2eb6eb133 100644 --- a/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc +++ b/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc @@ -1,16 +1,16 @@ --- layout: post title: "Get started with Appwrite Realtime for Flutter" -description: Learn how to build a Flutter app using Appwrite's powerful Realtime API. +description: Learn how to build a Flutter app using Appwrite's powerful Realtime API. cover: /images/blog/appwrite-realtime-with-flutter/cover.png timeToRead: 6 date: 2024-09-02 author: damodar-lohani category: tutorial -lastUpdated: 2025-10-14 +lastUpdated: 2026-04-07 --- -Realtime service is one of Appwrite’s most popular features. It allows you to subscribe and react to any Appwrite event using the Realtime API. +Realtime service is one of Appwrite's most popular features. It allows you to subscribe and react to any Appwrite event using the Realtime API. In this tutorial, we will get into the details and learn how to develop a Flutter app that leverages Appwrite's real-time capabilities. @@ -26,26 +26,26 @@ On the databases page, click on the **Create database** button. ![Create database](/images/blog/appwrite-realtime-with-flutter/1.png) -In the dialog that pops up, enter a name and database ID, and click **Create to create the database and show the database page**. Make sure to note down the database ID next to the database name as we will need that later in our code. +In the dialog that pops up, enter a name and database ID, and click **Create** to create the database and show the database page. Make sure to note down the database ID next to the database name as we will need that later in our code. -Once on the database page, click on the **Create collection** button. +Once on the database page, click on the **Create table** button. -![Create collection](/images/blog/appwrite-realtime-with-flutter/2.png) +![Create table](/images/blog/appwrite-realtime-with-flutter/2.png) -In the dialog that pops up, set the collection name to **Items** and click on the **Create** button to create the collection, and you will be redirected to the new collection's page. +In the dialog that pops up, set the table name to **Items** and click on the **Create** button to create the table, and you will be redirected to the new table's page. -Switch to the Attributes tab and create the following attribute. Also note down the **Collection ID** from the top of the page next to the collection name. +Switch to the Columns tab and create the following column. Also note down the **Table ID** from the top of the page next to the table name. -- Type: String -- Attribute Key: name +- Type: Varchar +- Column Key: name - Size: 25 - Default: null - Required: true - Array: false - ![Create String](/images/blog/appwrite-realtime-with-flutter/3.png) - -Switch to the Settings tab and scroll down to **Permissions** to configure the permissions for the collection. Add the **Any** role and check create, read, update, and delete so that anyone can read and write. + ![Create Varchar column](/images/blog/appwrite-realtime-with-flutter/3.png) + +Switch to the Settings tab and scroll down to **Permissions** to configure the permissions for the table. Add the **Any** role and check create, read, update, and delete so that anyone can read and write. ![Set permissions](/images/blog/appwrite-realtime-with-flutter/4.png) @@ -67,17 +67,17 @@ flutter pub add appwrite This command will add Appwrite's latest Flutter SDK with real-time service as a dependency to your Flutter project. -Once you have installed the dependency and run `flutter pub get` you should be ready to use it. +Once you have installed the dependency and run `flutter pub get` you should be ready to use it. # Add Flutter platforms -To initialize the Appwrite SDK and start interacting with Appwrite services, you first need to add a new Flutter platform to your project. To add a new platform, go to the Appwrite Console, select your project, and click the **Add Platform** button on the project Overview. Click on the Flutter app to register a Flutter platform. +To initialize the Appwrite SDK and start interacting with Appwrite services, you first need to add a new Flutter platform to your project. To add a new platform, go to the Appwrite Console, select your project, and click the **Add Platform** button on the project Overview. Click on the Flutter app to register a Flutter platform. In the wizard that appears, choose the platform you plan to run on. If you want to run on another platform, add another platform -If you choose to add an Android platform, on the dialog box, add the details. Add your app name and package name. Your package name is generally the `applicationId` in your app-level `build.gradle` +If you choose to add an Android platform, on the dialog box, add the details. Add your app name and package name. Your package name is generally the `applicationId` in your app-level `build.gradle` -file. You may also find your package name in your `AndroidManifest.xml` file. +file. You may also find your package name in your `AndroidManifest.xml` file. ![Add the Flutter platform](/images/blog/appwrite-realtime-with-flutter/5.png) @@ -85,7 +85,7 @@ By registering a new platform, you are allowing your app to communicate with the # Home page -We will start by creating a simple stateful widget that will list all the items from our items collection and allow adding new items and deleting existing items. Our home page will also connect to Appwrite's Realtime service and display changes in the collection of items by updating the UI as they happen. So, let's create our **HomePage** widget. Modify the code in **lib/main.dart** as follows: +We will start by creating a simple stateful widget that will list all the items from our items table and allow adding new items and deleting existing items. Our home page will also connect to Appwrite's Realtime service and display changes in the table by updating the UI as they happen. So, let's create our **HomePage** widget. Modify the code in **lib/main.dart** as follows: ```dart import 'package:flutter/material.dart'; @@ -173,7 +173,7 @@ class _HomePageState extends State { ``` -In the **initState** function of the HomePage, we will create and initialize our Appwrite client, as well as subscribe to real-time changes in documents in our **items** collection. +In the **initState** function of the HomePage, we will create and initialize our Appwrite client, as well as subscribe to real-time changes in rows in our **Items** table. ```dart RealtimeSubscription? subscription; @@ -186,7 +186,7 @@ In the **initState** function of the HomePage, we will create and initialize o } ``` -And in **dispose** method, close the subscription. +And in **dispose** method, close the subscription. ```dart dispose(){ @@ -195,33 +195,33 @@ dispose(){ } ``` -Now, let us set up different variables and functions to load the initial data, listen to changes in the collection documents, and update the UI to reflect the changes in real time. +Now, let us set up different variables and functions to load the initial data, listen to changes in the table rows, and update the UI to reflect the changes in real time. -First, initialize our database ID and items collection ID and set up a function to load initial data when the application first starts. For that, we will also set up an Appwrite database service. +First, initialize our database ID and items table ID and set up a function to load initial data when the application first starts. For that, we will also set up an Appwrite TablesDB service. ```dart final database = 'default'; // your database id - final itemsCollection = 'items'; // your collection id - late final Databases databases; + final itemsTable = 'items'; // your table id + late final TablesDB tablesDB; @override initState() { super.initState(); - client = Client().setProject('delete'); // your project id - databases = Databases(client); + client = Client().setProject(''); // your project id + tablesDB = TablesDB(client); loadItems(); subscribe(); } loadItems() async { try { - final res = await databases.listDocuments( + final res = await tablesDB.listRows( databaseId: database, - collectionId: itemsCollection, + tableId: itemsTable, ); setState(() { items = - List>.from(res.documents.map((e) => e.data)); + List>.from(res.rows.map((e) => e.data)); }); } on AppwriteException catch (e) { print(e.message); @@ -229,14 +229,14 @@ First, initialize our database ID and items collection ID and set up a function } ``` -Now, we will set up our subscribe function, which will listen to changes to documents in our items collection. +Now, we will set up our subscribe function, which will listen to changes to rows in our items table using Channel helpers. ```dart void subscribe() { final realtime = Realtime(client); subscription = realtime.subscribe([ - 'documents' // subscribe to all documents in every database and collection + Channel.tablesdb(database).table(itemsTable).row() ]); // listen to changes @@ -263,10 +263,10 @@ Finally, let's modify our `_addItem` function to add items to Appwrite's databas ```dart void _addItem(String name) async { try { - await databases.createDocument( + await tablesDB.createRow( databaseId: database, - collectionId: itemsCollection, - documentId: ID.unique(), + tableId: itemsTable, + rowId: ID.unique(), data: {'name': name}, ); } on AppwriteException catch (e) { @@ -275,7 +275,7 @@ Finally, let's modify our `_addItem` function to add items to Appwrite's databas } ``` -Let us also modify our `ListTile` widget to add a delete button that will allow us to delete the item. +Let us also modify our `ListTile` widget to add a delete button that will allow us to delete the item. ```dart ListTile( @@ -283,10 +283,10 @@ Let us also modify our `ListTile` widget to add a delete button that will allo trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () async { - await databases.deleteDocument( + await tablesDB.deleteRow( databaseId: database, - collectionId: itemsCollection, - documentId: item['\$id'], + tableId: itemsTable, + rowId: item['\$id'], ); }, ), @@ -295,7 +295,7 @@ Let us also modify our `ListTile` widget to add a delete button that will allo ## Complete example -``` +```dart import 'package:appwrite/appwrite.dart'; import 'package:flutter/material.dart'; @@ -331,27 +331,27 @@ class _HomePageState extends State { RealtimeSubscription? subscription; late final Client client; final database = 'default'; // your database id - final itemsCollection = 'items'; // your collection id - late final Databases databases; + final itemsTable = 'items'; // your table id + late final TablesDB tablesDB; @override initState() { super.initState(); - client = Client().setProject('delete'); // your project id - databases = Databases(client); + client = Client().setProject(''); // your project id + tablesDB = TablesDB(client); loadItems(); subscribe(); } loadItems() async { try { - final res = await databases.listDocuments( + final res = await tablesDB.listRows( databaseId: database, - collectionId: itemsCollection, + tableId: itemsTable, ); setState(() { items = - List>.from(res.documents.map((e) => e.data)); + List>.from(res.rows.map((e) => e.data)); }); } on AppwriteException catch (e) { print(e.message); @@ -362,7 +362,7 @@ class _HomePageState extends State { final realtime = Realtime(client); subscription = realtime.subscribe([ - 'documents' // subscribe to all documents in every database and collection + Channel.tablesdb(database).table(itemsTable).row() ]); // listen to changes @@ -401,10 +401,10 @@ class _HomePageState extends State { trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () async { - await databases.deleteDocument( + await tablesDB.deleteRow( databaseId: database, - collectionId: itemsCollection, - documentId: item['\$id'], + tableId: itemsTable, + rowId: item['\$id'], ); }, ), @@ -448,10 +448,10 @@ class _HomePageState extends State { void _addItem(String name) async { try { - await databases.createDocument( + await tablesDB.createRow( databaseId: database, - collectionId: itemsCollection, - documentId: ID.unique(), + tableId: itemsTable, + rowId: ID.unique(), data: {'name': name}, ); } on AppwriteException catch (e) { @@ -464,7 +464,7 @@ class _HomePageState extends State { # Conclusion -I hope you enjoyed learning and building Flutter applications with Appwrite Realtime service. +I hope you enjoyed learning and building Flutter applications with Appwrite Realtime service. If you have any questions, feel free to ask on our [Discord server](https://appwrite.io/discord). You can also share your apps built Flutter and Appwrite Realtime on [Built with Appwrite](https://builtwith.appwrite.io/), and we'll feature it on our socials! @@ -474,11 +474,11 @@ Happy coding! **1. What is Appwrite Realtime, and how does it work?** -Appwrite Realtime lets your app respond instantly to changes, like new database entries or deleted documents, without needing manual refreshes. You simply subscribe to events (e.g., document creation, updates, or deletions), and Appwrite automatically pushes updates to your app via a WebSocket connection. +Appwrite Realtime lets your app respond instantly to changes, like new database rows or deleted rows, without needing manual refreshes. You simply subscribe to events (e.g., row creation, updates, or deletions), and Appwrite automatically pushes updates to your app via a WebSocket connection. **2. How does Realtime improve Flutter app performance?** -Realtime keeps your app’s state instantly in sync with the backend. Instead of constantly polling APIs for updates, your app reacts to live events, reducing unnecessary requests and improving responsiveness. It’s perfect for features like chats, dashboards, or collaborative tools. +Realtime keeps your app's state instantly in sync with the backend. Instead of constantly polling APIs for updates, your app reacts to live events, reducing unnecessary requests and improving responsiveness. It's perfect for features like chats, dashboards, or collaborative tools. 3. Can I use Appwrite Realtime without a database? @@ -486,7 +486,7 @@ Yes. While databases are a common use case, Realtime can also subscribe to other **4. Is Realtime secure?** -Yes. Appwrite Realtime enforces the same permissions and roles as other Appwrite services. Users only receive updates for documents or data they are allowed to access, ensuring security and privacy even in live data streams. +Yes. Appwrite Realtime enforces the same permissions and roles as other Appwrite services. Users only receive updates for rows or data they are allowed to access, ensuring security and privacy even in live data streams. **5. Can I use Appwrite Realtime with other frameworks or just Flutter?** diff --git a/src/routes/blog/post/best-pagination-technique/+page.markdoc b/src/routes/blog/post/best-pagination-technique/+page.markdoc index 58fe8b283d..f2d3875d0a 100644 --- a/src/routes/blog/post/best-pagination-technique/+page.markdoc +++ b/src/routes/blog/post/best-pagination-technique/+page.markdoc @@ -8,6 +8,7 @@ timeToRead: 8 author: matej-baco category: tutorial callToAction: true +lastUpdated: 2026-04-07 --- The Database is one of the cornerstones of every application. It's where you store everything your app needs to remember, compute later, or display to other users online. It's all smooth sailing until your database grows and your application starts lagging because it's trying to fetch and render 1,000 posts simultaneously. As a smart engineer, you quickly patch this with a `Show more` button. However, a few weeks later, you encounter a `Timeout` error. Turning to Stack Overflow, you find that copying and pasting solutions is no longer helping. With no other options, you start debugging and discover that the database returns over 50,000 posts each time a user opens your app. What do you do now? @@ -81,47 +82,47 @@ This is a really rare condition and only occurs if the row's ID that you are abo [Appwrite](https://appwrite.io/) is an open-source backend-as-a-service that abstracts all the complexity involved in building a modern application by providing you with a set of REST APIs for your core backend needs. Appwrite handles user authentication and authorization, databases, file storage, cloud functions, webhooks, messaging, and more. You can extend Appwrite using your favorite backend language if anything is missing. -Appwrite Database lets you store any text-based data that needs to be shared across your users. Appwrite's database allows you to create multiple collections (tables) and store multiple documents (rows) in it. Each collection has attributes (columns) configured to give your dataset a proper schema. You can also configure indexes to make your search queries more performant. When reading your data, you can use powerful queries, filter them, sort them, limit the number of results, and paginate over them. +Appwrite Database lets you store any text-based data that needs to be shared across your users. Appwrite's database allows you to create multiple tables and store multiple rows in them. Each table has columns configured to give your dataset a proper schema. You can also configure indexes to make your search queries more performant. When reading your data, you can use powerful queries, filter them, sort them, limit the number of results, and paginate over them. -Appwrite's pagination supports both offset and cursor pagination. Let's imagine you have a collection with ID `articles`. You can get documents from this collection with either offset or cursor pagination: +Appwrite's pagination supports both offset and cursor pagination. Let's imagine you have a table with ID `articles`. You can get rows from this table with either offset or cursor pagination: ```jsx // Setup -import { Appwrite, Databases, Query } from "appwrite"; +import { Client, TablesDB, Query } from "appwrite"; -const client = new Appwrite(); +const client = new Client(); client .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint .setProject('articles-demo'); // Your project ID -const databases = new Databases(client); +const tablesDB = new TablesDB(client); // Offset pagination -databases.listDocuments( - 'main', // Database ID - 'articles', // Collection ID - [ - Query.limit(10), // Limit, total documents in the response - Query.offset(500), // Offset, amount of documents to skip +tablesDB.listRows({ + databaseId: 'main', + tableId: 'articles', + queries: [ + Query.limit(10), // Limit, total rows in the response + Query.offset(500), // Offset, amount of rows to skip ] -).then((response) => { +}).then((response) => { console.log(response); }); // Cursor pagination -databases.listDocuments( - 'main', // Database ID - 'articles', // Collection ID - [ - Query.limit(10), // Limit, total documents in the response - Query.cursorAfter('61d6eb2281fce3650c2c', // ID of document I want to paginate after +tablesDB.listRows({ + databaseId: 'main', + tableId: 'articles', + queries: [ + Query.limit(10), // Limit, total rows in the response + Query.cursorAfter('61d6eb2281fce3650c2c'), // ID of row to paginate after ] -).then((response) => { +}).then((response) => { console.log(response); }); ``` -First, we import the Appwrite SDK and set up an instance that connects to our Appwrite Cloud project. Then, we list 10 documents using offset pagination. Right after, we write the exact same list documents query, but this time using `cursor` instead of offset pagination. +First, we import the Appwrite SDK and set up an instance that connects to our Appwrite Cloud project. Then, we list 10 rows using offset pagination. Right after, we write the exact same list rows query, but this time using `cursor` instead of offset pagination. ## Benchmarks @@ -129,7 +130,7 @@ We’ve frequently mentioned the term "performance" in this article without prov > You can find complete source code in the [GitHub repository.](https://github.com/appwrite) -First, you set up Appwrite, register a user, create a project and create a collection called `posts` with read permission set to `any`. To learn more about this process, visit the [Appwrite docs](https://appwrite.io/docs). You should now have Appwrite ready to go. +First, you set up Appwrite, register a user, create a project and create a table called `posts` with read permission set to `any`. To learn more about this process, visit the [Appwrite docs](https://appwrite.io/docs). You should now have Appwrite ready to go. Use the following script to load data into our MariaDB database and prepare for the benchmark. We could use Appwrite SDK, but talking directly to MariaDB offers more optional write queries for large datasets. @@ -175,9 +176,9 @@ console.error(`🌟 Successfully finished`); > We used two layers for loops to increase the speed of the script. First for loop creates query executions that need to be awaited, and the second loop creates a long query holding multiple insert requests. Ideally, we would want everything in one request, but that is impossible due to MySQL configuration, so we split it into 100 requests. -Now you have 1 million documents inserted in less than a minute, and are ready to start the benchmarks. We will be using the [k6](https://k6.io/) load-testing library for this demo. +Now you have 1 million rows inserted in less than a minute, and are ready to start the benchmarks. We will be using the [k6](https://k6.io/) load-testing library for this demo. -Let's benchmark the well-known and widely used offset pagination first. During each test scenario, we try to fetch a page with 10 documents, from different parts of our dataset. We will start with offset 0 and go all the way to an offset of 900k in increments of 100k. The benchmark is written in a way that makes only one request at a time to keep it as accurate as possible. We will also run the same benchmark ten times and measure average response times to ensure statistical significance. We'll be using k6's HTTP client to make requests to Appwrite's REST API. +Let's benchmark the well-known and widely used offset pagination first. During each test scenario, we try to fetch a page with 10 rows, from different parts of our dataset. We will start with offset 0 and go all the way to an offset of 900k in increments of 100k. The benchmark is written in a way that makes only one request at a time to keep it as accurate as possible. We will also run the same benchmark ten times and measure average response times to ensure statistical significance. We'll be using k6's HTTP client to make requests to Appwrite's REST API. ```jsx // script_offset.sh @@ -198,7 +199,7 @@ export default function () { const offset = Query.offset(__ENV.OFFSET); const limit = 10; - http.get(`${config.endpoint}/databases/main/collections/posts/documents?queries[]=${offset}&queries[]=${limit}`, { + http.get(`${config.endpoint}/databases/main/tables/posts/rows?queries[]=${offset}&queries[]=${limit}`, { headers: { 'content-type': 'application/json', 'X-Appwrite-Project': config.projectId @@ -243,9 +244,9 @@ Within a minute, all benchmarks finished, providing me with the average response | 90% offset | 482.71 | ![Cursor pagination benchmark](/images/blog/best-pagination-technique/graph.png) -As you can see, offset 0 was pretty fast, responding in less than 4ms. The first jump was to offset 100k, and the change was drastic, increasing response times to 52ms. With each increase in the offset, the duration went up, resulting in almost 500ms to get ten documents after an offset of 900k documents. That is crazy! +As you can see, offset 0 was pretty fast, responding in less than 4ms. The first jump was to offset 100k, and the change was drastic, increasing response times to 52ms. With each increase in the offset, the duration went up, resulting in almost 500ms to get ten rows after an offset of 900k rows. That is crazy! -Now let's update our script to use cursor pagination. We will update our script to use a cursor instead of offset and update our bash script to provide a cursor (document ID) instead of an offset number. +Now let's update our script to use cursor pagination. We will update our script to use a cursor instead of offset and update our bash script to provide a cursor (row ID) instead of an offset number. ```jsx // script_cursor.js @@ -266,7 +267,7 @@ export default function () { const cursor = Query.cursorAfter(__ENV.CURSOR); const limit = 10; - http.get(`${config.endpoint}/databases/main/collections/posts/documents?queries[]=${offset}&queries[]=${limit}`, { + http.get(`${config.endpoint}/databases/main/tables/posts/rows?queries[]=${offset}&queries[]=${limit}`, { headers: { 'content-type': 'application/json', 'X-Appwrite-Project': config.projectId diff --git a/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc b/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc index 417c7f39fb..cbaa685f39 100644 --- a/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc +++ b/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc @@ -3,6 +3,7 @@ layout: post title: Building a full-stack app with Svelte and Appwrite description: Learn how to build full-stack apps with Svelte and Appwrite by building an expense tracker. date: 2025-01-31 +lastUpdated: 2026-04-07 cover: /images/blog/build-fullstack-svelte-appwrite/cover.png timeToRead: 15 author: ebenezer-don @@ -61,7 +62,7 @@ Our application needs to communicate with [Appwrite](https://cloud.appwrite.io/? PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 PUBLIC_APPWRITE_PROJECT_ID=your-project-id PUBLIC_APPWRITE_DATABASE_ID=expense-db -PUBLIC_APPWRITE_COLLECTION_ID=expenses +PUBLIC_APPWRITE_TABLE_ID=expenses ``` The `PUBLIC_` prefix makes these variables available to our client-side code in Svelte. You'll need to replace `your-project-id` with your actual Appwrite project ID, which we'll create in the next step. @@ -72,28 +73,28 @@ Before we continue with the frontend implementation, we need to configure our Ap 1. Create a new project 2. Open the **Databases** tab from the sidebar and create a database with the ID "expense-db" -3. In your new database, create a collection with the ID "expenses" +3. In your new database, create a table with the ID "expenses" -The expenses collection needs several attributes to store the expense data effectively. Open the **Attributes** tab of your new collection and add the following attributes: +The expenses table needs several columns to store the expense data effectively. Open the **Columns** tab of your new table and add the following columns: ```md -- `userId` (String, required) +- `userId` (Varchar, required) - `amount` (Float, required) - `category` (Enum, required) - Elements: "food", "rent", "transportation", "entertainment", "shopping", "healthcare", "utilities", "education", "other" -- `description` (String, required) +- `description` (Varchar, required) - `date` (DateTime, required) - `createdAt` (DateTime, required) - `updatedAt` (DateTime, required) ``` -Notice that the `category` attribute is an enumerated type with a set of predefined values. This structured approach helps us organize and filter expenses effectively. We have both a `date` attribute and a `createdAt` attribute because when an expense is created is not necessarily the same as when it occurred. +Notice that the `category` column is an enumerated type with a set of predefined values. This structured approach helps us organize and filter expenses effectively. We have both a `date` column and a `createdAt` column because when an expense is created is not necessarily the same as when it occurred. -To ensure that users can only access their own expenses, Open the collection's **Settings** tab and scroll to **Permissions**. Click **Add role**, select **Users** and check **Create** permission. +To ensure that users can only access their own expenses, Open the table's **Settings** tab and scroll to **Permissions**. Click **Add role**, select **Users** and check **Create** permission. -Next, enable **Document Security** to allow users to access their documents. We'll ensure this by giving users the **Read** permission when creating documents in our code. +Next, enable **Row Security** to allow users to access their rows. We'll ensure this by giving users the **Read** permission when creating rows in our code. -![permissions-document-security](/images/blog/build-fullstack-svelte-appwrite/permissions-document-security.png) +![permissions-row-security](/images/blog/build-fullstack-svelte-appwrite/permissions-row-security.png) ## Project structure @@ -160,12 +161,12 @@ This template provides the basic structure for our application. The `data-svelte Let's set up our connection to Appwrite. If you haven't already, create a new file in the `src/lib` directory named `appwrite.js`. We'll use this file to configure the Appwrite client and provide access to our database and account services. ```js -import { Client, Account, Databases } from 'appwrite' +import { Client, Account, TablesDB } from 'appwrite' import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID, PUBLIC_APPWRITE_DATABASE_ID, - PUBLIC_APPWRITE_COLLECTION_ID + PUBLIC_APPWRITE_TABLE_ID } from '$env/static/public' const client = new Client() @@ -173,17 +174,17 @@ const client = new Client() client.setEndpoint(PUBLIC_APPWRITE_ENDPOINT).setProject(PUBLIC_APPWRITE_PROJECT_ID) export const account = new Account(client) -export const databases = new Databases(client) +export const tablesDB = new TablesDB(client) -// Collection IDs from environment variables -export const EXPENSES_COLLECTION_ID = PUBLIC_APPWRITE_COLLECTION_ID +// Table IDs from environment variables +export const EXPENSES_TABLE_ID = PUBLIC_APPWRITE_TABLE_ID export const DATABASE_ID = PUBLIC_APPWRITE_DATABASE_ID ``` -This configuration file initializes our connection to Appwrite. The `Client` class creates a new Appwrite client instance, which we configure with our endpoint and project ID from our environment variables. We then create instances of the `Databases` and `Account` services, which we'll use throughout our application for database operations and user authentication. +This configuration file initializes our connection to Appwrite. The `Client` class creates a new Appwrite client instance, which we configure with our endpoint and project ID from our environment variables. We then create instances of the `TablesDB` and `Account` services, which we'll use throughout our application for database operations and user authentication. -Finally, we export the collection IDs from our environment variables so that we can use them in other parts of our application. +Finally, we export the table IDs from our environment variables so that we can use them in other parts of our application. ## Managing authentication state @@ -532,8 +533,8 @@ We'll start with our imports and state management: ```svelte @@ -76,15 +77,15 @@ const client = new Appwrite.Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID -const database = new Appwrite.Databases(client); +const database = new Appwrite.TablesDB(client); document.querySelector('button').addEventListener('click', async () => { const promises = []; for (let i = 0; i < 200; i++) { - const promise = database.createDocument({ + const promise = database.createRow({ databaseId: 'testDb', // Your database ID - collectionId: 'testCollection', // Your collection ID - documentId: Appwrite.ID.unique(), + tableId: 'testTable', // Your table ID + rowId: Appwrite.ID.unique(), data: { number: i + i } }); promises.push(promise); @@ -95,7 +96,7 @@ document.querySelector('button').addEventListener('click', async () => { }); ``` -If you open the HTML page in your browser and click on the `Add documents` button, you will notice numerous errors in the console with the HTTP code `429`, as Appwrite's rate limits allow one client to create 120 requests per minute for this API endpoint. +If you open the HTML page in your browser and click on the `Add rows` button, you will notice numerous errors in the console with the HTTP code `429`, as Appwrite's rate limits allow one client to create 120 requests per minute for this API endpoint. ## Step 3: Create dev key @@ -114,7 +115,7 @@ const client = new Appwrite.Client() ## Step 4: Test the app -Reopen the HTML page in your browser. Clicking the `Add documents` button will allow all 200 requests to execute successfully. +Reopen the HTML page in your browser. Clicking the `Add rows` button will allow all 200 requests to execute successfully. # Next steps diff --git a/src/routes/blog/post/introducing-new-database-operators/+page.markdoc b/src/routes/blog/post/introducing-new-database-operators/+page.markdoc index 2445f9ad8e..83f250ae66 100644 --- a/src/routes/blog/post/introducing-new-database-operators/+page.markdoc +++ b/src/routes/blog/post/introducing-new-database-operators/+page.markdoc @@ -8,6 +8,7 @@ timeToRead: 5 author: dennis-ivy category: product, announcement featured: false +lastUpdated: 2026-04-07 --- We've added two new query methods, `or` and `contains`, to Appwrite Databases. By adding array element matches, partial text matches, as well as logical OR queries, we allow for more flexibility in managing your data. @@ -22,9 +23,9 @@ These two query methods have been highly requested by the Appwrite community, an The contains operator is a great addition to the existing text search operators such as `startsWith` & `endsWith`, and can be used in combination with these two. With contains, we can now perform a broader search by matching against any text within a substring. This is extremely useful when searching a large body of text or when the placement of keywords is unknown. ```js -db.listDocuments({ +tablesDB.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.contains('content', ['happy', 'love']) ] @@ -45,9 +46,9 @@ The logical OR operator allows us to nest queries in an OR condition. This gives To use the OR operator pass `Query.or([...])` to the queries array and provide at least two queries within the nested array. ```js -db.listDocuments({ +tablesDB.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.or([ Query.contains('name','ivy'), diff --git a/src/routes/blog/post/offline-first-journal/+page.markdoc b/src/routes/blog/post/offline-first-journal/+page.markdoc index a7887ec69d..b2a026f363 100644 --- a/src/routes/blog/post/offline-first-journal/+page.markdoc +++ b/src/routes/blog/post/offline-first-journal/+page.markdoc @@ -3,6 +3,7 @@ layout: post title: Build an offline-first journal app with RxDB and Appwrite description: Learn how you can use RxDB to setup data replication and enable offline synchronization with Appwrite Databases in a JavaScript app. date: 2025-04-17 +lastUpdated: 2026-04-07 cover: /images/blog/offline-first-journal/cover.png timeToRead: 14 author: aditya-oberai @@ -42,21 +43,21 @@ Our tech stack for this app will be: ## Configure your Appwrite project -First, [create an Appwrite Cloud account](https://cloud.appwrite.io/) if you haven’t already. Once your project is ready, go to the **Settings** page and copy your project ID and API endpoint for further usage. Next, go to the **Databases** page from the left sidebar, create a new database with the ID `journals`, and then a collection with the ID `entries` (save both IDs for further usage). +First, [create an Appwrite Cloud account](https://cloud.appwrite.io/) if you haven’t already. Once your project is ready, go to the **Settings** page and copy your project ID and API endpoint for further usage. Next, go to the **Databases** page from the left sidebar, create a new database with the ID `journals`, and then a table with the ID `entries` (save both IDs for further usage). -Next, head to the **Attributes** tab and add the following: +Next, head to the **Columns** tab and add the following: | Key | Type | Size | Required | | --- | --- | --- | --- | -| `title` | String | 100 | Yes | -| `content` | String | 20000 | Yes | +| `title` | Varchar | 100 | Yes | +| `content` | Text | 20000 | Yes | | `createdAt` | Integer | | Yes | | `updatedAt` | Integer | | Yes | | `deleted` | Boolean | | Yes | > **Note:** The `deleted` attribute is necessary to add because RxDB does not hard delete any data, only soft deletes to prevent data loss in offline scenarios. -Then, head to the **Settings** tab of your collection, scroll down to the **Permissions** section, and add the following: +Then, head to the **Settings** tab of your table, scroll down to the **Permissions** section, and add the following: | Role | Create | Read | Update | Delete | | --- | --- | --- | --- | --- | @@ -103,7 +104,7 @@ In the root directory of your app, create a `.env` file and add the information PUBLIC_APPWRITE_ENDPOINT=your-appwrite-cloud-endpoint PUBLIC_APPWRITE_PROJECT_ID=your-project-id PUBLIC_APPWRITE_DATABASE_ID=your-database-id -PUBLIC_APPWRITE_COLLECTION_ID=your-collection-id +PUBLIC_APPWRITE_TABLE_ID=your-table-id ``` Next, in the `src/lib` subdirectory, create a file `appwrite.js` and add the following code: @@ -114,14 +115,14 @@ import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID, PUBLIC_APPWRITE_DATABASE_ID, - PUBLIC_APPWRITE_COLLECTION_ID + PUBLIC_APPWRITE_TABLE_ID } from '$env/static/public'; export const appwriteConfig = { endpoint: PUBLIC_APPWRITE_ENDPOINT, projectId: PUBLIC_APPWRITE_PROJECT_ID, databaseId: PUBLIC_APPWRITE_DATABASE_ID, - collectionId: PUBLIC_APPWRITE_COLLECTION_ID + tableId: PUBLIC_APPWRITE_TABLE_ID }; export const client = new Client() @@ -208,7 +209,7 @@ export const getDB = async () => { // Add collections await db.addCollections({ - entries: { // Name must match the collection ID from Appwrite + entries: { // Name must match the table ID from Appwrite schema: journalSchema } }); @@ -238,7 +239,7 @@ const setupReplication = async (db) => { replicationIdentifier: 'journals-replication', client, databaseId: appwriteConfig.databaseId, - collectionId: appwriteConfig.collectionId, + collectionId: appwriteConfig.tableId, deletedField: 'deleted', collection: db.entries, pull: { diff --git a/src/routes/blog/post/simplify-your-data-management-with-relationships/+page.markdoc b/src/routes/blog/post/simplify-your-data-management-with-relationships/+page.markdoc index 904abba967..0a02245c42 100644 --- a/src/routes/blog/post/simplify-your-data-management-with-relationships/+page.markdoc +++ b/src/routes/blog/post/simplify-your-data-management-with-relationships/+page.markdoc @@ -1,17 +1,18 @@ --- layout: post title: Simplify your data management with relationships -description: Learn how to simplify your collection management and save time and effort with database relationships. +description: Learn how to simplify your table management and save time and effort with database relationships. date: 2024-01-05 +lastUpdated: 2026-04-07 cover: /images/blog/simplify-your-data-management-with-relationships/cover.png timeToRead: 6 author: jake-barnby category: accessibility --- -Managing collections of data is an essential task for any application, but it can quickly become complex when you need to keep track of the relationships between different collections. For instance, if you have two collections of data, such as movies and reviews, you may need to retrieve all the reviews associated with a particular movie. However, writing complex code to retrieve this data and merge it together manually can be time-consuming and prone to errors. +Managing tables of data is an essential task for any application, but it can quickly become complex when you need to keep track of the relationships between different tables. For instance, if you have two tables of data, such as movies and reviews, you may need to retrieve all the reviews associated with a particular movie. However, writing complex code to retrieve this data and merge it together manually can be time-consuming and prone to errors. -That's where one of Appwrite’s newest features comes in. Database relationships help you manage the links between your collections of data more easily. With this feature, you can create links between different collections by simply adding a new attribute to them. You can choose from four types of relationships: one-to-one, one-to-many, many-to-one, and many-to-many. +That’s where one of Appwrite’s newest features comes in. Database relationships help you manage the links between your tables of data more easily. With this feature, you can create links between different tables by simply adding a new column to them. You can choose from four types of relationships: one-to-one, one-to-many, many-to-one, and many-to-many. # Relationship Types @@ -19,25 +20,25 @@ That's where one of Appwrite’s newest features comes in. Database relationship ![one to one](/images/blog/simplify-your-data-management-with-relationships/one-to-one.png) -A one-to-one relationship means that each record in one collection is associated with only one record in another collection. For example, if you have a collection of users and a collection of profiles, each user can have only one profile, and each profile can belong to only one user. +A one-to-one relationship means that each record in one table is associated with only one record in another table. For example, if you have a table of users and a table of profiles, each user can have only one profile, and each profile can belong to only one user. ## One to Many ![one to many](/images/blog/simplify-your-data-management-with-relationships/one-to-many.png) -A one-to-many relationship is when each record in one collection can be associated with multiple records in another collection. For instance, if you have a collection of artists and a collection of albums, each artist can have many albums released, but each album can only be released by one artist. +A one-to-many relationship is when each record in one table can be associated with multiple records in another table. For instance, if you have a table of artists and a table of albums, each artist can have many albums released, but each album can only be released by one artist. ## Many To One ![many to one](/images/blog/simplify-your-data-management-with-relationships/many-to-one.png) -In contrast, many-to-one relationships are when multiple records in one collection can be associated with a single record in another collection. Inversely to the previous example, if you have a collection of albums and a collection of artists, many albums can be released by a single artist. While this may seem the same as a one-to-many relationship, it differs once you take the direction into account. A many-to-one relationship that is one-way can be used to represent a relationship that is only seen on the many side. +In contrast, many-to-one relationships are when multiple records in one table can be associated with a single record in another table. Inversely to the previous example, if you have a table of albums and a table of artists, many albums can be released by a single artist. While this may seem the same as a one-to-many relationship, it differs once you take the direction into account. A many-to-one relationship that is one-way can be used to represent a relationship that is only seen on the many side. ## Many to Many ![many to many](/images/blog/simplify-your-data-management-with-relationships/many-to-many.png) -Finally, many-to-many relationships describe a scenario where multiple records in one collection can be associated with multiple records in another collection. For example, if you have a collection of books and a collection of authors, each book can have multiple authors, and each author can write multiple books. +Finally, many-to-many relationships describe a scenario where multiple records in one table can be associated with multiple records in another table. For example, if you have a table of books and a table of authors, each book can have multiple authors, and each author can write multiple books. By understanding the differences between these relationship types, you can choose the best one that suits your application's needs and create a more efficient database management system. @@ -47,7 +48,7 @@ Appwrite relationships offer a high degree of flexibility and customization, all ## One Way -One-way relationships are a type of relationship where only the collection where the relationship attribute was created will see the relationship. For example, suppose a developer creates a one-way relationship between a movies collection and a reviews collection. In that case, the movies collection will have an attribute containing the related reviews, but the reviews collection will not have an attribute containing the related movie. This type of relationship is useful in scenarios where the parent collection holds all the necessary information, and the child collection only needs to access the parent's data. +One-way relationships are a type of relationship where only the table where the relationship column was created will see the relationship. For example, suppose a developer creates a one-way relationship between a movies table and a reviews table. In that case, the movies table will have a column containing the related reviews, but the reviews table will not have a column containing the related movie. This type of relationship is useful in scenarios where the parent table holds all the necessary information, and the child table only needs to access the parent's data. The movie response: @@ -55,7 +56,7 @@ The movie response: { "$id": "642b9afc785532a807d8", "$databaseId": "marvel", - "$collectionId": "movies", + "$tableId": "movies", "$createdAt": "2023-04-04T03:35:24.493+00:00", "$updatedAt": "2023-04-04T03:35:24.493+00:00", "$permissions": [ @@ -66,7 +67,7 @@ The movie response: { "$id": "642b9d627d866e646602", "$databaseId": "marvel", - "$collectionId" :"reviews", + "$tableId" :"reviews", "$createdAt": "2023-04-04T03:45:38.514+00:00", "$updatedAt": "2023-04-04T03:45:38.514+00:00", "$permissions": [ @@ -85,7 +86,7 @@ The review response, notably with no review attribute: { "$id": "642b9d627d866e646602", "$databaseId": "marvel", - "$collectionId": "reviews" + "$tableId": "reviews" "$createdAt": "2023-04-04T03:45:38.514+00:00", "$updatedAt": "2023-04-04T03:45:38.514+00:00", "$permissions": [], @@ -96,7 +97,7 @@ The review response, notably with no review attribute: ## Two Way -On the other hand, a two-way relationship is a type of relationship where both collections will see the relationship. In the same example, if a two-way relationship is created between the movies and reviews collections, both collections will have attributes containing the related data. This type of relationship is beneficial in scenarios where both the parent and child collections need to access each other's data. +On the other hand, a two-way relationship is a type of relationship where both tables will see the relationship. In the same example, if a two-way relationship is created between the movies and reviews tables, both tables will have columns containing the related data. This type of relationship is beneficial in scenarios where both the parent and child tables need to access each other's data. The movie response: @@ -104,7 +105,7 @@ The movie response: { "$id": "642b9afc785532a807d8", "$databaseId": "marvel", - "$collectionId": "movies", + "$tableId": "movies", "$createdAt": "2023-04-04T03:35:24.493+00:00", "$updatedAt": "2023-04-04T03:35:24.493+00:00", "$permissions": [], @@ -113,7 +114,7 @@ The movie response: { "$id": "642b9d627d866e646602", "$databaseId": "marvel", - "$collectionId": "reviews", + "$tableId": "reviews", "$createdAt": "2023-04-04T03:45:38.514+00:00", "$updatedAt": "2023-04-04T03:45:38.514+00:00", "$permissions": [], @@ -130,7 +131,7 @@ The review response, now with the movie attribute: { "$id": "642b9d627d866e646602", "$databaseId": "marvel", - "$collectionId": "reviews", + "$tableId": "reviews", "$createdAt": "2023-04-04T03:45:38.514+00:00", "$updatedAt": "2023-04-04T03:45:38.514+00:00", "$permissions": [], @@ -138,7 +139,7 @@ The review response, now with the movie attribute: "movie": { "$id": "642b9afc785532a807d8", "$databaseId": "marvel", - "$collectionId": "movies", + "$tableId": "movies", "$createdAt": "2023-04-04T03:35:24.493+00:00", "$updatedAt": "2023-04-04T03:35:24.493+00:00", "$permissions": [], @@ -148,7 +149,7 @@ The review response, now with the movie attribute: ``` -One-way and two-way relationships offer different advantages and disadvantages, depending on the specific requirements of the application. In some cases, one-way relationships can provide better performance by reducing the amount of data that needs to be stored and accessed. However, two-way relationships can provide more flexibility by allowing both collections to access each other's data. +One-way and two-way relationships offer different advantages and disadvantages, depending on the specific requirements of the application. In some cases, one-way relationships can provide better performance by reducing the amount of data that needs to be stored and accessed. However, two-way relationships can provide more flexibility by allowing both tables to access each other's data. # On Delete Behavior @@ -156,23 +157,23 @@ Managing related data can still be challenging, especially when deleting data. T ## Restrict -If you select the restrict option, you won't be able to delete a parent document if it has any related child documents. This option is helpful if you want to ensure that data integrity is maintained and that you don't accidentally delete data that is still relevant. +If you select the restrict option, you won't be able to delete a parent row if it has any related child rows. This option is helpful if you want to ensure that data integrity is maintained and that you don't accidentally delete data that is still relevant. ## Cascade -If you choose cascade, deleting a parent document will also delete all related child documents. This option can be helpful if you want to remove all data associated with a particular parent record, such as when you want to delete a user and all their associated data. +If you choose cascade, deleting a parent row will also delete all related child rows. This option can be helpful if you want to remove all data associated with a particular parent record, such as when you want to delete a user and all their associated data. ## Set Null -Finally, the set null option means that deleting a parent document will remove the relationship to the parent document for all of its related children. This can be useful if you want to retain the child's records but simply remove the relationship. +Finally, the set null option means that deleting a parent row will remove the relationship to the parent row for all of its related children. This can be useful if you want to retain the child's records but simply remove the relationship. Each of these options has its own use cases, and the choice ultimately depends on your specific application requirements. By providing these different options, Appwrite allows you to manage related data in a flexible and intuitive way, making it easier for you to build complex applications without worrying about data management. # Other Benefits -In addition to simplifying your collection management, database relationships also provide several other benefits. First, they can help to ensure data consistency and integrity by enforcing referential constraints between related collections. This means that you can prevent orphaned documents or other data inconsistencies that might arise if you were to manage the relationships between collections manually. Additionally, using relationships can also improve the performance of your requests and reduce the amount of code you need to write, since you can retrieve related data in a single request rather than having to fetch it separately. +In addition to simplifying your table management, database relationships also provide several other benefits. First, they can help to ensure data consistency and integrity by enforcing referential constraints between related tables. This means that you can prevent orphaned rows or other data inconsistencies that might arise if you were to manage the relationships between tables manually. Additionally, using relationships can also improve the performance of your requests and reduce the amount of code you need to write, since you can retrieve related data in a single request rather than having to fetch it separately. -Overall, database relationships are a powerful tool that simplifies your collection management and saves you time and effort. By easily linking your collections and retrieving related data, you can focus on developing your application's core features. Check out the [docs](/docs/databases-relationships) for more information. We encourage you to give it a try today and see how it can benefit your development process. +Overall, database relationships are a powerful tool that simplifies your table management and saves you time and effort. By easily linking your tables and retrieving related data, you can focus on developing your application's core features. Check out the [docs](/docs/databases-relationships) for more information. We encourage you to give it a try today and see how it can benefit your development process. - [Appwrite GitHub](https://github.com/appwrite) - [Appwrite Docs](/docs) diff --git a/src/routes/blog/post/track-document-order-with-sequence/+page.markdoc b/src/routes/blog/post/track-document-order-with-sequence/+page.markdoc index ed5033f37c..983b4fd3cd 100644 --- a/src/routes/blog/post/track-document-order-with-sequence/+page.markdoc +++ b/src/routes/blog/post/track-document-order-with-sequence/+page.markdoc @@ -1,21 +1,22 @@ --- layout: post -title: Using $sequence to track document order in Appwrite -description: Learn how to use Appwrite's $sequence attribute to track document order in your database. +title: Using $sequence to track row order in Appwrite +description: Learn how to use Appwrite's $sequence attribute to track row order in your database. date: 2025-07-16 cover: /images/blog/track-document-order-with-sequence/cover.png timeToRead: 6 author: ebenezer-don category: tutorial +lastUpdated: 2026-04-07 --- Some systems need to reflect the order in which actions happen. A ticketing system, for example, should assign "Ticket #41" before "Ticket #42". But in a user interface, it often makes sense to display the latest tickets first, so "Ticket #42" may appear above #41. -Relying on timestamps to get this right is often not enough. Two documents can be created almost simultaneously, and the sort order might vary. What is needed is a consistent, backend-assigned number that increases with each insert and cannot be modified or skipped. +Relying on timestamps to get this right is often not enough. Two rows can be created almost simultaneously, and the sort order might vary. What is needed is a consistent, backend-assigned number that increases with each insert and cannot be modified or skipped. -Appwrite's new `$sequence` attribute provides exactly this. Every time a document is added to a collection, the system assigns it a unique, auto-incrementing integer. This value reflects the insert history of the collection and can be used for sorting, display, filtering, and pagination. +Appwrite's new `$sequence` attribute provides exactly this. Every time a row is added to a table, the system assigns it a unique, auto-incrementing integer. This value reflects the insert history of the table and can be used for sorting, display, filtering, and pagination. -In this tutorial, we will build a simple web-based support ticket tracker using plain HTML and JavaScript. Each submitted ticket will be stored in Appwrite with a title and description, and each will receive a `$sequence` number automatically. We will use that number to display and order the tickets. +In this tutorial, we will build a simple web-based support ticket tracker using plain HTML and JavaScript. Each submitted ticket will be stored as a row in Appwrite with a title and description, and each will receive a `$sequence` number automatically. We will use that number to display and order the tickets. {% section #step-1 step=1 title="Set up your Appwrite project" %} @@ -25,35 +26,35 @@ Start by opening the [Appwrite console](https://cloud.appwrite.io/). If you do n Give it a name like **Support Tracker**. After creation, note the **Project ID**, as you will need it later in your frontend code. -## Create a database and collection +## Create a database and table Inside your project: - Go to the **Database** section - Create a new database named "Support DB" -- Inside that database, create a new collection named "Tickets" +- Inside that database, create a new table named "Tickets" -## Add document attributes +## Add columns -In the **Tickets** collection, define the following attributes: +In the **Tickets** table, define the following columns: -- `title` - type: `string`, required: `yes`, size: 256 -- `body` - type: `string`, required: `yes`, size: 1000 +- `title` - type: `varchar`, required: `yes`, size: 256 +- `body` - type: `text`, required: `yes` -Appwrite will automatically include system attributes such as `$id` and `$sequence`. You do not need to create `$sequence` manually. +Appwrite will automatically include system columns such as `$id` and `$sequence`. You do not need to create `$sequence` manually. -Confirm the schema once all attributes are added. +Confirm the schema once all columns are added. ## Set permissions for testing -For now, allow anyone to create and read documents: +For now, allow anyone to create and read rows: -- In the collection's **Settings**, under **Permissions** +- In the table's **Settings**, under **Permissions** - Add `role:any` to **Create** and **Read** You can change this later when adding [authentication](https://appwrite.io/docs/products/auth). -Also take note of your **Database ID** and **Collection ID**. You will use them in your frontend. +Also take note of your **Database ID** and **Table ID**. You will use them in your frontend. ## Configure web platform @@ -213,16 +214,16 @@ client .setEndpoint('') .setProject('') -const databases = new Appwrite.Databases(client) +const tablesDB = new Appwrite.TablesDB(client) const databaseId = '' -const collectionId = '' +const tableId = '' ``` Replace the placeholders with your actual values from the Appwrite console. -This code sets up the SDK so that you can call `databases.createDocument()` and `databases.listDocuments()` in the rest of the script. +This code sets up the SDK so that you can call `tablesDB.createRow()` and `tablesDB.listRows()` in the rest of the script. {% /section %} @@ -242,10 +243,10 @@ form.addEventListener('submit', async (e) => { if (!title || !body) return try { - await databases.createDocument({ + await tablesDB.createRow({ databaseId, - collectionId, - documentId: ID.unique(), + tableId, + rowId: Appwrite.ID.unique(), data: { title, body, @@ -261,7 +262,7 @@ form.addEventListener('submit', async (e) => { ``` -This code creates a new document in your collection when the form is submitted. It uses `'unique()'` to generate a unique ID. After submission, the form is reset and the list of tickets is reloaded to show the new entry. +This code creates a new row in your table when the form is submitted. It uses `Appwrite.ID.unique()` to generate a unique ID. After submission, the form is reset and the list of tickets is reloaded to show the new entry. {% /section %} @@ -272,9 +273,9 @@ Now add a function that retrieves tickets and shows them in order, newest first: ```javascript async function loadTickets() { try { - const response = await databases.listDocuments({ + const response = await tablesDB.listRows({ databaseId, - collectionId, + tableId, queries: [ Appwrite.Query.orderDesc('$sequence'), ] @@ -285,16 +286,16 @@ async function loadTickets() { ticketList.innerHTML = '' - if (response.documents.length === 0) { + if (response.rows.length === 0) { emptyState.style.display = 'block' ticketCount.textContent = '0 tickets' } else { emptyState.style.display = 'none' - ticketCount.textContent = `${response.documents.length} ticket${ - response.documents.length === 1 ? '' : 's' + ticketCount.textContent = `${response.rows.length} ticket${ + response.rows.length === 1 ? '' : 's' }` - response.documents.forEach((ticket, index) => { + response.rows.forEach((ticket, index) => { const ticketElement = document.createElement('div') ticketElement.className = 'card u-padding-24' ticketElement.innerHTML = ` @@ -334,8 +335,8 @@ You now have a working support ticket tracker that looks like this: ![Support tracker demo](/images/blog/track-document-order-with-sequence/support-tracker-demo.png) -- Each submitted ticket is stored as a document in Appwrite -- Every document receives a `$sequence` number, guaranteed to be unique and increasing +- Each submitted ticket is stored as a row in Appwrite +- Every row receives a `$sequence` number, guaranteed to be unique and increasing - The interface displays each ticket using that number - The ticket list is reliably sorted by creation order diff --git a/src/routes/blog/post/understand-data-queries/+page.markdoc b/src/routes/blog/post/understand-data-queries/+page.markdoc index beb9487804..8bc300dfdf 100644 --- a/src/routes/blog/post/understand-data-queries/+page.markdoc +++ b/src/routes/blog/post/understand-data-queries/+page.markdoc @@ -8,6 +8,7 @@ timeToRead: 6 author: aditya-oberai category: engineering, tutorial featured: false +lastUpdated: 2026-04-07 --- For any developer working with databases in any capacity, knowledge of queries is fundamental. These queries, executed through languages like SQL (Structured Query Language), are the backbone of database interaction, allowing you to retrieve, update, insert, or delete data. In this blog, we'll delve into the different types of data queries, shedding light on their functions and best practices. @@ -67,47 +68,53 @@ To remove records from a table, you use deletion queries. While powerful, they s A lot of developers today don’t perform raw SQL queries but prefer to use an ORM such as Prima or a managed database provider such as Appwrite. While these tools enable the same end goal, a managed service can provide an easy-to-use wrapper and helper methods that make these queries easier to write and don’t require you to have a deep knowledge of SQL syntax. Appwrite offers the aforementioned data queries as a part of our Database product, which you can discover in our [product documentation](/docs/products/databases). -One of the data retrieval APIs the Appwrite Database offers is a list documents API to get multiple documents from any collection. The endpoint also allows you to filter, sort, and paginate results, for which Appwrite provides a common set of syntax to build queries, which you can build manually or using our SDKs. With our latest release, we’re adding support for database operators such as `OR`, `AND`, and `CONTAINS` to allow further flexibility. +One of the data retrieval APIs the Appwrite Database offers is a list rows API to get multiple rows from any table. The endpoint also allows you to filter, sort, and paginate results, for which Appwrite provides a common set of syntax to build queries, which you can build manually or using our SDKs. With our latest release, we’re adding support for database operators such as `OR`, `AND`, and `CONTAINS` to allow further flexibility. - `AND` operation: This operator allows nesting queries in an AND condition. - `OR` operation: This operator allows nesting queries in an OR condition. - `CONTAINS` operation: The contains operator allows filtering by values that are contained in an array. ```client-web -import { Client, Databases, Query } from "appwrite"; +import { Client, TablesDB, Query } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') .setProject(''); -const databases = new Databases(client); +const tablesDB = new TablesDB(client); // OR operator example -const movieData1 = databases.listDocuments( - '', - '', - Query.or([ - Query.equal('title', ['Back To The Future', 'Top Gun']), - Query.greaterThan('year', 2017) - ]) -); +const movieData1 = tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ + Query.or([ + Query.equal('title', ['Back To The Future', 'Top Gun']), + Query.greaterThan('year', 2017) + ]) + ] +}); // AND operator example -const movieData2 = databases.listDocuments( - '', - '', - Query.and([ - Query.startsWith("title", "Once"), - Query.greaterThan('year', 1995) - ]) -); +const movieData2 = tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ + Query.and([ + Query.startsWith("title", "Once"), + Query.greaterThan('year', 1995) + ]) + ] +}); // CONTAINS operator example -const movieData3 = databases.listDocuments( - '', - '', - Query.contains('director', ["Christopher Nolan"]) -); +const movieData3 = tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ + Query.contains('director', ["Christopher Nolan"]) + ] +}); ``` Mastering the art of data querying is a continuous process. As a developer, your aim should be to write efficient, secure, and maintainable queries. Remember, the power of a database is harnessed through the effectiveness of its queries. diff --git a/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc b/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc index 733c344196..4cc2fa6e18 100644 --- a/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc +++ b/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc @@ -1,10 +1,10 @@ --- layout: article title: Timestamp overrides -description: Set custom $createdAt and $updatedAt timestamps for your documents when using server SDKs. +description: Set custom $createdAt and $updatedAt timestamps for your rows when using server SDKs. --- -When creating or updating documents, Appwrite automatically sets `$createdAt` and `$updatedAt` timestamps. However, there are scenarios where you might need to set these timestamps manually, such as when migrating data from another system or backfilling historical records. +When creating or updating rows, Appwrite automatically sets `$createdAt` and `$updatedAt` timestamps. However, there are scenarios where you might need to set these timestamps manually, such as when migrating data from another system or backfilling historical records. {% info title="Server SDKs required" %} To manually set `$createdAt` and `$updatedAt`, you must use a **server SDK** with an **API key**. These attributes can be passed inside the `data` parameter on any of the create, update, or upsert routes (single or bulk). @@ -12,13 +12,13 @@ To manually set `$createdAt` and `$updatedAt`, you must use a **server SDK** wit # Setting custom timestamps {% #setting-custom-timestamps %} -You can override a document's timestamps by providing ISO 8601 strings (for example, `2025-08-10T12:34:56.000Z`) in the `data` payload. If these attributes are not provided, Appwrite will set them automatically. +You can override a row's timestamps by providing ISO 8601 strings (for example, `2025-08-10T12:34:56.000Z`) in the `data` payload. If these attributes are not provided, Appwrite will set them automatically. -Custom timestamps work with all document operations: create, update, upsert, and their bulk variants. +Custom timestamps work with all row operations: create, update, upsert, and their bulk variants. -## Single document operations {% #single-document-operations %} +## Single row operations {% #single-row-operations %} -When working with individual documents, you can set custom timestamps during create, update, and upsert operations. +When working with individual rows, you can set custom timestamps during create, update, and upsert operations. ### Create with custom timestamps {% #create-custom %} @@ -31,36 +31,36 @@ const client = new sdk.Client() .setProject('') .setKey(''); -const databases = new sdk.Databases(client); +const tablesDB = new sdk.TablesDB(client); -await databases.createDocument( - '', - '', - sdk.ID.unique(), - { +await tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: sdk.ID.unique(), + data: { '$createdAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), '$updatedAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), // ...your attributes } -); +}); ``` ```server-php use Appwrite\Client; use Appwrite\ID; -use Appwrite\Services\Databases; +use Appwrite\Services\TablesDB; $client = (new Client()) ->setEndpoint('https://.cloud.appwrite.io/v1') ->setProject('') ->setKey(''); -$databases = new Databases($client); +$tablesDB = new TablesDB($client); -$databases->createDocument( +$tablesDB->createRow( databaseId: '', - collectionId: '', - documentId: '', - [ + tableId: '', + rowId: '', + data: [ '$createdAt' => (new DateTime(''))->format(DATE_ATOM), '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), // ...your attributes @@ -76,7 +76,7 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -85,10 +85,10 @@ let createdAt = isoFormatter.string(from: customDate) let updatedAt = isoFormatter.string(from: customDate) do { - let created = try await databases.createDocument( + let created = try await tablesDB.createRow( databaseId: "", - collectionId: "", - documentId: "", + tableId: "", + rowId: "", data: [ "$createdAt": createdAt, "$updatedAt": updatedAt, @@ -102,7 +102,7 @@ do { ``` ```server-python from appwrite.client import Client -from appwrite.services.databases import Databases +from appwrite.services.tables_db import TablesDB from appwrite.id import ID from datetime import datetime, timezone @@ -111,14 +111,14 @@ client.set_endpoint('https://.cloud.appwrite.io/v1') client.set_project('') client.set_key('') -databases = Databases(client) +tablesDB = TablesDB(client) iso = datetime(2025, 8, 10, 12, 34, 56, tzinfo=timezone.utc).isoformat() -databases.create_document( +tablesDB.create_row( database_id='', - collection_id='', - document_id=ID.unique(), + table_id='', + row_id=ID.unique(), data={ '$createdAt': iso, '$updatedAt': iso, @@ -137,14 +137,14 @@ client = Client.new .set_project('') .set_key('') -databases = Databases.new(client) +tablesDB = TablesDB.new(client) custom_date = Time.parse('2025-08-10T12:34:56.000Z').iso8601 -databases.create_document( +tablesDB.create_row( database_id: '', - collection_id: '', - document_id: ID.unique(), + table_id: '', + row_id: ID.unique(), data: { '$createdAt' => custom_date, '$updatedAt' => custom_date, @@ -162,14 +162,14 @@ Client client = new Client() .SetProject("") .SetKey(""); -Databases databases = new Databases(client); +TablesDB tablesDB = new TablesDB(client); string customDate = DateTimeOffset.Parse("2025-08-10T12:34:56.000Z").ToString("O"); -await databases.CreateDocument( +await tablesDB.CreateRow( databaseId: "", - collectionId: "", - documentId: ID.Unique(), + tableId: "", + rowId: ID.Unique(), data: new Dictionary { ["$createdAt"] = customDate, @@ -186,14 +186,14 @@ Client client = Client() .setProject('') .setKey(''); -Databases databases = Databases(client); +TablesDB tablesDB = TablesDB(client); String customDate = DateTime.parse('2025-08-10T12:34:56.000Z').toIso8601String(); -await databases.createDocument( +await tablesDB.createRow( databaseId: '', - collectionId: '', - documentId: ID.unique(), + tableId: '', + rowId: ID.unique(), data: { '\$createdAt': customDate, '\$updatedAt': customDate, @@ -205,26 +205,26 @@ await databases.createDocument( ### Update with custom timestamps {% #update-custom %} -When updating documents, you can also set a custom `$updatedAt` timestamp: +When updating rows, you can also set a custom `$updatedAt` timestamp: {% multicode %} ```server-nodejs -await databases.updateDocument( - '', - '', - '', - { +await tablesDB.updateRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { '$updatedAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), // ...your attributes } -); +}); ``` ```server-php -$databases->updateDocument( +$tablesDB->updateRow( databaseId: '', - collectionId: '', - documentId: '', - [ + tableId: '', + rowId: '', + data: [ '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), // ...your attributes ] @@ -233,10 +233,10 @@ $databases->updateDocument( ```server-python from datetime import datetime, timezone -databases.update_document( +tablesDB.update_row( database_id='', - collection_id='', - document_id='', + table_id='', + row_id='', data={ '$updatedAt': datetime(2025, 8, 10, 12, 34, 56, tzinfo=timezone.utc).isoformat(), # ...your attributes @@ -252,17 +252,17 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] let updatedAt = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) do { - let updated = try await databases.updateDocument( + let updated = try await tablesDB.updateRow( databaseId: "", - collectionId: "", - documentId: "", + tableId: "", + rowId: "", data: [ "$updatedAt": updatedAt, // ...your attributes @@ -283,14 +283,14 @@ client = Client.new .set_project('') .set_key('') -databases = Databases.new(client) +tablesDB = TablesDB.new(client) custom_date = Time.parse('').iso8601 -databases.update_document( +tablesDB.update_row( database_id: '', - collection_id: '', - document_id: '', + table_id: '', + row_id: '', data: { '$updatedAt' => custom_date, # ...your attributes @@ -307,14 +307,14 @@ Client client = new Client() .SetProject("") .SetKey(""); -Databases databases = new Databases(client); +TablesDB tablesDB = new TablesDB(client); string customDate = DateTimeOffset.Parse("").ToString("O"); -await databases.UpdateDocument( +await tablesDB.UpdateRow( databaseId: "", - collectionId: "", - documentId: "", + tableId: "", + rowId: "", data: new Dictionary { ["$updatedAt"] = customDate, @@ -330,14 +330,14 @@ Client client = Client() .setProject('') .setKey(''); -Databases databases = Databases(client); +TablesDB tablesDB = TablesDB(client); String customDate = DateTime.parse('').toIso8601String(); -await databases.updateDocument( +await tablesDB.updateRow( databaseId: '', - collectionId: '', - documentId: '', + tableId: '', + rowId: '', data: { '\$updatedAt': customDate, // ...your attributes @@ -348,16 +348,16 @@ await databases.updateDocument( ## Bulk operations {% #bulk-operations %} -Custom timestamps also work with bulk operations, allowing you to set different timestamps for each document in the batch: +Custom timestamps also work with bulk operations, allowing you to set different timestamps for each row in the batch: ### Bulk create {% #bulk-create %} {% multicode %} ```server-nodejs -await databases.createDocuments( - '', - '', - [ +await tablesDB.createRows({ + databaseId: '', + tableId: '', + rows: [ { '$id': sdk.ID.unique(), '$createdAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), @@ -371,13 +371,13 @@ await databases.createDocuments( // ...your attributes } ] -); +}); ``` ```server-python -databases.create_documents( +tablesDB.create_rows( database_id='', - collection_id='', - documents=[ + table_id='', + rows=[ { '$id': ID.unique(), '$createdAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), @@ -396,19 +396,19 @@ databases.create_documents( ```server-php use Appwrite\Client; use Appwrite\ID; -use Appwrite\Services\Databases; +use Appwrite\Services\TablesDB; $client = (new Client()) ->setEndpoint('https://.cloud.appwrite.io/v1') ->setProject('') ->setKey(''); -$databases = new Databases($client); +$tablesDB = new TablesDB($client); -$databases->createDocuments( +$tablesDB->createRows( databaseId: '', - collectionId: '', - documents: [ + tableId: '', + rows: [ [ '$id' => ID::unique(), '$createdAt' => (new DateTime(''))->format(DATE_ATOM), @@ -433,7 +433,7 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -442,10 +442,10 @@ let first = isoFormatter.string(from: isoFormatter.date(from: "") ? let second = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) do { - let bulkCreated = try await databases.createDocuments( + let bulkCreated = try await tablesDB.createRows( databaseId: "", - collectionId: "", - documents: [ + tableId: "", + rows: [ [ "$id": ID.unique(), "$createdAt": first, @@ -475,15 +475,15 @@ client = Client.new .set_project('') .set_key('') -databases = Databases.new(client) +tablesDB = TablesDB.new(client) first = Time.parse('').iso8601 second = Time.parse('').iso8601 -databases.create_documents( +tablesDB.create_rows( database_id: '', - collection_id: '', - documents: [ + table_id: '', + rows: [ { '$id' => ID.unique(), '$createdAt' => first, @@ -509,15 +509,15 @@ Client client = new Client() .SetProject("") .SetKey(""); -Databases databases = new Databases(client); +TablesDB tablesDB = new TablesDB(client); string first = DateTimeOffset.Parse("").ToString("O"); string second = DateTimeOffset.Parse("").ToString("O"); -await databases.CreateDocuments( +await tablesDB.CreateRows( databaseId: "", - collectionId: "", - documents: new List + tableId: "", + rows: new List { new Dictionary { @@ -544,15 +544,15 @@ Client client = Client() .setProject('') .setKey(''); -Databases databases = Databases(client); +TablesDB tablesDB = TablesDB(client); String first = DateTime.parse('').toIso8601String(); String second = DateTime.parse('').toIso8601String(); -await databases.createDocuments( +await tablesDB.createRows( databaseId: '', - collectionId: '', - documents: [ + tableId: '', + rows: [ { '\$id': ID.unique(), '\$createdAt': first, @@ -574,26 +574,26 @@ await databases.createDocuments( {% multicode %} ```server-nodejs -await databases.upsertDocuments( - '', - '', - [ +await tablesDB.upsertRows({ + databaseId: '', + tableId: '', + rows: [ { - '$id': '', + '$id': '', '$createdAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), '$updatedAt': new Date('2025-01-01T00:00:00.000Z').toISOString(), // ...your attributes } ] -); +}); ``` ```server-python -databases.upsert_documents( +tablesDB.upsert_rows( database_id='', - collection_id='', - documents=[ + table_id='', + rows=[ { - '$id': '', + '$id': '', '$createdAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), '$updatedAt': datetime(2025, 1, 1, tzinfo=timezone.utc).isoformat(), # ...your attributes @@ -604,21 +604,21 @@ databases.upsert_documents( ```server-php use Appwrite\Client; use Appwrite\ID; -use Appwrite\Services\Databases; +use Appwrite\Services\TablesDB; $client = (new Client()) ->setEndpoint('https://.cloud.appwrite.io/v1') ->setProject('') ->setKey(''); -$databases = new Databases($client); +$tablesDB = new TablesDB($client); -$databases->upsertDocuments( +$tablesDB->upsertRows( databaseId: '', - collectionId: '', - documents: [ + tableId: '', + rows: [ [ - '$id' => '', + '$id' => '', '$createdAt' => (new DateTime(''))->format(DATE_ATOM), '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), // ...your attributes @@ -635,7 +635,7 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -643,12 +643,12 @@ let createdAt = isoFormatter.string(from: isoFormatter.date(from: " let updatedAt = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) do { - let bulkUpserted = try await databases.upsertDocuments( + let bulkUpserted = try await tablesDB.upsertRows( databaseId: "", - collectionId: "", - documents: [ + tableId: "", + rows: [ [ - "$id": "", + "$id": "", "$createdAt": createdAt, "$updatedAt": updatedAt, // ...your attributes @@ -671,16 +671,16 @@ client = Client.new .set_project('') .set_key('') -databases = Databases.new(client) +tablesDB = TablesDB.new(client) custom_date = Time.parse('').iso8601 -databases.upsert_documents( +tablesDB.upsert_rows( database_id: '', - collection_id: '', - documents: [ + table_id: '', + rows: [ { - '$id' => '', + '$id' => '', '$createdAt' => custom_date, '$updatedAt' => custom_date, # ...your attributes @@ -698,19 +698,19 @@ Client client = new Client() .SetProject("") .SetKey(""); -Databases databases = new Databases(client); +TablesDB tablesDB = new TablesDB(client); string createdAt = DateTimeOffset.Parse("").ToString("O"); string updatedAt = DateTimeOffset.Parse("").ToString("O"); -await databases.UpsertDocuments( +await tablesDB.UpsertRows( databaseId: "", - collectionId: "", - documents: new List + tableId: "", + rows: new List { new Dictionary { - ["$id"] = "", + ["$id"] = "", ["$createdAt"] = createdAt, ["$updatedAt"] = updatedAt, // ...your attributes @@ -726,17 +726,17 @@ Client client = Client() .setProject('') .setKey(''); -Databases databases = Databases(client); +TablesDB tablesDB = TablesDB(client); String createdAt = DateTime.parse('').toIso8601String(); String updatedAt = DateTime.parse('').toIso8601String(); -await databases.upsertDocuments( +await tablesDB.upsertRows( databaseId: '', - collectionId: '', - documents: [ + tableId: '', + rows: [ { - '\$id': '', + '\$id': '', '\$createdAt': createdAt, '\$updatedAt': updatedAt, // ...your attributes @@ -756,24 +756,24 @@ creation and modification times: {% multicode %} ```server-nodejs -await databases.createDocument( - '', - 'blog_posts', - sdk.ID.unique(), - { +await tablesDB.createRow({ + databaseId: '', + tableId: 'blog_posts', + rowId: sdk.ID.unique(), + data: { '$createdAt': '', '$updatedAt': '', title: '', content: '<CONTENT>' } -) +}); ``` ```server-php -$databases->createDocument( +$tablesDB->createRow( databaseId: '<DATABASE_ID>', - collectionId: 'blog_posts', - documentId: ID::unique(), - [ + tableId: 'blog_posts', + rowId: ID::unique(), + data: [ '$createdAt' => '<ORIGINAL_CREATED_AT_ISO>', '$updatedAt' => '<LAST_MODIFIED_ISO>', 'title' => '<TITLE>', @@ -782,10 +782,10 @@ $databases->createDocument( ); ``` ```server-swift -let _ = try await databases.createDocument( +let _ = try await tablesDB.createRow( databaseId: "<DATABASE_ID>", - collectionId: "blog_posts", - documentId: ID.unique(), + tableId: "blog_posts", + rowId: ID.unique(), data: [ "$createdAt": "<ORIGINAL_CREATED_AT_ISO>", "$updatedAt": "<LAST_MODIFIED_ISO>", @@ -795,10 +795,10 @@ let _ = try await databases.createDocument( ) ``` ```server-python -databases.create_document( +tablesDB.create_row( database_id='<DATABASE_ID>', - collection_id='blog_posts', - document_id=ID.unique(), + table_id='blog_posts', + row_id=ID.unique(), data={ '$createdAt': '<ORIGINAL_CREATED_AT_ISO>', '$updatedAt': '<LAST_MODIFIED_ISO>', @@ -808,10 +808,10 @@ databases.create_document( ) ``` ```server-ruby -databases.create_document( +tablesDB.create_row( database_id: '<DATABASE_ID>', - collection_id: 'blog_posts', - document_id: ID.unique(), + table_id: 'blog_posts', + row_id: ID.unique(), data: { '$createdAt' => '<ORIGINAL_CREATED_AT_ISO>', '$updatedAt' => '<LAST_MODIFIED_ISO>', @@ -821,10 +821,10 @@ databases.create_document( ) ``` ```server-dotnet -await databases.CreateDocument( +await tablesDB.CreateRow( databaseId: "<DATABASE_ID>", - collectionId: "blog_posts", - documentId: ID.Unique(), + tableId: "blog_posts", + rowId: ID.Unique(), data: new Dictionary<string, object> { ["$createdAt"] = "<ORIGINAL_CREATED_AT_ISO>", @@ -835,10 +835,10 @@ await databases.CreateDocument( ); ``` ```server-dart -await databases.createDocument( +await tablesDB.createRow( databaseId: '<DATABASE_ID>', - collectionId: 'blog_posts', - documentId: ID.unique(), + tableId: 'blog_posts', + rowId: ID.unique(), data: { '\$createdAt': '<ORIGINAL_CREATED_AT_ISO>', '\$updatedAt': '<LAST_MODIFIED_ISO>', @@ -854,24 +854,24 @@ For historical data entry or when creating records that represent past events: {% multicode %} ```server-nodejs -await databases.createDocument( - '<DATABASE_ID>', - 'transactions', - sdk.ID.unique(), - { +await tablesDB.createRow({ + databaseId: '<DATABASE_ID>', + tableId: 'transactions', + rowId: sdk.ID.unique(), + data: { '$createdAt': '2023-12-31T23:59:59.000Z', '$updatedAt': '2023-12-31T23:59:59.000Z', amount: 1000, type: 'year-end-bonus' } -) +}); ``` ```server-php -$databases->createDocument( +$tablesDB->createRow( databaseId: '<DATABASE_ID>', - collectionId: 'transactions', - documentId: ID::unique(), - [ + tableId: 'transactions', + rowId: ID::unique(), + data: [ '$createdAt' => '2023-12-31T23:59:59.000Z', '$updatedAt' => '2023-12-31T23:59:59.000Z', 'amount' => 1000, @@ -880,10 +880,10 @@ $databases->createDocument( ); ``` ```server-swift -let _ = try await databases.createDocument( +let _ = try await tablesDB.createRow( databaseId: "<DATABASE_ID>", - collectionId: "transactions", - documentId: ID.unique(), + tableId: "transactions", + rowId: ID.unique(), data: [ "$createdAt": "2023-12-31T23:59:59.000Z", "$updatedAt": "2023-12-31T23:59:59.000Z", @@ -893,10 +893,10 @@ let _ = try await databases.createDocument( ) ``` ```server-python -databases.create_document( +tablesDB.create_row( database_id='<DATABASE_ID>', - collection_id='transactions', - document_id=ID.unique(), + table_id='transactions', + row_id=ID.unique(), data={ '$createdAt': '2023-12-31T23:59:59.000Z', '$updatedAt': '2023-12-31T23:59:59.000Z', @@ -906,10 +906,10 @@ databases.create_document( ) ``` ```server-ruby -databases.create_document( +tablesDB.create_row( database_id: '<DATABASE_ID>', - collection_id: 'transactions', - document_id: ID.unique(), + table_id: 'transactions', + row_id: ID.unique(), data: { '$createdAt' => '2023-12-31T23:59:59.000Z', '$updatedAt' => '2023-12-31T23:59:59.000Z', @@ -919,10 +919,10 @@ databases.create_document( ) ``` ```server-dotnet -await databases.CreateDocument( +await tablesDB.CreateRow( databaseId: "<DATABASE_ID>", - collectionId: "transactions", - documentId: ID.Unique(), + tableId: "transactions", + rowId: ID.Unique(), data: new Dictionary<string, object> { ["$createdAt"] = "2023-12-31T23:59:59.000Z", @@ -933,10 +933,10 @@ await databases.CreateDocument( ); ``` ```server-dart -await databases.createDocument( +await tablesDB.createRow( databaseId: '<DATABASE_ID>', - collectionId: 'transactions', - documentId: ID.unique(), + tableId: 'transactions', + rowId: ID.unique(), data: { '\$createdAt': '2023-12-31T23:59:59.000Z', '\$updatedAt': '2023-12-31T23:59:59.000Z', @@ -952,32 +952,32 @@ When synchronizing data between systems while maintaining timestamp consistency: {% multicode %} ```server-nodejs -await databases.upsertDocument( - '<DATABASE_ID>', - 'users', - '<DOCUMENT_ID_OR_NEW_ID>', - { +await tablesDB.upsertRow({ + databaseId: '<DATABASE_ID>', + tableId: 'users', + rowId: '<ROW_ID_OR_NEW_ID>', + data: { '$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', profile: '<PROFILE_DATA>' } -) +}); ``` ```server-php -$databases->upsertDocument( +$tablesDB->upsertRow( databaseId: '<DATABASE_ID>', - collectionId: 'users', - documentId: '<DOCUMENT_ID_OR_NEW_ID>', - [ + tableId: 'users', + rowId: '<ROW_ID_OR_NEW_ID>', + data: [ '$updatedAt' => '<EXTERNAL_LAST_MODIFIED_ISO>', 'profile' => '<PROFILE_DATA>' ] ); ``` ```server-swift -let _ = try await databases.upsertDocument( +let _ = try await tablesDB.upsertRow( databaseId: "<DATABASE_ID>", - collectionId: "users", - documentId: "<DOCUMENT_ID_OR_NEW_ID>", + tableId: "users", + rowId: "<ROW_ID_OR_NEW_ID>", data: [ "$updatedAt": "<EXTERNAL_LAST_MODIFIED_ISO>", "profile": "<PROFILE_DATA>" @@ -985,10 +985,10 @@ let _ = try await databases.upsertDocument( ) ``` ```server-python -databases.upsert_document( +tablesDB.upsert_row( database_id='<DATABASE_ID>', - collection_id='users', - document_id='<DOCUMENT_ID_OR_NEW_ID>', + table_id='users', + row_id='<ROW_ID_OR_NEW_ID>', data={ '$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', 'profile': '<PROFILE_DATA>' @@ -996,10 +996,10 @@ databases.upsert_document( ) ``` ```server-ruby -databases.upsert_document( +tablesDB.upsert_row( database_id: '<DATABASE_ID>', - collection_id: 'users', - document_id: '<DOCUMENT_ID_OR_NEW_ID>', + table_id: 'users', + row_id: '<ROW_ID_OR_NEW_ID>', data: { '$updatedAt' => '<EXTERNAL_LAST_MODIFIED_ISO>', 'profile' => '<PROFILE_DATA>' @@ -1007,10 +1007,10 @@ databases.upsert_document( ) ``` ```server-dotnet -await databases.UpsertDocument( +await tablesDB.UpsertRow( databaseId: "<DATABASE_ID>", - collectionId: "users", - documentId: "<DOCUMENT_ID_OR_NEW_ID>", + tableId: "users", + rowId: "<ROW_ID_OR_NEW_ID>", data: new Dictionary<string, object> { ["$updatedAt"] = "<EXTERNAL_LAST_MODIFIED_ISO>", @@ -1019,10 +1019,10 @@ await databases.UpsertDocument( ); ``` ```server-dart -await databases.upsertDocument( +await tablesDB.upsertRow( databaseId: '<DATABASE_ID>', - collectionId: 'users', - documentId: '<DOCUMENT_ID_OR_NEW_ID>', + tableId: 'users', + rowId: '<ROW_ID_OR_NEW_ID>', data: { '\$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', 'profile': '<PROFILE_DATA>' @@ -1035,4 +1035,3 @@ await databases.upsertDocument( - Values must be valid ISO 8601 date-time strings (UTC recommended). Using `toISOString()` (JavaScript) or `datetime.isoformat()` (Python) is a good default. - You can set either or both attributes as needed. If omitted, Appwrite sets them automatically. {% /info %} - diff --git a/static/images/blog/appwrite-realtime-with-flutter/1.png b/static/images/blog/appwrite-realtime-with-flutter/1.png index b9769b2e46..ba46f70926 100644 Binary files a/static/images/blog/appwrite-realtime-with-flutter/1.png and b/static/images/blog/appwrite-realtime-with-flutter/1.png differ diff --git a/static/images/blog/appwrite-realtime-with-flutter/2.png b/static/images/blog/appwrite-realtime-with-flutter/2.png index cf4ebed92d..47d94b2861 100644 Binary files a/static/images/blog/appwrite-realtime-with-flutter/2.png and b/static/images/blog/appwrite-realtime-with-flutter/2.png differ diff --git a/static/images/blog/appwrite-realtime-with-flutter/3.png b/static/images/blog/appwrite-realtime-with-flutter/3.png index dd73233ccd..6beeff156f 100644 Binary files a/static/images/blog/appwrite-realtime-with-flutter/3.png and b/static/images/blog/appwrite-realtime-with-flutter/3.png differ diff --git a/static/images/blog/appwrite-realtime-with-flutter/4.png b/static/images/blog/appwrite-realtime-with-flutter/4.png index baf8986b1c..c876513158 100644 Binary files a/static/images/blog/appwrite-realtime-with-flutter/4.png and b/static/images/blog/appwrite-realtime-with-flutter/4.png differ diff --git a/static/images/blog/appwrite-realtime-with-flutter/5.png b/static/images/blog/appwrite-realtime-with-flutter/5.png index 32f2063e62..3bc91e3ba3 100644 Binary files a/static/images/blog/appwrite-realtime-with-flutter/5.png and b/static/images/blog/appwrite-realtime-with-flutter/5.png differ diff --git a/static/images/blog/build-fullstack-svelte-appwrite/permissions-row-security.png b/static/images/blog/build-fullstack-svelte-appwrite/permissions-row-security.png new file mode 100644 index 0000000000..66b68a1172 Binary files /dev/null and b/static/images/blog/build-fullstack-svelte-appwrite/permissions-row-security.png differ