diff --git a/src/partials/auth-security.md b/src/partials/auth-security.md index 25a18f44e6d..354f1a685e1 100644 --- a/src/partials/auth-security.md +++ b/src/partials/auth-security.md @@ -23,7 +23,7 @@ You can change the session limit in the **Security** tab of the Auth Service in Security is very important to protect users' data and privacy. Appwrite uses a [permissions model](/docs/advanced/platform/permissions) coupled with user sessions to ensure users need correct permissions to access resources. -With all Appwrite services, including databases and storage, access is granted at the collection, bucket, document, or file level. +With all Appwrite services, including databases and storage, access is granted at the table, bucket, row, or file level. These permissions are enforced for client SDKs and server SDKs when using JWT, but are ignored when using a server SDK with an API key. # Password history {% #password-history %} diff --git a/src/routes/blog/post/25-startup-ideas-you-can-build-with-vibe-coding/+page.markdoc b/src/routes/blog/post/25-startup-ideas-you-can-build-with-vibe-coding/+page.markdoc index acbef39aa77..bef4618f3d3 100644 --- a/src/routes/blog/post/25-startup-ideas-you-can-build-with-vibe-coding/+page.markdoc +++ b/src/routes/blog/post/25-startup-ideas-you-can-build-with-vibe-coding/+page.markdoc @@ -150,7 +150,7 @@ A backend that ships all seven under one model is the difference between a weeke [Appwrite Cloud](https://cloud.appwrite.io) is shaped for this exact list. - [Auth](/docs/products/auth) handles email and password, OAuth, magic URL, email and phone OTP, MFA, sessions, and [Teams](/docs/products/auth/teams) for the multi-user ideas. -- [Databases](/docs/products/databases) cover tables, typed columns, rows, relationships, queries, transactions, and per-document permissions. +- [Databases](/docs/products/databases) cover tables, typed columns, rows, relationships, queries, transactions, and row-level permissions. - [Storage](/docs/products/storage) gives you buckets with size and MIME limits, antivirus, encryption, image transformations, and signed URLs. - [Functions](/docs/products/functions) cover server-side logic in Node, Python, Go, PHP, Dart, Ruby, Deno, Rust, and more, with execution logs, env vars, CRON triggers, and event triggers. - [Sites](/docs/products/sites) hosts the frontend with custom domains, env vars, rollbacks, and build logs. diff --git a/src/routes/blog/post/3-things-you-can-build-with-go-runtime/+page.markdoc b/src/routes/blog/post/3-things-you-can-build-with-go-runtime/+page.markdoc index 488dcb01931..80801a7004d 100644 --- a/src/routes/blog/post/3-things-you-can-build-with-go-runtime/+page.markdoc +++ b/src/routes/blog/post/3-things-you-can-build-with-go-runtime/+page.markdoc @@ -16,7 +16,7 @@ faqs: - question: "How does the Go runtime compare to Node.js or Python in Appwrite Functions?" answer: "In Appwrite's internal benchmarks, the Go runtime had up to 3x faster cold starts and roughly 5x lower memory usage than several interpreted runtimes including Node.js, Deno, Ruby, and Python. Build times can be slower because Go is compiled, but the runtime performance gains usually pay off for production workloads." - question: "Can I trigger Go Appwrite Functions from events?" - answer: "Yes. Like every Appwrite Function, a Go function can be triggered by HTTP requests, CRON schedules, or events emitted by Appwrite products (user creation, document changes, file uploads, etc.). The event payload is delivered to your handler so you can route on it." + answer: "Yes. Like every Appwrite Function, a Go function can be triggered by HTTP requests, CRON schedules, or events emitted by Appwrite products (user creation, row changes, file uploads, etc.). The event payload is delivered to your handler so you can route on it." - question: "How do I deploy a Go Appwrite Function via CI/CD?" answer: "Connect your GitHub repository to your function in the Appwrite console and pick a branch. Every push to that branch triggers a new build and deployment automatically. You can also deploy from the Appwrite CLI by running `appwrite push functions` from your local project." - question: "Can I develop Appwrite Go functions locally before deploying?" @@ -38,11 +38,11 @@ Our Go runtime (just like our other runtimes) has been developed by our team and ## Event-driven nature -Appwrite Functions can be executed by various types of events, which allows you to integrate them into your applications in many different ways. These events include all HTTP actions (to consume like a REST API), CRON schedules (to run them on set time periods), and any events across the various Appwrite products in your project (for example, user creation, document deletion, or file upload). +Appwrite Functions can be executed by various types of events, which allows you to integrate them into your applications in many different ways. These events include all HTTP actions (to consume like a REST API), CRON schedules (to run them on set time periods), and any events across the various Appwrite products in your project (for example, user creation, row deletion, or file upload). ## Global environment variables -Aside from environment variables at the function level, Appwrite also allows you to environment variables at the project level so that they can be shared across multiple functions in a single project. +Aside from environment variables at the function level, Appwrite also allows you to environment variables at the project level so that they can be shared across multiple functions in a single project. ## Permissions system @@ -64,7 +64,7 @@ Once your function is set up, we can try some examples: ## Example 1: AI Chatbot using GPT-3.5 -The first example is a simple chatbot function that accepts a prompt in the request body and returns an answer in the response from the ChatGPT API. +The first example is a simple chatbot function that accepts a prompt in the request body and returns an answer in the response from the ChatGPT API. To do this, we must first add the `go-openai` dependency to our project’s `mod` file. @@ -151,7 +151,7 @@ You can then deploy this function using the `appwrite deploy function` command. ## Example 2: HTML Resume -The second example is an online HTML-based resume that you can deliver online through the function. +The second example is an online HTML-based resume that you can deliver online through the function. For this, the first thing we do is create a `static` directory in the function folder and add a file, `resume.html` with the contents of our resume. You can [copy our template](https://github.com/appwrite-community/go-function-examples/blob/main/functions/go-resume/static/resume.html) if you’d like. @@ -191,7 +191,7 @@ You can then deploy this function using the `appwrite deploy function` command. ## Example 3: URL Shortener -The third example is a personal URL shortener that stores your shortened URL path and long URL in an Appwrite Database and redirects the consumer to the appropriate long URL on pinging the shortened URL. +The third example is a personal URL shortener that stores your shortened URL path and long URL in an Appwrite database table and redirects the consumer to the appropriate long URL on pinging the shortened URL. To build this function, create a `services` directory in the function folder and add a file `setup.go`. Here, we will add the necessary functions to initialize our Appwrite database. @@ -199,62 +199,62 @@ To build this function, create a `services` directory in the function folder and package services import ( - "github.com/appwrite/sdk-for-go/databases" "github.com/appwrite/sdk-for-go/permission" + "github.com/appwrite/sdk-for-go/tablesdb" "github.com/open-runtimes/types-for-go/v4/openruntimes" ) -func DoesDatabaseExist(dbs databases.Databases, dbId string) bool { - _, err := dbs.Get(dbId) +func DoesDatabaseExist(tablesDB tablesdb.TablesDB, dbId string) bool { + _, err := tablesDB.Get(dbId) if err != nil { return false } return true } -func DoesCollectionExist(dbs databases.Databases, dbId string, collId string) bool { - _, err := dbs.GetCollection(dbId, collId) +func DoesTableExist(tablesDB tablesdb.TablesDB, dbId string, tableId string) bool { + _, err := tablesDB.GetTable(dbId, tableId) if err != nil { return false } return true } -func DoesAttributeExist(dbs databases.Databases, dbId string, collId string, attribId string) bool { - _, err := dbs.GetAttribute(dbId, collId, attribId) +func DoesColumnExist(tablesDB tablesdb.TablesDB, dbId string, tableId string, columnId string) bool { + _, err := tablesDB.GetColumn(dbId, tableId, columnId) if err != nil { return false } return true } -func InitialiseDatabase(Context openruntimes.Context, dbs databases.Databases, dbId string, collId string) { - doesDbExist := DoesDatabaseExist(dbs, dbId) +func InitialiseDatabase(Context openruntimes.Context, tablesDB tablesdb.TablesDB, dbId string, tableId string) { + doesDbExist := DoesDatabaseExist(tablesDB, dbId) if !doesDbExist { - dbs.Create( + tablesDB.Create( dbId, "URL Databases", ) } - doesCollExist := DoesCollectionExist(dbs, dbId, collId) - if !doesCollExist { - dbs.CreateCollection( + doesTableExist := DoesTableExist(tablesDB, dbId, tableId) + if !doesTableExist { + tablesDB.CreateTable( dbId, - collId, + tableId, "URLs", - dbs.WithCreateCollectionPermissions([]string{permission.Read("any")}), + tablesDB.WithCreateTablePermissions([]string{permission.Read("any")}), ) } - doesAttribExist := DoesAttributeExist(dbs, dbId, collId, "longUrl") - if !doesAttribExist { - dbs.CreateUrlAttribute( + doesColumnExist := DoesColumnExist(tablesDB, dbId, tableId, "longUrl") + if !doesColumnExist { + tablesDB.CreateUrlColumn( dbId, - collId, + tableId, "longUrl", true, - dbs.WithCreateUrlAttributeArray(false), + tablesDB.WithCreateUrlColumnArray(false), ) } } @@ -289,12 +289,12 @@ func Main(Context openruntimes.Context) openruntimes.Response { appwrite.WithKey(Context.Req.Headers["x-appwrite-key"]), ) - databases := appwrite.NewDatabases(client) + tablesDB := appwrite.NewTablesDB(client) dbId := "urlDatabase" - collId := "urlCollection" + tableId := "urlTable" - services.InitialiseDatabase(Context, *databases, dbId, collId) + services.InitialiseDatabase(Context, *tablesDB, dbId, tableId) if Context.Req.Method == "POST" { var requestBody RequestBody @@ -307,9 +307,9 @@ func Main(Context openruntimes.Context) openruntimes.Response { }, Context.Res.WithStatusCode(400)) } - _, err = databases.CreateDocument( + _, err = tablesDB.CreateRow( dbId, - collId, + tableId, requestBody.ShortId, map[string]interface{}{ "longUrl": requestBody.LongUrl, @@ -339,7 +339,7 @@ func Main(Context openruntimes.Context) openruntimes.Response { shortId := path[1:] - document, err := databases.GetDocument(dbId, collId, shortId) + row, err := tablesDB.GetRow(dbId, tableId, shortId) if err != nil { Context.Error(err) @@ -347,7 +347,7 @@ func Main(Context openruntimes.Context) openruntimes.Response { } var responseBody ResponseBody - document.Decode(&responseBody) + row.Decode(&responseBody) return Context.Res.Redirect(responseBody.LongUrl, Context.Res.WithStatusCode(302)) } @@ -358,7 +358,7 @@ func Main(Context openruntimes.Context) openruntimes.Response { You can then deploy this function using the `appwrite deploy function` command. -After deployment, go to the Settings tab on the Function page in your Appwrite project and enable the following scopes for the dynamic API key: `databases.read`, `databases.write`, `collections.read`, `collections.write`, `attributes.read`, `attributes.write`, `documents.read`, `documents.write`, +After deployment, go to the Settings tab on the Function page in your Appwrite project and enable the following scopes for the dynamic API key: `databases.read`, `databases.write`, `tables.read`, `tables.write`, `columns.read`, `columns.write`, `rows.read`, `rows.write`, # More resources diff --git a/src/routes/blog/post/7-prompting-mistakes-you-need-to-stop-making-right-now/+page.markdoc b/src/routes/blog/post/7-prompting-mistakes-you-need-to-stop-making-right-now/+page.markdoc index 982a613f03e..d3b9f23693d 100644 --- a/src/routes/blog/post/7-prompting-mistakes-you-need-to-stop-making-right-now/+page.markdoc +++ b/src/routes/blog/post/7-prompting-mistakes-you-need-to-stop-making-right-now/+page.markdoc @@ -104,7 +104,7 @@ The second prompt gives the model a language, a framework, specific libraries, e The developers getting the most out of AI tools aren't the ones who use it the most. They're the ones who've learned to communicate precisely. Prompting is a communication skill. The better you get at it, the more leverage you get from every AI tool in your workflow. -If you're building applications with AI features and want a backend that keeps up, [Appwrite](https://appwrite.io) gives you auth, databases, storage, and serverless functions in one platform. Appwrite also works with the [Model Context Protocol](/docs/tooling/mcp), so your AI assistant can interact directly with your backend, creating collections, querying data, and managing functions through natural language. It's a practical way to pair better prompting with better tooling. +If you're building applications with AI features and want a backend that keeps up, [Appwrite](https://appwrite.io) gives you auth, databases, storage, and serverless functions in one platform. Appwrite also works with the [Model Context Protocol](/docs/tooling/mcp), so your AI assistant can interact directly with your backend, creating tables, querying data, and managing functions through natural language. It's a practical way to pair better prompting with better tooling. # More resources - [Appwrite MCP server](/docs/tooling/mcp) diff --git a/src/routes/blog/post/7-steps-to-achieve-gdpr-compliance-for-startups/+page.markdoc b/src/routes/blog/post/7-steps-to-achieve-gdpr-compliance-for-startups/+page.markdoc index cbe6fe825e5..42eb50cf42c 100644 --- a/src/routes/blog/post/7-steps-to-achieve-gdpr-compliance-for-startups/+page.markdoc +++ b/src/routes/blog/post/7-steps-to-achieve-gdpr-compliance-for-startups/+page.markdoc @@ -16,7 +16,7 @@ faqs: - question: "How does Appwrite help with GDPR compliance?" answer: "Appwrite hashes passwords with Argon2, encrypts data in transit over TLS, supports MFA on [Appwrite Auth](/docs/products/auth), and lets you delete user accounts (and their data) on request. Appwrite Cloud also offers EU regions so personal data of EU users can stay within the EU, simplifying data transfer compliance." - question: "What is the right to be forgotten and how do I implement it?" - answer: "Users have the right to request deletion of their personal data. In Appwrite, you can delete a user via the Users API, which removes their identity record. You still need to delete documents, files, and other user-tied records in your collections and buckets, which you can do in an [Appwrite Function](/docs/products/functions) triggered by the user deletion event." + answer: "Users have the right to request deletion of their personal data. In Appwrite, you can delete a user via the Users API, which removes their identity record. You still need to delete rows, files, and other user-tied records in your tables and buckets, which you can do in an [Appwrite Function](/docs/products/functions) triggered by the user deletion event." - question: "Do I need a Data Processing Agreement (DPA) with my backend provider?" answer: "Yes. If your backend provider processes personal data on your behalf, GDPR requires a DPA. Appwrite Cloud customers can request a DPA covering Appwrite's role as a data processor. Self-hosted Appwrite means you are processor and controller for your own data." - question: "How quickly must I report a data breach under GDPR?" diff --git a/src/routes/blog/post/7-things-claude-can-do-that-will-blow-your-mind/+page.markdoc b/src/routes/blog/post/7-things-claude-can-do-that-will-blow-your-mind/+page.markdoc index 294ae13e7a4..7d7a3cceecb 100644 --- a/src/routes/blog/post/7-things-claude-can-do-that-will-blow-your-mind/+page.markdoc +++ b/src/routes/blog/post/7-things-claude-can-do-that-will-blow-your-mind/+page.markdoc @@ -17,7 +17,7 @@ faqs: - question: "What is Claude Projects?" answer: "Projects is a feature in Claude that lets the model retain context across sessions. You can give it background about your codebase, your team's conventions, and the constraints you work under, and it carries that forward into every conversation inside the project. This removes the repetitive setup of re-explaining your stack at the start of each chat." - question: "What is the Model Context Protocol (MCP) in Claude?" - answer: "MCP gives Claude the ability to connect to external systems like databases, APIs, and services, and actually act on them rather than just describing what it would do. With the Appwrite MCP server, for example, Claude can create users, query collections, manage storage, and trigger functions directly inside an Appwrite project." + answer: "MCP gives Claude the ability to connect to external systems like databases, APIs, and services, and actually act on them rather than just describing what it would do. With the Appwrite MCP server, for example, Claude can create users, query tables, manage storage, and trigger functions directly inside an Appwrite project." - question: "Can Claude analyze images and generate code from them?" answer: "Yes. Claude understands images and can take screenshots of UIs, mockups, diagrams, or schemas drawn on a whiteboard, then describe what it sees and generate code to match. This is useful for design-to-code workflows, where a Figma screenshot or screen recording of a broken UI can produce a matching layout or pinpoint where the CSS is off." - question: "What is Claude Code?" @@ -64,7 +64,7 @@ It's worth trying for tasks that would normally take you an afternoon of focused The **Model Context Protocol (MCP)** gives Claude the ability to connect to external systems, your database, your APIs, your services, and actually interact with them. Not just describe what it would do, but do it. -With the [Appwrite MCP server](/docs/tooling/mcp), for example, Claude can create users, query collections, manage storage, and trigger functions directly inside your Appwrite project. You describe what you need in plain language, and Claude handles the operation. No copy-pasting API responses. No switching tabs to check the console. +With the [Appwrite MCP server](/docs/tooling/mcp), for example, Claude can create users, query tables, manage storage, and trigger functions directly inside your Appwrite project. You describe what you need in plain language, and Claude handles the operation. No copy-pasting API responses. No switching tabs to check the console. This is what [agentic AI](/blog/post/agentic-ai-vs-generative-ai) actually looks like in practice: the model connected to real systems, taking real actions. If you've only ever used Claude in a chat window, adding MCP changes the category of work it can handle. diff --git a/src/routes/blog/post/add-a-search-function-to-your-app/+page.markdoc b/src/routes/blog/post/add-a-search-function-to-your-app/+page.markdoc index 4093fa2388d..7e92bf32223 100644 --- a/src/routes/blog/post/add-a-search-function-to-your-app/+page.markdoc +++ b/src/routes/blog/post/add-a-search-function-to-your-app/+page.markdoc @@ -10,17 +10,17 @@ category: tutorial featured: false faqs: - question: "Why use Meilisearch instead of Appwrite's built-in queries?" - answer: "[Appwrite Databases](/docs/products/databases) supports basic queries, including a `search` operator on indexed string columns, which is sufficient for many apps. Meilisearch adds typo tolerance, ranking, faceted filtering, and instant search-as-you-type that is a step up for product search or large content catalogues. Use the built-in queries until you hit limits, then introduce Meilisearch." + answer: "[Appwrite Databases](/docs/products/databases) supports basic queries, including a `search` operator on indexed text columns, which is sufficient for many apps. Meilisearch adds typo tolerance, ranking, faceted filtering, and instant search-as-you-type that is a step up for product search or large content catalogues. Use the built-in queries until you hit limits, then introduce Meilisearch." - question: "What is an Appwrite Function template?" answer: "Function templates are pre-built [Appwrite Functions](/docs/products/functions) you can deploy in a few clicks. They cover common integrations like Stripe, OpenAI, Meilisearch, and more. You configure the required environment variables, connect a Git repo, and the function is deployed and ready to call." - - question: "How does the Meilisearch sync function stay in sync with new documents?" - answer: "By default the template syncs documents on demand when the function runs. To keep the index up to date automatically, configure the function to trigger on database events (document.create, document.update, document.delete) so every change is mirrored to Meilisearch in near real time." + - question: "How does the Meilisearch sync function stay in sync with new rows?" + answer: "By default the template syncs rows on demand when the function runs. To keep the index up to date automatically, configure the function to trigger on database row events so every change is mirrored to Meilisearch in near real time." - question: "Do I need to self-host Meilisearch to use this template?" answer: "No. Meilisearch Cloud is the easiest option and the template works against either Cloud or a self-hosted instance. You just need a Meilisearch endpoint URL, an admin API key for indexing, and a search key for client-side queries." - question: "Can I run the Meilisearch sync function on a schedule?" answer: "Yes. Appwrite Functions support CRON schedules, so you can run the sync every 5 minutes, every hour, or whatever cadence your use case needs. For most production cases, combining a periodic full sync with event-driven incremental updates gives you both consistency and freshness." - - question: "Is there a way to search documents directly in Appwrite without Meilisearch?" - answer: "Yes. Appwrite Databases supports a `Query.search` operator on string attributes that have a fulltext index. It is enough for simple search UIs. Meilisearch (or another dedicated search engine) is the right move once you need typo tolerance, custom ranking, or faceted filters." + - question: "Is there a way to search rows directly in Appwrite without Meilisearch?" + answer: "Yes. Appwrite Databases supports a `Query.search` operator on text columns that have a fulltext index. It is enough for simple search UIs. Meilisearch (or another dedicated search engine) is the right move once you need typo tolerance, custom ranking, or faceted filters." --- Function templates are pre-built Appwrite Functions that can be integrated into your Appwrite project with just a few clicks. Using them, you can easily incorporate new features and integrations into your app without writing additional code or managing infrastructure. @@ -29,14 +29,14 @@ One such integration you can implement using Appwrite Functions is **Searching** # Setting up the Template -Meilisearch is a flexible and powerful user-focused search engine that can be added to any website or application. The purpose of this function template is to sync documents in an Appwrite database collection to a Meilisearch index. Using this function template, users can explore, search, and retrieve information from the connected database collection. Through this template, documents from the Appwrite collection are systematically indexed within Meilisearch. +Meilisearch is a flexible and powerful user-focused search engine that can be added to any website or application. The purpose of this function template is to sync rows in an Appwrite database table to a Meilisearch index. Using this function template, users can explore, search, and retrieve information from the connected database table. Through this template, rows from the Appwrite table are systematically indexed within Meilisearch. To use the function, you need the following set of keys: - `APPWRITE_KEY` - API Key to talk to Appwrite backend APIs.To generate API Keys you can follow the documentation [here](https://appwrite.io/docs/getting-started-for-server#apiKey) - `APPWRITE_ENDPOINT` - To get the Appwrite endpoint, you need to go to [Appwrite](https://cloud.appwrite.io/) and find it under “Settings” -- `APPWRITE_DATABASE_ID` - The ID of the Appwrite database that contains the collection to sync. You can find the documentation [here](https://appwrite.io/docs/databases). -- `APPWRITE_COLLECTION_ID` - The ID of the collection in the Appwrite database to sync. +- `APPWRITE_DATABASE_ID` - The ID of the Appwrite database that contains the table to sync. You can find the documentation [here](/docs/products/databases). +- `APPWRITE_TABLE_ID` - The ID of the table in the Appwrite database to sync. To use Meilisearch, you can either self-host it using the command 👇 @@ -63,12 +63,12 @@ Here’s the keys you need: ![Keys](/images/blog/add-a-search-function-to-your-app/connect.avif) -- `MEILISEARCH_INDEX_NAME` - Name of the Meilisearch index to which the documents will be synchronized. For e.g, in the above picture, the Index name is `Newindex`. You can also find it under `Settings` as `Index Name`. +- `MEILISEARCH_INDEX_NAME` - Name of the Meilisearch index to which the rows will be synchronized. For e.g, in the above picture, the Index name is `Newindex`. You can also find it under `Settings` as `Index Name`. ## Preparing the Function -The function template syncs documents in an Appwrite database collection to a Meilisearch index. It should get you up and running, but you will need to add real data to build a useful search index. +The function template syncs rows in an Appwrite database table to a Meilisearch index. It should get you up and running, but you will need to add real data to build a useful search index. If you want to see the source code, you can find it on our [templates GitHub repository](https://github.com/appwrite/templates/tree/main/node/sync-with-meilisearch). Now, let’s navigate to our functions page on **[Appwrite](https://cloud.appwrite.io/)**. From there, we will select the **Templates** tab, search for and select the **Sync with Meilisearch** function template. @@ -76,7 +76,7 @@ If you want to see the source code, you can find it on our [templates GitHub re ![templates](/images/blog/add-a-search-function-to-your-app/templates.avif) -The function requires `APPWRITE_API_KEY`, `APPWRITE_DATABASE_ID`, `APPWRITE_COLLECTION_ID` , `MEILISEARCH_ENDPOINT`, `MEILISEARCH_ADMIN_API_KEY`, `MEILISEARCH_SEARCH_API_KEY`, `MEILISEARCH_INDEX_NAME`. Once you have added them you can proceed to the Connect step. +The function requires `APPWRITE_API_KEY`, `APPWRITE_DATABASE_ID`, `APPWRITE_TABLE_ID`, `MEILISEARCH_ENDPOINT`, `MEILISEARCH_ADMIN_API_KEY`, `MEILISEARCH_SEARCH_API_KEY`, `MEILISEARCH_INDEX_NAME`. Once you have added them you can proceed to the Connect step. Select **Create a new repository** (this will generate a GitHub repository for you with the function), and leave the production branch and root settings as default to create this function. @@ -97,7 +97,7 @@ Visit the **Domains** tab on the function page and copy the domain URL to test We’ve added search functionality to our app and opened up many possibilities to improve the experience of our app’s users. How can the *template* be extended ? -- Using events to automatically index new collections +- Using events to automatically index new tables - Using weights and other meilisearch features to optimise search such as excluding certain fields from indexing Some examples are: @@ -109,6 +109,6 @@ Be sure to check out the other available Function Templates. We’ve created mul For more information about Appwrite and Appwrite Functions: -1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: These documents provide more information on how to use Appwrite Functions. +1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: Learn more about how to use Appwrite Functions. 2. **[Functions Announcement](https://dev.to/appwrite/serverless-your-way-unleashing-appwrite-functions-true-potential-2l4f)**: Read the full announcement on Functions 1.4. 3. **[Appwrite Discord](https://discord.com/invite/appwrite)**: Connect with other developers and the Appwrite team for discussion, questions, and collaboration. diff --git a/src/routes/blog/post/adding-url-shortener-function/+page.markdoc b/src/routes/blog/post/adding-url-shortener-function/+page.markdoc index 4adfdecbf24..7acf1a5dc24 100644 --- a/src/routes/blog/post/adding-url-shortener-function/+page.markdoc +++ b/src/routes/blog/post/adding-url-shortener-function/+page.markdoc @@ -14,13 +14,13 @@ faqs: - question: "How do I create a custom domain for my URL shortener function?" answer: "Use the Domains tab of the function to attach a custom domain like `s.example.com`. Once configured, all generated short URLs will use that domain instead of the default Appwrite Function URL, which is what most production shorteners need." - question: "Can I add analytics or click tracking?" - answer: "Yes. Extend the template so that on each redirect it writes a row to a `clicks` collection with the timestamp, IP, referrer, and user agent. You can then query that collection (or wire it into a dashboard) for click-through rates and geographic data. The original function template intentionally keeps this minimal." + answer: "Yes. Extend the template so that on each redirect it writes a row to a `clicks` table with the timestamp, IP, referrer, and user agent. You can then query that table (or wire it into a dashboard) for click-through rates and geographic data. The original function template intentionally keeps this minimal." - question: "Where are the shortened URLs stored?" - answer: "The function template stores them as documents in an [Appwrite Databases](/docs/products/databases) collection in your project. That gives you a query-able list of all generated short URLs with their target destinations and creation timestamps." + answer: "The function template stores them as rows in an [Appwrite Databases](/docs/products/databases) table in your project. That gives you a query-able list of all generated short URLs with their target destinations and creation timestamps." - question: "Is the URL shortener function open source?" answer: "Yes, the template source code is available in the [Appwrite templates repo](https://github.com/appwrite/templates/tree/main/node/url-shortener). You can fork it, customize the slug generator, add authentication, or rewrite it in another runtime." - question: "Can I limit who can create short URLs?" - answer: "Yes. By default the function endpoint is open, but you can require an Appwrite session by reading the user context from the function execution. Combine that with permission rules on the underlying collection so only authenticated users can create entries." + answer: "Yes. By default the function endpoint is open, but you can require an Appwrite session by reading the user context from the function execution. Combine that with permission rules on the underlying table so only authenticated users can create entries." --- Appwrite Functions are user-defined functions that can start small and scale big, deploying automatically from source control. With the introduction of function templates, you can quickly add new integrations into your app without writing additional code or managing infrastructure. Function templates are pre-built Appwrite Functions that can be integrated into your Appwrite project with just a few clicks. @@ -68,6 +68,6 @@ Be sure to check out the other available Function Templates. We’ve created man For more information about Appwrite and Appwrite Functions: -1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: These documents provide more information on how to use Appwrite Functions. +1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: Learn more about how to use Appwrite Functions. 2. **[Functions Announcement](https://dev.to/appwrite/serverless-your-way-unleashing-appwrite-functions-true-potential-2l4f)**: Read the full announcement on Functions 1.4. 3. **[Appwrite Discord](https://discord.com/invite/appwrite)**: Connect with other developers and the Appwrite team for discussion, questions, and collaboration. diff --git a/src/routes/blog/post/agency-backend-standardization/+page.markdoc b/src/routes/blog/post/agency-backend-standardization/+page.markdoc index e0069082e6f..d119d374ed6 100644 --- a/src/routes/blog/post/agency-backend-standardization/+page.markdoc +++ b/src/routes/blog/post/agency-backend-standardization/+page.markdoc @@ -59,7 +59,7 @@ Agencies that have standardized their backend stack typically follow a pattern: 4. **Use the platform's organizational features.** Most backend platforms offer organizations, teams, or workspaces for managing multiple projects. Use them consistently so there's a clear structure that any team member can navigate. -5. **Document your patterns, not just the platform.** Your team's specific conventions (how you name collections, how you structure permissions, how you handle file uploads) should be documented alongside the platform documentation. +5. **Document your patterns, not just the platform.** Your team's specific conventions (how you name tables, how you structure permissions, how you handle file uploads) should be documented alongside the platform documentation. # Evaluating a backend platform for agency use diff --git a/src/routes/blog/post/agentic-ai-vs-generative-ai/+page.markdoc b/src/routes/blog/post/agentic-ai-vs-generative-ai/+page.markdoc index d785fae0471..3c87d4aa3cd 100644 --- a/src/routes/blog/post/agentic-ai-vs-generative-ai/+page.markdoc +++ b/src/routes/blog/post/agentic-ai-vs-generative-ai/+page.markdoc @@ -17,7 +17,7 @@ faqs: - question: "What role does MCP play in agentic AI?" answer: "[Model Context Protocol](/blog/post/what-is-mcp) (MCP) is a standard that lets agents connect to external tools, APIs, and data sources in a consistent way. Without MCP, every tool needs a custom integration. With it, an agent can use the same wire protocol to talk to GitHub, Appwrite, Stripe, and your internal services." - question: "How do I expose an Appwrite project to an AI agent?" - answer: "Use the [Appwrite MCP server](/docs/tooling/mcp/api). It exposes Appwrite Auth, Databases, Storage, and Functions as tools an MCP-compatible agent can call. The agent can then create users, query collections, upload files, or trigger functions as part of its plan." + answer: "Use the [Appwrite MCP server](/docs/tooling/mcp/api). It exposes Appwrite Auth, Databases, Storage, and Functions as tools an MCP-compatible agent can call. The agent can then create users, query tables, upload files, or trigger functions as part of its plan." - question: "Do I need agentic AI for my product, or is generative AI enough?" answer: "If your use case is summarization, content drafting, or chat, generative AI is usually enough. Reach for agentic systems when your task requires multiple steps, calling external systems, or adapting based on intermediate results, e.g., a research assistant, a code-writing agent, or an ops automation tool." - question: "Are agentic AI systems reliable enough for production?" diff --git a/src/routes/blog/post/ai-crystal-ball/+page.markdoc b/src/routes/blog/post/ai-crystal-ball/+page.markdoc index 69377ac672d..c5321227c75 100644 --- a/src/routes/blog/post/ai-crystal-ball/+page.markdoc +++ b/src/routes/blog/post/ai-crystal-ball/+page.markdoc @@ -17,7 +17,7 @@ faqs: - question: "How do I prevent abuse of an OpenAI API key on the frontend?" answer: "Never call OpenAI directly from the client. Instead, expose your own endpoint via an [Appwrite Function](/docs/products/functions) that holds the API key server-side, validates the authenticated user, and applies rate limits. The client only ever talks to your function, not to OpenAI." - question: "How do permissions work for the shareable destiny link?" - answer: "The `destiny` collection in the post grants `read` permission to `Any` and `create` permission to `Users`, so any anonymous visitor can read a destiny by document ID but only logged-in users can create new ones. This is the standard Appwrite permission pattern for share links." + answer: "The `destiny` table in the post grants `read` permission to `Any` and `create` permission to `Users`, so any anonymous visitor can read a destiny by row ID but only logged-in users can create new ones. This is the standard Appwrite permission pattern for share links." - question: "Can I build this entirely without writing my own backend?" answer: "Largely yes. [Appwrite Functions](/docs/products/functions) hosts the OpenAI call, [Appwrite Databases](/docs/products/databases) stores the data, and [Appwrite Auth](/docs/products/auth) handles GitHub OAuth. The only piece you write is the SvelteKit frontend and the single function that talks to GPT-4." --- @@ -33,7 +33,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 @@ -72,35 +72,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 | Text | 2000 | - | Yes | | followers | Integer | - | Yes | - | | following | Integer | - | Yes | - | -| username | String | 255 | Yes | - | +| username | Text | 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 | Text | 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 | | --- | --- | --- | --- | --- | @@ -136,15 +136,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() @@ -152,7 +152,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 }; ``` @@ -281,23 +281,23 @@ export const github = { } ``` -At this point, we also want to create our Database library using the Appwrite SDK, so that we can store the information from the GitHub API. For that, we shall create a file `./src/lib/databases.js` and add the following: +At this point, we also want to create our database library using the Appwrite SDK, so that we can store the information from the GitHub API. For that, we shall create a file `./src/lib/tables-db.js` and add the following: ```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; @@ -305,10 +305,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, @@ -514,44 +514,36 @@ Since our UI has already been prepared in the previous step, we need not make an Lastly, to share our destiny with the rest of the world, we must create an additional page that gets destiny data from the Appwrite Database and displays it. For this, we must first add functions to add and get destinies from the Appwrite Database to our Database library. To do so, we visit `./src/lib/databases/js` and add the following functions to our export: ```js -. -. -. -const destinyCollectionId = env.PUBLIC_APPWRITE_COLLECTION_ID_DESTINY; - -export const db = {** -. -. -. - addDestiny: async(username, destiny) => { - return await databases.createDocument({ - databaseId, - collectionId: destinyCollectionId, - documentId: ID.unique(), - data: { - username, - destiny - } - }) - }, - - getDestiny: async(documentId) => { - try{ - return await databases.getDocument({ - databaseId, - collectionId: destinyCollectionId, - documentId - }); - } catch(err){ - return { - username: 'Not found', - destiny: 'Not found' - } - } - } -. -. -. +const destinyTableId = env.PUBLIC_APPWRITE_TABLE_ID_DESTINY; + +export const destinyDb = { + addDestiny: async (username, destiny) => { + return await tablesDB.createRow({ + databaseId, + tableId: destinyTableId, + rowId: ID.unique(), + data: { + username, + destiny + } + }); + }, + + getDestiny: async (rowId) => { + try { + return await tablesDB.getRow({ + databaseId, + tableId: destinyTableId, + rowId + }); + } catch (err) { + return { + username: 'Not found', + destiny: 'Not found' + }; + } + } +}; ``` After that, we will create a new directory `./src/routes/destiny/[slug]` and add the following @@ -624,4 +616,4 @@ The web app is still live and can be tried at the following link: [aicrystalball You can find the application’s complete source code at this [GitHub Repo](https://github.com/adityaoberai/AI-Crystal-Ball/). -[Join us on Discord](https://appwrite.io/discord) to be the first to get updates and to be part of a vibrant community! \ No newline at end of file +[Join us on Discord](https://appwrite.io/discord) to be the first to get updates and to be part of a vibrant community! diff --git a/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc b/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc index 4e84d953086..932c570e5d1 100644 --- a/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc +++ b/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc @@ -13,7 +13,7 @@ faqs: - question: "Which AI coding tools do developers use most?" answer: "Survey responses from the Appwrite community show ChatGPT, GitHub Copilot, Claude, and Cursor as the most-used tools, with Warp AI CLI gaining traction in the terminal. Most developers combine two or three of these depending on the task: in-editor completions, longer reasoning, and quick one-off questions." - question: "How can I use AI to build with Appwrite?" - answer: "Use the [Appwrite MCP server](/docs/tooling/mcp/api) to give AI assistants direct access to your project. They can create collections, manage users, deploy functions, and run queries through natural language. The [Appwrite plugin for Claude Code](/blog/post/announcing-appwrite-claude-code-plugin) bundles this together for Claude users." + answer: "Use the [Appwrite MCP server](/docs/tooling/mcp/api) to give AI assistants direct access to your project. They can create tables, manage users, deploy functions, and run queries through natural language. The [Appwrite plugin for Claude Code](/blog/post/announcing-appwrite-claude-code-plugin) bundles this together for Claude users." - question: "Should I worry about security when vibe coding?" answer: "Yes. Vibe-coded apps often skip auth, permissions, and rate limiting because the AI does not bring them up by default. Read the [backend checklist for vibe-coded apps](/blog/post/backend-checklist-vibe-coded-apps) before you ship, and keep API keys server-side in an [Appwrite Function](/docs/products/functions) rather than the browser." - question: "Is vibe coding suitable for production apps?" @@ -97,7 +97,7 @@ This gap doesn’t necessarily mean senior developers aren’t interested, it ma Many developers are already pairing AI tools with Appwrite for backend tasks like databases, authentication, and hosting. In fact, **Appwrite was the most‑mentioned backend in the survey**, ahead of other options like Supabase. -The **Appwrite MCP server** builds on this by letting AI assistants interact directly with Appwrite, creating collections, managing functions, and running queries through natural language. +The **Appwrite MCP server** builds on this by letting AI assistants interact directly with Appwrite, creating tables, managing functions, and running queries through natural language. > As one developer noted, *“Appwrite MCP connecting with AI tools could be a game‑changer for building full‑stack apps faster.”* > diff --git a/src/routes/blog/post/announcing-appwrite-databases-new-ui/+page.markdoc b/src/routes/blog/post/announcing-appwrite-databases-new-ui/+page.markdoc index 473f97220bd..32681807c47 100644 --- a/src/routes/blog/post/announcing-appwrite-databases-new-ui/+page.markdoc +++ b/src/routes/blog/post/announcing-appwrite-databases-new-ui/+page.markdoc @@ -12,19 +12,19 @@ faqs: - question: "What is TablesDB in Appwrite?" answer: "TablesDB is the updated naming and API layer for [Appwrite Databases](/docs/products/databases). It maps the familiar relational vocabulary (tables, rows, columns) onto Appwrite's existing data model. The storage engine itself has not changed, only the terminology and the new method names exposed in the SDKs and Console." - question: "Do I have to migrate my code to the new TablesDB API?" - answer: "No. The old document-based methods like `createDocument` and `listDocuments` continue to work and are fully backwards compatible. They are marked as deprecated and will receive security patches but not new features, so we recommend the TablesDB methods for new projects." - - question: "What is the mapping between the old and new Database terms?" - answer: "Collections become tables, attributes become columns, and documents become rows. Methods follow the same pattern: `createCollection` becomes `createTable`, `createDocument` becomes `createRow`, `listDocuments` becomes `listRows`, and so on. The underlying behavior of each call is unchanged." + answer: "TablesDB is the recommended API for new projects. Existing projects can adopt it at their own pace, and new tutorials, SDK snippets, and Console workflows use the TablesDB methods." + - question: "What data model does TablesDB use?" + answer: "TablesDB uses databases, tables, columns, and rows. Methods follow the same pattern: `createTable`, `createTextColumn`, `createRow`, `listRows`, and so on. The underlying behavior is consistent across the Console, SDKs, and API." - question: "Does the new TablesDB UI replace the existing Databases section in the Console?" answer: "Yes. The Databases section in the Appwrite Console has been redesigned around the TablesDB UI, with browsing, editing, and managing data built around tables, rows, and columns. Existing projects automatically open in the new UI without any migration step." - - question: "Can I still use Appwrite Databases as a document store?" - answer: "Yes. The rename is purely a terminology and API change, not a switch to a strict relational engine. You can still store flexible row data, use the same query operators, and apply per-row permissions. The relational vocabulary is meant to make the model easier to reason about for developers used to SQL." + - question: "Can I store flexible row data in Appwrite Databases?" + answer: "Yes. You can store flexible row data, use the same query operators, and apply per-row permissions. The relational vocabulary is meant to make the model easier to reason about for developers used to SQL." - question: "Where can I learn the new TablesDB methods in detail?" - answer: "The [Databases documentation](/docs/products/databases) covers every TablesDB method with examples for each server and client SDK. The deprecated document methods remain documented for reference, but new tutorials and references default to the TablesDB naming." + answer: "The [Databases documentation](/docs/products/databases) covers every TablesDB method with examples for each server and client SDK." --- -As Appwrite has grown, so has the diversity of developers building with it, from document-based systems to others deeply familiar with relational data structures. To make the platform more intuitive and consistent for everyone, we’re evolving how Appwrite Databases is presented and managed. This update also adds the ability to generate dummy data for faster prototyping, and we’re actively working on features like AI-powered suggestions to help you design your structure even faster. +As Appwrite has grown, so has the diversity of developers building with it, from teams shipping fast prototypes to teams deeply familiar with relational data structures. To make the platform more intuitive and consistent for everyone, we’re evolving how Appwrite Databases is presented and managed. This update also adds the ability to generate dummy data for faster prototyping, and we’re actively working on features like AI-powered suggestions to help you design your structure even faster. -Appwrite Databases now adopts a more familiar **relational model** across the Console, API, SDKs, and beyond, moving away from the documents and collections terminology rooted in our JSON-based API. The new model introduces conventional data concepts like **tables**, **rows**, and **columns**, making it easier to work with structured data while maintaining full backward compatibility. +Appwrite Databases now adopts a more familiar **relational model** across the Console, API, SDKs, and beyond. The model uses conventional data concepts like **tables**, **rows**, and **columns**, making it easier to work with structured data while maintaining full backward compatibility. As part of this shift, the Appwrite Console has been completely redesigned. The new **TablesDB UI** was built specifically to support the upgraded database experience, making browsing, editing, and managing data more intuitive. @@ -32,35 +32,11 @@ We’re also introducing the **TablesDB API,** a new API layer designed to bridg # What’s new -This release brings both **terminology changes** and a **completely reimagined data management interface**. Let’s dive in. +This release brings a **completely reimagined data management interface** and a dedicated **TablesDB API**. Let’s dive in. -# Updated terminology & the TablesDB API +# TablesDB API -Until now, Appwrite Databases used terminology that made sense in a JSON-document world but could be unintuitive for developers used to relational data concepts. We’re introducing a parallel set of API endpoints and terms aligned with relational database language to make the platform more approachable. - -| Previous terms | New terms | -| --- | --- | -| Collections | Tables | -| Attributes | Columns | -| Documents | Rows | - -Functionality remains the same, but the developer experience becomes clearer and more consistent for anyone familiar with structured data workflows. - -# New TablesDB methods - -Alongside these terminology updates, the **TablesDB API** introduces new method names that map directly to common CRUD operations in a relational model: - -| Previous methods | New methods | -| --- | --- | -| `createCollection` | `createTable` | -| `createDocument` | `createRow` | -| `createAttribute` | `createColumn` | -| `updateDocument` | `updateRow` | -| `deleteDocument` | `deleteRow` | -| `getDocument` | `getRow` | -| `listDocuments` | `listRows` | - -These methods make it easier to understand exactly what an operation does at a glance, while still running on the same powerful API that Appwrite has always used. +The **TablesDB API** introduces method names that map directly to common CRUD operations in a relational model. Methods like `createTable`, `createTextColumn`, `createRow`, `updateRow`, `deleteRow`, `getRow`, and `listRows` make it easier to understand exactly what an operation does at a glance, while still running on the same powerful API that Appwrite has always used. Here’s an example of how you can use the new SDK methods: @@ -86,11 +62,11 @@ async function main() { name: '' }); - const nameColumn = await tablesDB.createStringColumn({ + const nameColumn = await tablesDB.createTextColumn({ databaseId: database.$id, tableId: table.$id, key: 'name', - size: 255 + required: true }); const row = await tablesDB.createRow({ @@ -104,15 +80,9 @@ async function main() { main(); ``` -# What happens to the existing methods? - -The old document-based methods are now deprecated and will no longer receive major upgrades. They will continue to receive security patches and essential maintenance, and we are ensuring backwards compatibility with those methods for the API, so there’s no need to migrate immediately if a specific issue arises. - -However, we recommend adopting the new TablesDB API for future projects to take advantage of ongoing improvements and new capabilities. - # The new TablesDB UI -The TablesDB UI changes the way you interact with your data in the Console. The previous Databases view was read-only and required multiple clicks to edit a single record, fine for small fixes, but inefficient for larger updates. +The TablesDB UI changes the way you interact with your data in the Console. It gives you a fast, spreadsheet-like surface for browsing and editing rows. With TablesDB UI, now you can: @@ -127,9 +97,9 @@ Watch this [product tour](https://www.youtube.com/watch?v=b80BXqj5CJE) to learn # What changes in your workflow? -**For the new terminology and TablesDB API:** +**For the TablesDB API:** -The existing `Collections` API will continue to function as expected. The new `TablesDB` API introduces a parallel, relational model experience. +The `TablesDB` API introduces a relational model experience. Key changes include: diff --git a/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc b/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc index ba23c4cf706..cd628de0c82 100644 --- a/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc +++ b/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc @@ -18,7 +18,7 @@ faqs: - question: "How does the GitHub integration with Appwrite Functions work?" answer: "The GitHub integration connects an Appwrite project to a GitHub repository so that pushes to a branch automatically deploy your [Function](/docs/products/functions). It also powers function templates: you can pick a template, and Appwrite forks the source repo into your GitHub account before deploying." - question: "Can I use Stripe or Lemon Squeezy for subscriptions with Appwrite?" - answer: "Yes. Stripe and Lemon Squeezy both have step-by-step integration guides that use [Appwrite Functions](/docs/products/functions) to handle webhooks and a Database collection to store subscription state. Functions sit between your client and the payment provider, so you do not have to run a separate backend." + answer: "Yes. Stripe and Lemon Squeezy both have step-by-step integration guides that use [Appwrite Functions](/docs/products/functions) to handle webhooks and a database table to store subscription state. Functions sit between your client and the payment provider, so you do not have to run a separate backend." - question: "How are OAuth2 providers added to a project?" answer: "OAuth2 providers are configured per project in the Appwrite Console under [Auth](/docs/products/auth). You add your client ID and secret from the provider (Google, GitHub, Discord, and so on), enable the provider, and then call `createOAuth2Session` from a client SDK to start the login flow." --- diff --git a/src/routes/blog/post/announcing-appwrite-is-hipaa-compliant/+page.markdoc b/src/routes/blog/post/announcing-appwrite-is-hipaa-compliant/+page.markdoc index b375b0dbd7c..f9ea39f3b15 100644 --- a/src/routes/blog/post/announcing-appwrite-is-hipaa-compliant/+page.markdoc +++ b/src/routes/blog/post/announcing-appwrite-is-hipaa-compliant/+page.markdoc @@ -13,7 +13,7 @@ faqs: - question: "What does Appwrite Cloud being HIPAA compliant mean for me?" answer: "Appwrite Cloud has the technical and organizational safeguards in place (encryption, access controls, audit logging, backups, MFA on internal access) to act as a HIPAA-compliant backend. You can build healthcare apps on Appwrite without having to run that infrastructure yourself." - question: "Do I still have to make my own app HIPAA compliant?" - answer: "Yes. Appwrite covers the platform layer, but you remain responsible for how your application collects, displays, and shares PHI. That includes things like configuring permissions correctly, restricting who can read sensitive collections, training your staff, and obtaining consent where required." + answer: "Yes. Appwrite covers the platform layer, but you remain responsible for how your application collects, displays, and shares PHI. That includes things like configuring permissions correctly, restricting who can read sensitive tables, training your staff, and obtaining consent where required." - question: "Can I get a Business Associate Agreement (BAA) with Appwrite?" answer: "BAAs are required for any service that processes PHI on your behalf. If you are building a HIPAA-regulated app on Appwrite Cloud, contact Appwrite via the [contact form](/contact-us) to discuss a BAA before going live with PHI." - question: "Is self-hosted Appwrite HIPAA compliant?" diff --git a/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc b/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc index 91ad2588203..7628f86d338 100644 --- a/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc +++ b/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc @@ -16,7 +16,7 @@ faqs: - question: "What is the difference between topics, users, and targets in Messaging?" answer: "A target is a specific destination (an email address, phone number, or device push token). A user holds all the targets associated with a single Appwrite account. A topic is a named group of targets that you can publish a message to in one call. Newsletters typically use topics, alerts use users, and location-specific notifications use individual targets." - question: "Can I send a message from an Appwrite Function?" - answer: "Yes. You can call any Messaging method from a server SDK inside an [Appwrite Function](/docs/products/functions). Combine this with event triggers (for example, on user creation or document update) to send automated welcome emails, login alerts, order confirmations, and other transactional messages." + answer: "Yes. You can call any Messaging method from a server SDK inside an [Appwrite Function](/docs/products/functions). Combine this with event triggers (for example, on user creation or row update) to send automated welcome emails, login alerts, order confirmations, and other transactional messages." - question: "Can I schedule messages for later?" answer: "Yes. Each Messaging API call accepts a scheduled send time, and you can also pick a date in the Appwrite Console when drafting a message. This is useful for appointment reminders, time-zoned promos, and announcements that should land at a specific moment." - question: "Does Messaging work the same on Appwrite Cloud and self-hosted?" diff --git a/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc b/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc index c7c77d39b6c..9a14b6cb7aa 100644 --- a/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc +++ b/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post title: "Announcing Atomic numeric operations: Safe, server-side increments and decrements" -description: Safely update numeric fields like counters, stock levels, or credits without fetching or rewriting the document. +description: Safely update numeric columns like counters, stock levels, or credits without fetching or rewriting the row. date: 2025-08-04 cover: /images/blog/announcing-atomic-numeric-operations/cover.avif timeToRead: 5 @@ -10,33 +10,33 @@ category: announcement featured: false faqs: - question: "What are atomic numeric operations in Appwrite?" - answer: "Atomic numeric operations let you increment or decrement a numeric field on a document directly on the server using `incrementDocumentAttribute` and `decrementDocumentAttribute`. Each delta is applied in a single write under concurrency control, so concurrent updates do not overwrite each other." + answer: "Atomic numeric operations let you increment or decrement a numeric column on a row directly on the server using `incrementRowColumn` and `decrementRowColumn`. Each delta is applied in a single write under concurrency control, so concurrent updates do not overwrite each other." - question: "Why are atomic increments safer than read-modify-write?" answer: "With a read-modify-write loop, two clients can read the same value, both add 1, and both write back the same new value. One increment is lost. Atomic operations send only the delta to the server, where the database applies the change in one locked step, eliminating the race condition." - question: "Where would I use atomic numeric operations?" answer: "Anywhere you mutate a number that multiple clients can touch at once: likes and follower counts, API quotas, stock levels, game scores, retry counters, and rate limits. The feature is especially valuable in real-time and high-concurrency scenarios." - question: "Can I set min and max bounds on the value?" answer: "Yes. Each operation accepts optional `min` and `max` constraints. If the resulting value would fall outside the bounds, the update is rejected instead of silently clamping. This is useful for rules like \"stock cannot go below zero\" or \"credits cannot exceed a cap\"." - - question: "Do atomic operations respect document permissions?" - answer: "Yes. The same [Appwrite permissions](/docs/products/databases) that gate `updateDocument` apply to increments and decrements. If a user is not allowed to write to the document, the atomic operation also fails. No special role is added by using the new method." + - question: "Do atomic operations respect row permissions?" + answer: "Yes. The same [Appwrite permissions](/docs/products/databases) that gate `updateRow` apply to increments and decrements. If a user is not allowed to write to the row, the atomic operation also fails. No special role is added by using the new method." - question: "Are atomic operations available on both Cloud and self-hosted Appwrite?" answer: "Yes. Atomic numeric operations work the same on Appwrite Cloud and self-hosted installations. You can call them from any [Appwrite SDK](/docs/sdks) that has been updated to expose the new methods." --- In high-concurrency systems like social apps, games, and usage-tracked services, even updating a single number such as a like, retry count, or quota, can lead to consistency issues. When multiple clients try to update the same value simultaneously, it’s easy to end up with conflicting writes, lost updates, or inaccurate data. -Most setups require you to fetch the document, change the number on the client, and then write it back. This process is slow, error-prone, and wastes bandwidth, especially when you're only trying to change a single field. +Most setups require you to fetch the row, change the number on the client, and then write it back. This process is slow, error-prone, and wastes bandwidth, especially when you're only trying to change a single column. To change this, we introduce **Atomic numeric operations** in Appwrite. -A new feature that lets you increment or decrement numeric fields directly on the server, without fetching the full document. It’s fast, safe, bandwidth-efficient, and concurrency-friendly. +A new feature that lets you increment or decrement numeric columns directly on the server, without fetching the full row. It’s fast, safe, bandwidth-efficient, and concurrency-friendly. # Race-free numeric updates -Before this feature, updating a number meant fetching the entire document, modifying it on the client, and writing it back, a process prone to race conditions, unnecessary bandwidth use, and extra logic to handle edge cases. +Before this feature, updating a number meant fetching the entire row, modifying it on the client, and writing it back, a process prone to race conditions, unnecessary bandwidth use, and extra logic to handle edge cases. -With **Atomic numeric operations,** you simply send a delta (like `+1` or `-3`), and Appwrite applies the update atomically on the server. No full document reads, no conflicts, no custom logic. Just consistent, permission-aware updates that work reliably under load. +With **Atomic numeric operations,** you simply send a delta (like `+1` or `-3`), and Appwrite applies the update atomically on the server. No full row reads, no conflicts, no custom logic. Just consistent, permission-aware updates that work reliably under load. # Built for real-time, multi-user systems @@ -54,63 +54,63 @@ Use it for: # Performing an atomic operation -Use the `incrementDocumentAttribute` and `decrementDocumentAttribute` methods to perform atomic numeric operations. The server will apply these changes atomically under concurrency control. +Use the `incrementRowColumn` and `decrementRowColumn` methods to perform atomic numeric operations. The server will apply these changes atomically under concurrency control. ## Increment a field {% #increment-field %} ```client-web -import { Client, Databases } from "appwrite"; +import { Client, TablesDB } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID -const databases = new Databases(client); +const tablesDB = new TablesDB(client); -const result = await databases.incrementDocumentAttribute( - '', - '', - '', - 'likes', // attribute - 1 // value -); +const result = await tablesDB.incrementRowColumn({ + databaseId: '', + tableId: '', + rowId: '', + column: 'likes', + value: 1 +}); ``` ## Decrement a field {% #decrement-field %} ```client-web -import { Client, Databases } from "appwrite"; +import { Client, TablesDB } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID -const databases = new Databases(client); +const tablesDB = new TablesDB(client); -const result = await databases.decrementDocumentAttribute( - '', - '', - '', - 'credits', // attribute - 5 // value -); +const result = await tablesDB.decrementRowColumn({ + databaseId: '', + tableId: '', + rowId: '', + column: 'credits', + value: 5 +}); ``` # Immediate benefits This feature solves a common problem with a clean, built-in approach. You don’t need to write custom logic to handle concurrency, retries, or limits. It’s a simple API call that replaces a lot of complex edge-case handling. And it just works. -- **Atomic by default:** Every delta is applied in a single server-side write. The document is locked during the update, so there’s no room for race conditions or overlapping writes, even under heavy concurrency. +- **Atomic by default:** Every delta is applied in a single server-side write. The row is locked during the update, so there’s no room for race conditions or overlapping writes, even under heavy concurrency. - **Supports both increments and decrements:** You're not limited to just adding `+1`. You can apply any positive or negative delta, whether you're increasing API credits or reducing stock levels after a purchase. - **Built-in constraints:** You can define optional `min` and `max` bounds on the value. If the update would push the value outside that range, it’s rejected. Great for enforcing limits like “stock can’t go below zero” or “credits can't exceed a cap.” -- **Respects permissions:** This works just like any other Appwrite document update. If the user doesn’t have permission to modify the document, the update doesn’t go through. No exceptions. +- **Respects permissions:** This works just like any other Appwrite row update. If the user doesn’t have permission to modify the row, the update doesn’t go through. No exceptions. Atomic numeric operations are live for both **Appwrite Cloud** and **Self-Hosted** environments. -This is a core building block for modern, concurrent-safe applications and it’s now built into Appwrite’s document system. +This is a core building block for modern, concurrent-safe applications and it’s now built into Appwrite’s row update flow. # More resources 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 f86f655e1ba..d498a92e1b8 100644 --- a/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc +++ b/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc @@ -1,7 +1,7 @@ --- 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 cover: /images/blog/announcing-auto-increment-support/cover.avif timeToRead: 5 @@ -9,29 +9,29 @@ author: jake-barnby category: announcement featured: false faqs: - - question: "What is the `$sequence` field on Appwrite documents?" - answer: "`$sequence` is a numeric field that Appwrite automatically assigns to each document at insertion time. It starts at one for the first row in a collection and increases by one with every new insert, giving you a strictly ascending, indexed identifier without writing any counter logic." + - question: "What is the `$sequence` field on Appwrite rows?" + answer: "`$sequence` is a numeric field that Appwrite automatically assigns to each row at insertion time. It starts at one for the first row in a table and increases by one with every new insert, giving you a strictly ascending, indexed identifier without writing any counter logic." - question: "How is `$sequence` different from `$id`?" answer: "`$id` is a string identifier that is either generated as a unique slug (`ID.unique()`) or set by the caller. `$sequence` is a numeric, monotonically increasing counter managed by Appwrite. Use `$id` for lookups and references and `$sequence` when you need ordering or numeric IDs like invoice numbers." - - question: "How do I sort documents by insertion order?" - answer: "Query the collection with `Query.orderAsc('$sequence')` (or `orderDesc` for newest-first). The field is fully indexed, so sorting and pagination by `$sequence` perform efficiently even on large collections. The full reference lives in the [order documentation](/docs/products/databases/order#sequence-ordering)." + - question: "How do I sort rows by insertion order?" + answer: "Query the table with `Query.orderAsc('$sequence')` (or `orderDesc` for newest-first). The field is fully indexed, so sorting and pagination by `$sequence` perform efficiently even on large tables. The full reference lives in the [order documentation](/docs/products/databases/order#sequence-ordering)." - question: "Can I modify the `$sequence` value?" answer: "No. `$sequence` is read-only by design. That guarantees the values are consistent, monotonically increasing, and tamper-resistant, which is what makes it suitable for audit logs, invoice numbers, and other use cases that require stable ordering." - question: "When should I use timestamps instead of `$sequence`?" - answer: "Use `$createdAt` when you need wall-clock ordering and are comfortable with millisecond resolution. Use `$sequence` when you need a strict numeric identifier or guaranteed insertion order, since two documents created in the same millisecond will have distinct sequence numbers but identical timestamps." + answer: "Use `$createdAt` when you need wall-clock ordering and are comfortable with millisecond resolution. Use `$sequence` when you need a strict numeric identifier or guaranteed insertion order, since two rows created in the same millisecond will have distinct sequence numbers but identical timestamps." - question: "Is auto-increment available on self-hosted Appwrite?" - answer: "Yes. Auto-increment support is available on both Appwrite Cloud and self-hosted installations of the supported version. Existing collections automatically expose `$sequence` for newly inserted documents going forward." + answer: "Yes. Auto-increment support is available on both Appwrite Cloud and self-hosted installations of the supported version. Existing tables automatically expose `$sequence` for newly inserted rows." --- Managing ordered data can often be complex and error-prone, especially when it requires manual counters or timestamp-based sorting, which can introduce inconsistencies and unpredictability. 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. +Maintaining consistent insertion order and generating numeric identifiers can require manual increments, complex logic, or dependence on timestamps, which are not always accurate or reliable. Appwrite’s 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. @@ -50,21 +50,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'), ] @@ -77,4 +77,4 @@ databases.listDocuments({ - [Read the documentation to get started](/docs/products/databases/order) - [Explore best practices for database pagination](/blog/post/best-pagination-technique) -- [Secure sensitive database fields with encrypted attributes](/blog/post/encrypted-attributes-for-sensitive-fields) +- [Secure sensitive database fields with encrypted columns](/blog/post/encrypted-attributes-for-sensitive-fields) diff --git a/src/routes/blog/post/announcing-bigint-columns/+page.markdoc b/src/routes/blog/post/announcing-bigint-columns/+page.markdoc index 90933b26583..5aaaa26159d 100644 --- a/src/routes/blog/post/announcing-bigint-columns/+page.markdoc +++ b/src/routes/blog/post/announcing-bigint-columns/+page.markdoc @@ -182,7 +182,7 @@ In the Appwrite Console, open your table, head to the **Columns** tab, and click ## From a Server SDK -Appwrite [Server SDKs](/docs/sdks#server) require an [API key](/docs/advanced/platform/api-keys) with `tables.write` (or `collections.write` for the legacy API). +Appwrite [Server SDKs](/docs/sdks#server) require an [API key](/docs/advanced/platform/api-keys) with `tables.write` and `columns.write`. {% multicode %} diff --git a/src/routes/blog/post/announcing-bulk-api/+page.markdoc b/src/routes/blog/post/announcing-bulk-api/+page.markdoc index 307096690c6..118eb8aaf67 100644 --- a/src/routes/blog/post/announcing-bulk-api/+page.markdoc +++ b/src/routes/blog/post/announcing-bulk-api/+page.markdoc @@ -10,14 +10,14 @@ category: announcement featured: false faqs: - question: "What is the Appwrite Bulk API?" - answer: "The Bulk API lets you create, update, upsert, or delete many documents in a single request using `createDocuments`, `updateDocuments`, `upsertDocuments`, and `deleteDocuments`. It is much faster than sending one request per document and reduces network overhead for batch workloads." + answer: "The Bulk API lets you create, update, upsert, or delete many rows in a single request using `createRows`, `updateRows`, `upsertRows`, and `deleteRows`. It is much faster than sending one request per row and reduces network overhead for batch workloads." - question: "Why is the Bulk API restricted to server-side SDKs?" answer: "Bulk operations typically involve high-volume data changes that should be authorized by trusted code, not by end users. Restricting the API to server SDKs (which use API keys) prevents clients from issuing mass writes or deletes against your project." - - question: "When should I use the Bulk API instead of single-document calls?" - answer: "Reach for the Bulk API any time you are processing data in batches: importing CSV files, syncing from another database, applying changes from a scheduled job, or cleaning up stale records. For interactive UI writes (one document at a time), the regular methods are usually clearer." + - question: "When should I use the Bulk API instead of single-row calls?" + answer: "Reach for the Bulk API any time you are processing data in batches: importing CSV files, syncing from another database, applying changes from a scheduled job, or cleaning up stale records. For interactive UI writes, the regular methods are usually clearer." - question: "How does Appwrite handle partial failures in a bulk request?" - answer: "Bulk operations are transactional at the document level, so individual document validations (such as permissions or schema mismatches) are reported alongside successes. Check the response to see which documents were processed and which failed, then retry only the failures." - - question: "Are there limits on how many documents I can send in one bulk call?" + answer: "Bulk operations are transactional at the row level, so individual row validations (such as permissions or schema mismatches) are reported alongside successes. Check the response to see which rows were processed and which failed, then retry only the failures." + - question: "Are there limits on how many rows I can send in one bulk call?" answer: "Yes. There is a maximum batch size per request that depends on payload size and the underlying database engine. The [bulk operations documentation](/docs/products/databases/bulk-operations) lists the current limits and recommended batch sizes for large imports." - question: "Does Bulk API work with self-hosted Appwrite?" answer: "Yes. Bulk API is available on both Appwrite Cloud and self-hosted installations that run the supporting version. You call the same methods from your server SDK regardless of where Appwrite is hosted." @@ -27,9 +27,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. +Writing or modifying large amounts of data in Appwrite Databases can require many row-level operations. Sending one request per row is inefficient for batch workloads, 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 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 @@ -49,12 +49,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 @@ -65,22 +65,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-csv-export/+page.markdoc b/src/routes/blog/post/announcing-csv-export/+page.markdoc index 077ddd6efc1..f6c6b3d7189 100644 --- a/src/routes/blog/post/announcing-csv-export/+page.markdoc +++ b/src/routes/blog/post/announcing-csv-export/+page.markdoc @@ -14,7 +14,7 @@ faqs: - question: "Can I export only filtered rows instead of the entire table?" answer: "Yes, CSV exports respect the filters and search parameters you've applied to the table view. This lets you export targeted slices of data without writing custom scripts. You can also choose specific columns to keep the output lean." - question: "What customization options are available for CSV exports?" - answer: "You can pick columns, apply queries, set a custom delimiter, include or skip the header row, and get notified by email when the export completes. Related documents are exported as IDs by default to keep the data structured. This makes the same export usable for engineering, analytics, or non-technical teammates." + answer: "You can pick columns, apply queries, set a custom delimiter, include or skip the header row, and get notified by email when the export completes. Related rows are exported as IDs by default to keep the data structured. This makes the same export usable for engineering, analytics, or non-technical teammates." - question: "Are CSV exports available on self-hosted Appwrite?" answer: "CSV exports launched first on Appwrite Cloud and will be coming to self-hosted in a later release. If you self-host, watch the Appwrite changelog and upgrade once the feature lands in a stable version." - question: "How does Appwrite handle large CSV exports without timing out?" @@ -58,7 +58,7 @@ You can: - Include or skip headers: Add an optional header row for readability. - Run in the background: Appwrite processes exports asynchronously, so you can continue working while it completes. - Get notified: Receive an email with a short-lived download link when the export is ready. -- Handle relationships cleanly: Related documents are exported as IDs by default, ensuring your data stays structured and easy to use. +- Handle relationships cleanly: Related rows are exported as IDs by default, ensuring your data stays structured and easy to use. This flexibility makes CSV exports powerful enough for developers and simple enough for non-technical team members. diff --git a/src/routes/blog/post/announcing-csv-imports/+page.markdoc b/src/routes/blog/post/announcing-csv-imports/+page.markdoc index 54974be0c31..3740b738bb1 100644 --- a/src/routes/blog/post/announcing-csv-imports/+page.markdoc +++ b/src/routes/blog/post/announcing-csv-imports/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post title: "Announcing CSV Import: Bring in large datasets to Appwrite with ease" -description: Learn how to import documents into your Appwrite collections using a simple CSV file, a new feature built on top of Appwrite's migration APIs. +description: Learn how to import rows into your Appwrite tables using a simple CSV file, a new feature built on top of Appwrite's migration APIs. date: 2025-07-01 # update this cover later, once available! cover: /images/blog/announcing-csv-imports/cover.avif @@ -10,42 +10,42 @@ author: darshan-pandya category: announcement featured: false faqs: - - question: "How do I import a CSV file into an Appwrite collection?" - answer: "Create a collection with the attributes you want, then upload a CSV file whose first row contains matching attribute names. You can upload a new file during import or pick an existing one from your storage bucket. [Appwrite](/docs/products/databases) validates each row before importing and runs the job in the background." - - question: "Can I assign custom document IDs when importing from CSV?" - answer: "Yes, include an optional `$id` column in the CSV and Appwrite will use those values as document IDs. If you omit the column, Appwrite generates unique IDs automatically. Custom IDs are useful when migrating from another system that already has stable identifiers." + - question: "How do I import a CSV file into an Appwrite table?" + answer: "Create a table with the columns you want, then upload a CSV file whose first row contains matching column names. You can upload a new file during import or pick an existing one from your storage bucket. [Appwrite](/docs/products/databases) validates each row before importing and runs the job in the background." + - question: "Can I assign custom row IDs when importing from CSV?" + answer: "Yes, include an optional `$id` column in the CSV and Appwrite will use those values as row IDs. If you omit the column, Appwrite generates unique IDs automatically. Custom IDs are useful when migrating from another system that already has stable identifiers." - question: "What format does the CSV file need to follow?" - answer: "The first row must be a header containing attribute names that exactly match your collection's attributes. Each subsequent row represents a document, with values separated by commas. All required attributes must be present, or the row will fail validation." + answer: "The first row must be a header containing column names that exactly match your table's columns. Each subsequent row represents a row, with values separated by commas. All required columns must be present, or the row will fail validation." - question: "How does Appwrite handle very large CSV imports?" answer: "The CSV import system runs as a background task and performs per-row validation, so it can handle production-scale files without blocking the Console. Built on top of [Appwrite's migration APIs](/docs/products/databases), it's designed for reliability across both small datasets and large imports." - question: "What happens if a row in my CSV fails validation?" - answer: "Appwrite validates each row before importing it, so invalid rows are flagged and skipped rather than corrupting your collection. Common causes are missing required attributes, type mismatches, or values that violate length or enum constraints. Fix the source CSV and re-run the import for those rows." + answer: "Appwrite validates each row before importing it, so invalid rows are flagged and skipped rather than corrupting your table. Common causes are missing required columns, type mismatches, or values that violate length or enum constraints. Fix the source CSV and re-run the import for those rows." - question: "What are common use cases for CSV imports?" answer: "CSV imports are useful for migrating user data from external systems, importing inventory records, seeding test environments, and onboarding structured content like FAQs or product catalogs. They're also handy when prototyping with realistic data from spreadsheets or third-party tools." --- -We're introducing a new way to populate your Appwrite databases: **document imports from CSV files**. +We're introducing a new way to populate your Appwrite databases: **row imports from CSV files**. -Built on top of Appwrite's migration APIs, this feature makes it easy to bring in large datasets, seed collections, or migrate structured data using only a CSV file. +Built on top of Appwrite's migration APIs, this feature makes it easy to bring in large datasets, seed tables, or migrate structured data using only a CSV file. -The CSV document import is useful for migrating user data from external systems, importing inventory records, seeding test environments, or onboarding structured content such as FAQs. +The CSV row import is useful for migrating user data from external systems, importing inventory records, seeding test environments, or onboarding structured content such as FAQs. # How it works -To get started, create a collection and define its attributes in the Appwrite Console. Your CSV file should follow a standard format: +To get started, create a table and define its columns in the Appwrite Console. Your CSV file should follow a standard format: -- The first row must be a header containing attribute names that match your collection -- Each subsequent row represents a document, with values separated by commas +- The first row must be a header containing column names that match your table +- Each subsequent row represents a row, with values separated by commas {% info title="Good to know" %} -You can optionally include the `$id` column to assign custom document IDs. +You can optionally include the `$id` column to assign custom row IDs. {% /info %} -![Collections screen](/images/blog/announcing-csv-imports/csv-import.avif) +![Tables screen](/images/blog/announcing-csv-imports/csv-import.avif) -All required attributes must be present in the CSV, and Appwrite will validate each row before importing it. +All required columns must be present in the CSV, and Appwrite will validate each row before importing it. -For example, if your collection contains attributes like `title`, `author`, `year`, and `available`, a valid CSV file would look like this: +For example, if your table contains columns like `title`, `author`, `year`, and `available`, a valid CSV file would look like this: ```text $id,title,author,year,available @@ -60,7 +60,7 @@ v42cj0quxp,Pride and Prejudice,Jane Austen,1813,true ## Uploading your CSV file You can upload a new file during import or select an existing one from your project's storage bucket. -The Console provides a guided interface to help you select the CSV and link it to your target collection. Once uploaded, the import process begins immediately. +The Console provides a guided interface to help you select the CSV and link it to your target table. Once uploaded, the import process begins immediately. ## Designed for scale diff --git a/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc b/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc index 487699609f5..5d29b734435 100644 --- a/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc +++ b/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc @@ -18,11 +18,11 @@ faqs: - question: "Will AI suggestions overwrite an existing table's schema?" answer: "No, Database AI suggestions are only offered when you're creating a new table. Existing tables stay untouched. If you want to evolve a schema, you can add columns and indexes manually or use [Appwrite's migration APIs](/docs/products/databases)." - question: "Why use AI suggestions instead of designing the schema by hand?" - answer: "Schemas designed by hand often miss timestamps, soft-delete fields, or indexes that you only realize you need later. AI suggestions bake those best practices in from the start, reduce naming inconsistencies across collections, and turn a slow setup step into a few seconds of review." + answer: "Schemas designed by hand often miss timestamps, soft-delete fields, or indexes that you only realize you need later. AI suggestions bake those best practices in from the start, reduce naming inconsistencies across tables, and turn a slow setup step into a few seconds of review." - question: "Is Database AI Suggestions available on self-hosted Appwrite?" answer: "Database AI Suggestions launched first on [Appwrite Cloud](/docs/products/databases). Check the Appwrite changelog for self-hosted availability, since features that depend on AI services are typically rolled out to Cloud before being available in self-hosted releases." --- -In many development workflows, setting up a database schema is one of the first and often one of the slowest steps. Starting with a blank schema, juggling naming conventions, remembering which fields need indexes, or ensuring every collection follows best practices can quickly become a chore. +In many development workflows, setting up a database schema is one of the first and often one of the slowest steps. Starting with a blank schema, juggling naming conventions, remembering which fields need indexes, or ensuring every table follows best practices can quickly become a chore. Whether spinning up a new project, designing an operational table for a feature, or standardizing schemas across multiple services, the setup should be fast, consistent, and reliable. @@ -51,7 +51,7 @@ This UI update complements Database AI Suggestions by making the schema design a Resulting in benefits such as: - **Faster setup:** Go from table name to sensible schema in seconds. -- **Consistency:** Keep field names and types aligned across collections. +- **Consistency:** Keep field names and types aligned across tables. - **Best practices baked in:** Get timestamps, soft-delete fields, enums, and indexes without having to remember them. - **Fewer errors:** Avoid schema issues that cause analytics problems or require migrations later. 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 11a63dabba5..56ac5abaeb5 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 @@ -12,13 +12,13 @@ featured: false callToAction: true faqs: - question: "How does Appwrite count database read and write operations?" - answer: "Most operations are counted by the number of documents affected, not the number of API calls. Fetching a collection of 50 documents in one call counts as 50 read operations. A query that returns no documents counts as a single operation. Writes are counted per document on create, update, and delete." + answer: "Most operations are counted by the number of rows affected, not the number of API calls. Fetching a table of 50 rows in one call counts as 50 read operations. A query that returns no rows counts as a single operation. Writes are counted per row on create, update, and delete." - question: "What's included in the free database operation quota?" answer: "The Free plan includes 500,000 read operations and 250,000 write operations per month. The Pro plan includes 1,750,000 reads and 750,000 writes monthly, and Enterprise has custom limits. Operations beyond the included quota are billed at $0.060 per 100,000 reads and $0.10 per 100,000 writes. See the [pricing page](/pricing) for details." - question: "How can I reduce my database operation usage?" answer: "Filter data server-side instead of fetching large datasets and filtering on the client. Use the `limit` and `offset` parameters to paginate so you only retrieve what you need. Cache frequently accessed data in your app or CDN, and monitor usage in the [Appwrite Console](/docs/products/databases) to spot inefficient query patterns." - question: "Where can I see my current database operation usage?" - answer: "You can review usage in your organization's usage page or in the usage section of a specific database. This shows reads and writes over time, broken down so you can identify which collections or workloads drive the most operations. Use this data to plan ahead before crossing the included quota." + answer: "You can review usage in your organization's usage page or in the usage section of a specific database. This shows reads and writes over time, broken down so you can identify which tables or workloads drive the most operations. Use this data to plan ahead before crossing the included quota." - question: "Does the pricing apply to self-hosted Appwrite?" answer: "No, the read and write operation pricing applies only to [Appwrite Cloud](/pricing). Self-hosted Appwrite has no per-operation billing because you provide the infrastructure. The same database APIs work in both, so you can develop locally on self-hosted and deploy to Cloud without code changes." - question: "Why is Appwrite charging for database operations now?" @@ -54,16 +54,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/announcing-database-upsert/+page.markdoc b/src/routes/blog/post/announcing-database-upsert/+page.markdoc index e5c52237fcc..265eabe8ab1 100644 --- a/src/routes/blog/post/announcing-database-upsert/+page.markdoc +++ b/src/routes/blog/post/announcing-database-upsert/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post title: "Announcing Database Upsert: Simplify your database interactions" -description: A cleaner, faster, and atomic way to manage your documents in Appwrite. +description: A cleaner, faster, and atomic way to manage your rows in Appwrite. date: 2025-07-08 cover: /images/blog/announcing-database-upsert/cover.avif timeToRead: 5 @@ -10,26 +10,26 @@ category: announcement featured: false faqs: - question: "What is an upsert?" - answer: "Upsert is a database operation that creates a document if it doesn't exist and updates it if it does. It removes the need to check for existence yourself or branch between create and update calls. The server handles both paths atomically in a single request." + answer: "Upsert is a database operation that creates a row if it doesn't exist and updates it if it does. It removes the need to check for existence yourself or branch between create and update calls. The server handles both paths atomically in a single request." - question: "How do I use upsert in Appwrite?" - answer: "Call `upsertDocument` on the [Appwrite Databases](/docs/products/databases) SDK with a database ID, collection ID, document ID, and the data you want to write. If the document with that ID exists, it's updated, otherwise it's created. This works the same across Cloud and self-hosted." + answer: "Call `upsertRow` on the [Appwrite Databases](/docs/products/databases) SDK with a database ID, table ID, row ID, and the data you want to write. If the row with that ID exists, it's updated, otherwise it's created. This works the same across Cloud and self-hosted." - question: "When should I use upsert instead of create or update?" - answer: "Use upsert when you don't know (or don't care) whether the document already exists, like syncing data from a mobile client, processing a background job, or ingesting events from IoT devices. It removes a round trip and a class of race conditions caused by checking existence before writing." + answer: "Use upsert when you don't know (or don't care) whether the row already exists, like syncing data from a mobile client, processing a background job, or ingesting events from IoT devices. It removes a round trip and a class of race conditions caused by checking existence before writing." - question: "Is upsert atomic in Appwrite?" - answer: "Yes, [Appwrite's](/docs/products/databases) upsert is fully atomic: the existence check and the write happen in the same operation on the server. That means two clients calling upsert on the same document ID can't both succeed in creating it, avoiding the classic check-then-write race condition." + answer: "Yes, [Appwrite's](/docs/products/databases) upsert is fully atomic: the existence check and the write happen in the same operation on the server. That means two clients calling upsert on the same row ID can't both succeed in creating it, avoiding the classic check-then-write race condition." - question: "Is upsert idempotent?" - answer: "Calling upsert with the same document ID and payload produces the same final state regardless of how many times the request runs. That makes retries safe, which is useful for unreliable networks, background workers, or anywhere you want at-least-once delivery semantics without duplicate data." + answer: "Calling upsert with the same row ID and payload produces the same final state regardless of how many times the request runs. That makes retries safe, which is useful for unreliable networks, background workers, or anywhere you want at-least-once delivery semantics without duplicate data." - question: "Can I upsert in bulk?" - answer: "Single-document upsert is available via `upsertDocument`. For larger workloads, look at [Appwrite's Bulk API](/docs/products/databases) and CSV imports, which let you write many documents in one operation. Pick the approach that matches the size and shape of your data." + answer: "Single-row upsert is available via `upsertRow`. For larger workloads, look at [Appwrite's Bulk API](/docs/products/databases) and CSV imports, which let you write many rows in one operation. Pick the approach that matches the size and shape of your data." --- -Working with databases often involves small but repetitive decisions like checking if a document exists, choosing between creating or updating, handling errors that come from guessing wrong. These steps are not difficult on their own, but over time they add complexity to your code and friction to your workflow. +Working with databases often involves small but repetitive decisions like checking if a row exists, choosing between creating or updating, handling errors that come from guessing wrong. These steps are not difficult on their own, but over time they add complexity to your code and friction to your workflow. To simplify this, we introduce Database Upsert in Appwrite. # How it works -Upsert allows you to create or update a document using a single API call. If the document does not exist, it is created. If it does, it is updated. You no longer need to write separate logic to check for existence or handle 404 responses. The server handles that for you. +Upsert allows you to create or update a row using a single API call. If the row does not exist, it is created. If it does, it is updated. You no longer need to write separate logic to check for existence or handle 404 responses. The server handles that for you. This change removes the need for client-side conditionals, reduces the number of requests between your app and the database, and helps avoid potential race conditions. It is a small shift in how you interact with the database, but one that can make your code cleaner and your application logic easier to follow. @@ -49,26 +49,26 @@ This brings you immediate benefits such as: Implementing Upsert is straightforward and intuitive: ```javascript -import { Client, Databases } from "appwrite"; +import { Client, TablesDB } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') .setProject(''); -const databases = new Databases(client); +const tablesDB = new TablesDB(client); -const result = await databases.upsertDocument( - '', - '', - '', - { +const result = await tablesDB.upsertRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { 'status': 'succeeded', 'amount': 4999, 'currency': 'usd', 'invoiceId': 'in_1Rd...', 'checks': ['cvc', 'zip'], } -) +}) ``` Database Upsert was developed to enhance developer productivity and satisfaction, providing a feature that matches or surpasses competitive solutions. Whether using Appwrite Cloud or a self-hosted setup, Database Upsert integrates smoothly into your development workflow. @@ -77,7 +77,7 @@ This feature simplifies your database interactions, enhancing efficiency, reduci # More resources -- [Read the documentation to get started](/docs/products/databases/documents) +- [Read the documentation to get started](/docs/products/databases/rows) - [Announcing Bulk API: Handle heavy data workloads with ease](/blog/post/announcing-bulk-api) - [Build a personal CRM with SvelteKit and Appwrite Databases](/blog/post/build-personal-crm-sveltekit) -- [Announcing: Document imports from CSV files](/blog/post/announcing-csv-imports) +- [Announcing: CSV imports for Appwrite Databases](/blog/post/announcing-csv-imports) diff --git a/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc b/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc index 97e9c7fa776..f0cda63dfba 100644 --- a/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc +++ b/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post -title: "Announcing Encrypted string attribute support: Built-in encryption for sensitive fields" -description: Easily encrypt sensitive string fields at rest, with no manual encryption logic. +title: "Announcing encrypted text column support: Built-in encryption for sensitive fields" +description: Easily encrypt sensitive text fields at rest, with no manual encryption logic. date: 2025-07-10 lastUpdated: 2026-05-22 cover: /images/blog/announcing-encrypted-string-attributes/cover.avif @@ -12,20 +12,20 @@ featured: false faqs: - question: "What is encryption at rest and why does it matter?" answer: "Encryption at rest means data is encrypted on disk when stored, so an attacker who gains access to the underlying database files still can't read the raw values. It matters for fintech, healthcare, messaging, and any app handling personal or regulated data. It's a baseline requirement for many compliance frameworks." - - question: "How do I encrypt a string attribute in Appwrite?" - answer: "Mark the string attribute as encrypted when you create it in your [Appwrite Databases](/docs/products/databases) collection. From that point, Appwrite encrypts the value before writing it to disk and decrypts it transparently when you read it. Your client code stays exactly the same." - - question: "What encryption algorithm does Appwrite use for encrypted attributes?" + - question: "How do I encrypt a text column in Appwrite?" + answer: "Mark the text column as encrypted when you create it in your [Appwrite Databases](/docs/products/databases) table. From that point, Appwrite encrypts the value before writing it to disk and decrypts it transparently when you read it. Your client code stays exactly the same." + - question: "What encryption algorithm does Appwrite use for encrypted columns?" answer: "Appwrite uses AES-128 in Galois/Counter Mode (GCM), an industry-standard authenticated encryption algorithm. The encryption is applied server-side before the value reaches the database, and Appwrite manages keys for you. Deployments that require FIPS-compliant environments are also supported." - - question: "Can I query or filter on encrypted string attributes?" - answer: "No, encrypted attributes can't be queried or used in filters. Encryption is intentionally one-way for queries to prevent leaking sensitive information through search patterns. If you need to look up records by a sensitive value, store a separate hashed or tokenized field for lookups." + - question: "Can I query or filter on encrypted text columns?" + answer: "No, encrypted columns can't be queried or used in filters. Encryption is intentionally one-way for queries to prevent leaking sensitive information through search patterns. If you need to look up records by a sensitive value, store a separate hashed or tokenized column for lookups." - question: "Do I need to manage encryption keys myself?" - answer: "No, [Appwrite](/docs/advanced/security) handles key management transparently. You don't generate, store, or rotate keys yourself, which removes a common source of implementation errors. The clients see encrypted fields as regular strings." - - question: "Is encrypted string attribute support available on the Free plan?" - answer: "Encrypted string attributes are available on Appwrite Cloud's Pro and Enterprise plans, plus all self-hosted deployments. If you're on the Free plan, you'd need to upgrade or self-host to use this feature." + answer: "No, [Appwrite](/docs/advanced/security) handles key management transparently. You don't generate, store, or rotate keys yourself, which removes a common source of implementation errors. The clients see encrypted fields as regular text values." + - question: "Is encrypted text column support available on the Free plan?" + answer: "Encrypted text columns are available on Appwrite Cloud's Pro and Enterprise plans, plus all self-hosted deployments. If you're on the Free plan, you'd need to upgrade or self-host to use this feature." --- -Appwrite is secure by default. We build every single product and feature with the highest regard for security. +Appwrite is secure by default. We build every single product and feature with the highest regard for security. -With this in mind, we introduce **Encrypted string attribute support** for Appwrite Databases, a new feature to enhance your databases’ security. +With this in mind, we introduce **encrypted text column support** for Appwrite Databases, a new feature to enhance your databases’ security. This critical addition lets you store sensitive data securely, encrypted at rest, directly within your databases. @@ -33,7 +33,7 @@ This critical addition lets you store sensitive data securely, encrypted at rest Previously, storing confidential or sensitive data required manual encryption and decryption. This not only increased complexity but also introduced potential security risks from implementation errors. -With Encrypted string attribute support, Appwrite handles encryption transparently using industry-standard AES-GCM encryption. Your data remains secure, encrypted server-side, without additional effort or complex custom code. Additionally, Appwrite ensures compliance with common security standards, including support for environments requiring FIPS compliance. +With encrypted text column support, Appwrite handles encryption transparently using industry-standard AES-GCM encryption. Your data remains secure, encrypted server-side, without additional effort or complex custom code. Additionally, Appwrite ensures compliance with common security standards, including support for environments requiring FIPS compliance. # Serving security-focused industries @@ -42,15 +42,15 @@ It is essential for creating secure apps in fintech, healthcare, messaging, and Key features entail: - **Server-side encryption at rest**: Your data is encrypted on the server automatically. -- **Transparent encryption**: Clients see and interact with encrypted fields just like regular strings. +- **Transparent encryption**: Clients see and interact with encrypted fields just like regular text values. - **Zero-effort protection**: No need to implement manual encryption or key management. - **Secure by design**: Fields can't be queried, ensuring sensitive data stays protected. # Setting the security standard -Appwrite delivers comprehensive, secure, easy-to-use encryption that is seamlessly integrated into your workflow. When a string attribute is marked as encrypted, Appwrite applies AES-128 encryption in Galois/Counter Mode (GCM) before writing it to the database. +Appwrite delivers comprehensive, secure, easy-to-use encryption that is seamlessly integrated into your workflow. When a text column is marked as encrypted, Appwrite applies AES-128 encryption in Galois/Counter Mode (GCM) before writing it to the database. -This new feature is available on Appwrite Cloud Pro and Enterprise plans, and self-hosted. Encrypted string attribute support significantly enhances your application's security posture, making Appwrite a trusted choice for handling sensitive data. +This new feature is available on Appwrite Cloud Pro and Enterprise plans, and self-hosted. Encrypted text column support significantly enhances your application's security posture, making Appwrite a trusted choice for handling sensitive data. # More resources diff --git a/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc b/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc index 024c6367a6e..9359c1cace2 100644 --- a/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc +++ b/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc @@ -28,7 +28,7 @@ When you’re spinning up a new feature, environment, or test pipeline, schema c That’s exactly why we’re announcing **Full Schema Creation** for Appwrite Databases. -With Full Schema Creation, you can define an entire table, its attributes and indexes, in a **single, synchronous request**. When the call returns, the table is immediately ready for reads and writes. If anything fails along the way, nothing is created. No partial schemas. No waiting. No brittle setup scripts. +With Full Schema Creation, you can define an entire table, its columns and indexes, in a **single, synchronous request**. When the call returns, the table is immediately ready for reads and writes. If anything fails along the way, nothing is created. No partial schemas. No waiting. No brittle setup scripts. # One request. One outcome. Fully usable. @@ -45,39 +45,6 @@ This step-wise flow introduced delays, race conditions in automation scripts, an With Full Schema Creation, all of that collapses into a single operation. You define everything upfront, Appwrite validates it all together, and either the entire schema is created successfully, or nothing is. -```js -const table = await tablesDB.createTable({ - databaseId: 'contacts_db', - tableId: 'contacts', - name: 'Contacts', - columns: [ - { - key: 'email', - type: 'email', - required: true - }, - { - key: 'name', - type: 'string', - size: 255, - required: true - }, - { - key: 'is_active', - type: 'boolean', - required: true - }, - ], - indexes: [ - { - key: 'idx_email', - type: 'unique', - attributes: ['email'] - } - ] -}); -``` - # How it works Full Schema Creation introduces an atomic, synchronous way to provision database schemas. Here’s what happens under the hood: diff --git a/src/routes/blog/post/announcing-go-support/+page.markdoc b/src/routes/blog/post/announcing-go-support/+page.markdoc index cdf3da51d90..d356ed5369d 100644 --- a/src/routes/blog/post/announcing-go-support/+page.markdoc +++ b/src/routes/blog/post/announcing-go-support/+page.markdoc @@ -229,7 +229,7 @@ func Main(Context openruntimes.Context) openruntimes.Response { } ``` -Some of our API endpoints such as the ones that return preferences from the Accounts and Teams APIs as well as the ones the return documents from the Appwrite Database are loosely typed because they can be customised by user. For these, the structs offer a `Decode()` method, as seen in the following example. +Some of our API endpoints such as the ones that return preferences from the Accounts and Teams APIs as well as the ones that return rows from Appwrite Databases are loosely typed because they can be customised by user. For these, the structs offer a `Decode()` method, as seen in the following example. ```go package handler @@ -238,19 +238,20 @@ import ( "log" "os" - "github.com/open-runtimes/types-for-go/v4/openruntimes" "github.com/appwrite/sdk-for-go/appwrite" + "github.com/appwrite/sdk-for-go/models" + "github.com/open-runtimes/types-for-go/v4/openruntimes" ) type Profile struct { - *models.Document + *models.Row Name string `json:"name"` Verified bool `json:"verified"` } type ProfileList struct { - *models.DocumentList - Documents []Profile `json:"documents"` + *models.RowList + Rows []Profile `json:"rows"` } func Main(Context openruntimes.Context) openruntimes.Response { @@ -260,9 +261,9 @@ func Main(Context openruntimes.Context) openruntimes.Response { appwrite.WithKey(Context.Req.Headers["x-appwrite-key"]), ) - databases := appwrite.NewDatabases(client) + tablesDB := appwrite.NewTablesDB(client) - response, err := databases.ListDocuments("main", "profiles") + response, err := tablesDB.ListRows("main", "profiles") if err != nil { Context.Error(err) return Context.Res.Text("Internal error" Context.Res.WithStatusCode(500)) @@ -275,11 +276,11 @@ func Main(Context openruntimes.Context) openruntimes.Response { return Context.Res.Text("Internal error", Context.Res.WithStatusCode(500)) } - for _, profile := range profiles.Documents { + for _, profile := range profiles.Rows { Context.Log(profile.Id, profile.Name, profile.Verified) } - return Context.Res.Json(profiles.Documents) + return Context.Res.Json(profiles.Rows) } ``` diff --git a/src/routes/blog/post/announcing-inversion-queries/+page.markdoc b/src/routes/blog/post/announcing-inversion-queries/+page.markdoc index d03780bded4..5eaa21dd407 100644 --- a/src/routes/blog/post/announcing-inversion-queries/+page.markdoc +++ b/src/routes/blog/post/announcing-inversion-queries/+page.markdoc @@ -22,7 +22,7 @@ faqs: - question: "Are inversion queries available on self-hosted Appwrite?" answer: "Yes, inversion queries are available on both Appwrite Cloud and self-hosted deployments. If you're on a recent self-hosted version, the new `NOT` operators are exposed through your client SDKs just like the existing query operators." --- -When you need to exclude certain records, the usual approach is to fetch a broad set of rows, sometimes even the entire collection, and then filter them in your application code. +When you need to exclude certain records, the usual approach is to fetch a broad set of rows, sometimes even the entire table, and then filter them in your application code. It works, but it also means you are moving more data across the network than necessary, increasing payload sizes, and adding extra logic to your client. In smaller projects this might not be noticeable, but as your datasets grow, it quickly becomes inefficient. diff --git a/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc b/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc index 0e720a97473..49e76051f77 100644 --- a/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc +++ b/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc @@ -23,7 +23,7 @@ faqs: answer: "Chain `.update()` (or `.create()`, `.delete()`) on the channel helper to filter by event type. For example, `Channel.tablesdb('db').table('t').row().update()` only delivers events when an existing row is updated, not when one is created or deleted." --- -If you've built realtime features in your apps, you've likely written channel strings by hand: concatenating IDs, formatting wildcards, and hoping you didn't introduce a typo that would silently break your subscription. While writing channel strings like `databases.*.tables.*.rows.*` works, it's error-prone and harder to maintain as your application grows. +If you've built realtime features in your apps, you've likely written channel strings by hand: concatenating IDs, formatting wildcards, and hoping you didn't introduce a typo that would silently break your subscription. While writing channel strings like `tablesdb.*.tables.*.rows.*` works, it's error-prone and harder to maintain as your application grows. To make realtime subscriptions clearer and safer, Appwrite is introducing **Channel helpers**: a type-safe, fluent API for building realtime channel subscriptions. diff --git a/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc b/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc index f9405ce9316..33921851ec6 100644 --- a/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc +++ b/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc @@ -11,7 +11,7 @@ featured: false faqs: - question: "What are time helper queries in Appwrite Databases?" answer: "Time helper queries are dedicated operators (`createdBefore`, `createdAfter`, `updatedBefore`, `updatedAfter`) that filter rows by their `$createdAt` and `$updatedAt` timestamps. They replace verbose range comparisons against system attributes with a more direct, intent-revealing syntax." - - question: "How do I filter Appwrite documents by date?" + - question: "How do I filter Appwrite rows by date?" answer: "Use the time helper queries with an ISO-8601 date string, for example `Query.createdAfter(new Date('2024-01-01').toISOString())`. You can combine multiple helpers in the same query to express a range, such as everything created in a specific month or updated after a given backup." - question: "What is ISO-8601 date format?" answer: "ISO-8601 is the international standard for representing dates and times, for example `2024-10-05T14:48:00Z`. The `Z` suffix denotes UTC. Appwrite expects timestamps in ISO-8601 because it is unambiguous and sorts correctly as a string, which makes it ideal for queries and APIs." @@ -26,11 +26,11 @@ If you’ve ever built a feed, a dashboard, or an audit report, you know how oft These time-based queries show up in so many of the apps you build, and when you’re working with dates, you naturally think in terms of *before* and *after*. -To make this workflow smoother, we’re excited to announce **time helper queries:** A new way to filter your documents by creating and updating times using simple, expressive syntax. +To make this workflow smoother, we’re excited to announce **time helper queries:** A new way to filter rows by creation and update times using simple, expressive syntax. # A better way to query by dates -Many workflows depend on knowing when a document was created or last updated. Whether you’re loading new items into a feed, exporting data that has changed since the previous run, or applying retention rules, you often need to filter by time. +Many workflows depend on knowing when a row was created or last updated. Whether you’re loading new items into a feed, exporting data that has changed since the previous run, or applying retention rules, you often need to filter by time. Previously, this meant comparing against `$createdAt` or `$updatedAt` using range operators. That approach works, but it adds extra noise to your queries. To make this more direct, you can now use dedicated helpers that map exactly to these common cases: @@ -66,7 +66,7 @@ Time-based filtering shows up in countless scenarios: - **Analytics dashboards:** Chart data for “last 7 days” or “last quarter.” - **Exports & reports:** Generate monthly summaries or audits. - **Archival & compliance:** Isolate data for retention or deletion policies. -- **Incremental jobs:** Process only documents updated after your last run. +- **Incremental jobs:** Process only rows updated after your last run. # Immediate benefits diff --git a/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc b/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc index 0f3645afe15..2decabef48d 100644 --- a/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc +++ b/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc @@ -10,11 +10,11 @@ category: announcement featured: false faqs: - question: "What are timestamp overrides in Appwrite Databases?" - answer: "Timestamp overrides let you set `$createdAt` and `$updatedAt` manually when creating or importing documents instead of having Appwrite auto-stamp the current time. This is useful for migrating historical data, preserving original event order, and maintaining accurate audit trails." + answer: "Timestamp overrides let you set `$createdAt` and `$updatedAt` manually when creating or importing rows instead of having Appwrite auto-stamp the current time. This is useful for migrating historical data, preserving original event order, and maintaining accurate audit trails." - question: "Why do I need to override timestamps in Appwrite?" answer: "When you import data from another system, automatic timestamps make every record look brand new, which breaks analytics, feeds, and user-facing timelines. Overrides keep the original event time so a user's join date or an order's placement time stays accurate after the migration." - question: "How do I set a custom $createdAt value in Appwrite?" - answer: "Include `$createdAt` (and/or `$updatedAt`) as fields in your document payload when calling a privileged API. The value must be a valid ISO-8601 string like `2023-10-05T14:48:00Z`. Omit the fields and Appwrite stamps them automatically as usual." + answer: "Include `$createdAt` (and/or `$updatedAt`) as fields in your row payload when calling a privileged API. The value must be a valid ISO-8601 string like `2023-10-05T14:48:00Z`. Omit the fields and Appwrite stamps them automatically as usual." - question: "Can I set custom timestamps from a client SDK?" answer: "No. Timestamp overrides require an API key or Console access via the CSV Import wizard for security reasons. Allowing arbitrary client-set timestamps would let users forge audit trails, so the feature is restricted to privileged callers." - question: "What is ISO-8601 and why does Appwrite require it?" @@ -26,11 +26,11 @@ In many data workflows, imported records automatically take on the time they are Whether it is maintaining accurate audit trails, reliable analytics, proper chronological order, or a customer’s “joined on” date, every detail matters. -That’s why we’re introducing **Timestamp Overrides**. A simple but powerful way to set `$createdAt` and `$updatedAt` manually when importing or creating documents, so your history stays accurate without extra steps. This means your data looks exactly how it should from day one, with no extra scripts or workarounds. +That’s why we’re introducing **Timestamp Overrides**. A simple but powerful way to set `$createdAt` and `$updatedAt` manually when importing or creating rows, so your history stays accurate without extra steps. This means your data looks exactly how it should from day one, with no extra scripts or workarounds. # Preserve timestamps without extra workarounds -Appwrite has always managed timestamps automatically, setting `$createdAt` and `$updatedAt` to the current time whenever a document is created or updated. That’s perfect for most workflows, like building new apps or adding fresh data, because it’s accurate, automatic, and requires zero extra effort. +Appwrite has always managed timestamps automatically, setting `$createdAt` and `$updatedAt` to the current time whenever a row is created or updated. That’s perfect for most workflows, like building new apps or adding fresh data, because it’s accurate, automatic, and requires zero extra effort. Migrations and bulk imports, however, require a different approach. When you import existing data, automatic timestamps overwrite the original ones, making every record look new and breaking analytics, feeds, and user-facing timelines. @@ -52,10 +52,10 @@ This change isn’t just about convenience. It unlocks a few tangible benefits f Before you go ahead and start using this feature, here are a few important points about how Timestamp Overrides work: - **Privileged-only:** This feature requires an API key or Console access via CSV Import. It isn’t available through client-side SDK calls for security reasons. -- **Opt-in per document:** If you leave `$createdAt` or `$updatedAt` out of your payload, Appwrite will auto-stamp them as usual. +- **Opt-in per row:** If you leave `$createdAt` or `$updatedAt` out of your payload, Appwrite will auto-stamp them as usual. - **Validation:** Both fields must be valid ISO-8601 strings (e.g., `2023-10-05T14:48:00Z`). Invalid values are rejected. - **Partial override:** You can set one field and let Appwrite handle the other; for example, preserve `$createdAt` but allow `$updatedAt` to update automatically. -- **SDK usage:** Include the fields inside the document data itself; no new API parameters or endpoints are required. +- **SDK usage:** Include the fields inside the row data itself; no new API parameters or endpoints are required. # Built for enterprise‑grade data integrity @@ -70,6 +70,6 @@ Whether importing a few thousand records or years of historical data, this featu It’s available now on **Cloud**, and will be coming to **Self-Hosted** in the next release. # More resources -- [Read the documentation to learn more](/docs/products/databases/documents#custom-timestamps) +- [Read the documentation to learn more](/docs/products/databases/rows#custom-timestamps) - [Announcing CSV Import: Bring in large datasets to Appwrite with ease](/blog/post/announcing-csv-imports) - [Announcing Bulk API: Handle heavy data workloads with ease](/blog/post/announcing-bulk-api) diff --git a/src/routes/blog/post/announcing-transactions-api/+page.markdoc b/src/routes/blog/post/announcing-transactions-api/+page.markdoc index a8b37e52c33..cb7ce120226 100644 --- a/src/routes/blog/post/announcing-transactions-api/+page.markdoc +++ b/src/routes/blog/post/announcing-transactions-api/+page.markdoc @@ -56,7 +56,7 @@ When ready, commit the transaction. Appwrite validates all staged operations (pe You can explicitly abort a transaction or let it expire. Idle transactions are automatically cleaned up after a configurable timeout, anywhere between 1 minute and 1 hour, with a default of 5 minutes, to avoid resource leaks or incomplete workflows. -This flow isolates your changes until you're confident they’re valid, giving you a safe way to manage complex, interdependent writes, especially useful in scenarios like user provisioning, multi-collection updates, or third-party sync operations. +This flow isolates your changes until you're confident they’re valid, giving you a safe way to manage complex, interdependent writes, especially useful in scenarios like user provisioning, multi-table updates, or third-party sync operations. # Example diff --git a/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc b/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc index 741b217a7da..c35830b474d 100644 --- a/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc +++ b/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post title: "Introducing Type generation: Automate your type definitions with Appwrite" -description: "Automatically generate types from your collections with support for multiple languages." +description: "Automatically generate types from your tables with support for multiple languages." date: 2025-06-24 cover: /images/blog/type-generation-feature/cover.avif timeToRead: 4 @@ -9,20 +9,20 @@ author: chirag-aggarwal category: announcement faqs: - question: "What is Appwrite type generation?" - answer: "Type generation is a CLI command that produces type definitions in your project's language directly from your Appwrite collections and tables. It keeps client code in sync with your database schema, reducing the chance of typos and stale types when the schema changes." + answer: "Type generation is a CLI command that produces type definitions in your project's language directly from your Appwrite tables. It keeps client code in sync with your database schema, reducing the chance of typos and stale types when the schema changes." - question: "Which languages does Appwrite type generation support?" answer: "The Appwrite CLI generates types for TypeScript, JavaScript, PHP, Swift, Dart, Java, and Kotlin. The CLI detects your project's language automatically, or you can pass options to specify it explicitly. More languages are added over time." - - question: "How do I generate types from my Appwrite collections?" - answer: "Install the [Appwrite CLI](/docs/tooling/command-line/installation), initialize your project, run `appwrite pull collections`, then run `appwrite types `. The CLI writes the generated type definitions into the specified folder, ready to import in your application code." + - question: "How do I generate types from my Appwrite tables?" + answer: "Install the [Appwrite CLI](/docs/tooling/command-line/installation), initialize your project, run `appwrite pull tables`, then run `appwrite types `. The CLI writes the generated type definitions into the specified folder, ready to import in your application code." - question: "Do I need to regenerate types every time my schema changes?" - answer: "Yes. After updating columns, indexes, or relationships in your [Appwrite Databases](/docs/products/databases), run `appwrite pull collections` followed by `appwrite types` to refresh the generated definitions. You can wire this into a CI step or pre-commit hook to keep types current." + answer: "Yes. After updating columns, indexes, or relationships in your [Appwrite Databases](/docs/products/databases), run `appwrite pull tables` followed by `appwrite types` to refresh the generated definitions. You can wire this into a CI step or pre-commit hook to keep types current." - question: "Why use generated types instead of writing them manually?" answer: "Manual types drift from the schema over time as columns are added, renamed, or removed, leading to subtle runtime bugs. Generation guarantees the types always reflect the current schema, which catches mismatches at compile time and removes a tedious manual sync step from your workflow." - question: "Does Appwrite type generation work with self-hosted Appwrite?" answer: "Yes. Type generation works against any Appwrite project the CLI can authenticate to, whether on Appwrite Cloud or self-hosted. The same commands and output formats apply, so your local workflow stays consistent regardless of deployment target." --- -We're excited to announce Appwrite’s newest CLI feature, **Type generation**. Designed specifically to enhance your developer experience. Type generation automates the creation of type definitions directly from your database collections, seamlessly integrating with your preferred programming language. +We're excited to announce Appwrite’s newest CLI feature, **Type generation**. Designed specifically to enhance your developer experience. Type generation automates the creation of type definitions directly from your database tables, seamlessly integrating with your preferred programming language. # Say goodbye to manual mapping @@ -36,10 +36,10 @@ Whether you work with PHP, Swift, Dart, TypeScript, JavaScript, Java, or Kotlin, Using Type generation is straightforward. -First, ensure you have the [Appwrite CLI](/docs/tooling/command-line/installation#getting-started) installed and your project is [initialised](/docs/tooling/command-line/installation#initialization). Then, run the following command in your terminal to pull collections from your Appwrite project: +First, ensure you have the [Appwrite CLI](/docs/tooling/command-line/installation#getting-started) installed and your project is [initialised](/docs/tooling/command-line/installation#initialization). Then, run the following command in your terminal to pull tables from your Appwrite project: ```bash -appwrite pull collections +appwrite pull tables ``` To generate types, you can use the Appwrite CLI command: diff --git a/src/routes/blog/post/appwrite-1-8-0-self-hosted-release/+page.markdoc b/src/routes/blog/post/appwrite-1-8-0-self-hosted-release/+page.markdoc index 5c88fd005d3..fca27565f0c 100644 --- a/src/routes/blog/post/appwrite-1-8-0-self-hosted-release/+page.markdoc +++ b/src/routes/blog/post/appwrite-1-8-0-self-hosted-release/+page.markdoc @@ -11,7 +11,7 @@ faqs: - question: "What is new in Appwrite 1.8.0 for self-hosted users?" answer: "Appwrite 1.8.0 introduces TablesDB, a Transactions API, spatial columns, CSV import, a Bulk API, Database Upsert, auto-increment support, opt-in relationship loading, and new time-based and inversion queries for [Databases](/docs/products/databases). It also adds new runtimes and performance improvements across the self-hosted stack." - question: "What is TablesDB in Appwrite?" - answer: "TablesDB is a relational-style API on top of Appwrite [Databases](/docs/products/databases). It introduces tables, columns, and rows alongside the existing collections and documents model, with endpoints like createTable, createColumn, and createRow for developers who prefer a structured schema." + answer: "TablesDB is a relational-style API on top of Appwrite [Databases](/docs/products/databases). It uses tables, columns, and rows, with endpoints like createTable, createTextColumn, and createRow for developers who prefer a structured schema." - question: "How do Appwrite database transactions work?" answer: "You stage multiple operations across one or more tables and only commit them when you are ready. If every step succeeds, Appwrite commits atomically. If anything fails (permissions, conflicts, validation), Appwrite rolls back the entire transaction so your data stays consistent." - question: "Can I use Appwrite to store and query geographic data?" @@ -35,9 +35,9 @@ Over the past two months, we've been rolling out a series of major database upgr ## Introducing TablesDB -Meet **TablesDB.** A major step forward in how Appwrite handles structured data. It introduces familiar **relational concepts** like tables, columns, and rows, along with a new **TablesDB API** that aligns with standard CRUD patterns such as `createTable`, `createRow`, and `createColumn`. +Meet **TablesDB.** A major step forward in how Appwrite handles structured data. It introduces familiar **relational concepts** like tables, columns, and rows, along with a new **TablesDB API** that aligns with standard CRUD patterns such as `createTable`, `createRow`, and `createTextColumn`. -Built for developers who prefer a more structured model, TablesDB offers the clarity of relational databases while remaining fully compatible with Appwrite's existing collections and documents. It's ideal for analytics, structured datasets, and complex relationships, giving you more control and flexibility when modelling data at scale. +Built for developers who prefer a more structured model, TablesDB offers the clarity of relational databases while keeping existing projects compatible. It's ideal for analytics, structured datasets, and complex relationships, giving you more control and flexibility when modelling data at scale. [Learn more about TablesDB](/blog/post/announcing-appwrite-databases-new-ui) @@ -60,7 +60,7 @@ With this new API, you can store points, lines, and polygons directly in Appwrit ## CSV import -CSV import is a new way to populate your Appwrite databases from CSV files. Built on top of Appwrite's migration APIs, this feature makes it easy to bring in large datasets, seed collections, or migrate structured data using only a CSV file. +CSV import is a new way to populate your Appwrite databases from CSV files. Built on top of Appwrite's migration APIs, this feature makes it easy to bring in large datasets, seed tables, or migrate structured data using only a CSV file. [Learn more about CSV import](/blog/post/announcing-csv-imports) 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 20fac797fcb..bc69dfc54d1 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 @@ -217,9 +217,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({ +tablesDB.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.contains('content', ['happy', 'love']) ] @@ -229,9 +229,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({ +tablesDB.listRows({ databaseId: '', - collectionId: '', + tableId: '', queries: [ Query.or([ Query.contains('name','ivy'), diff --git a/src/routes/blog/post/appwrite-for-hackathons-build-fast-ship-faster/+page.markdoc b/src/routes/blog/post/appwrite-for-hackathons-build-fast-ship-faster/+page.markdoc index 873f2e6dde9..d998814007d 100644 --- a/src/routes/blog/post/appwrite-for-hackathons-build-fast-ship-faster/+page.markdoc +++ b/src/routes/blog/post/appwrite-for-hackathons-build-fast-ship-faster/+page.markdoc @@ -85,7 +85,7 @@ Appwrite provides structured databases with built-in APIs and permissions, allow Appwrite provides scalable databases with built-in APIs and permissions. Developers benefit from type safety when working with database tables and rows through generic methods, and can specify custom model types for full type safety when interacting with databases. The Appwrite API layer is designed to be extremely fast by leveraging in-memory caching. -Instead of managing database servers and writing API layers manually, developers can create collections, store application data, and define access rules directly through the Appwrite dashboard or APIs. +Instead of managing database servers and writing API layers manually, developers can create tables, store application data, and define access rules directly through the Appwrite dashboard or APIs. ## File storage diff --git a/src/routes/blog/post/appwrite-functions-guide/+page.markdoc b/src/routes/blog/post/appwrite-functions-guide/+page.markdoc index 1a27f49e61c..753b528376b 100644 --- a/src/routes/blog/post/appwrite-functions-guide/+page.markdoc +++ b/src/routes/blog/post/appwrite-functions-guide/+page.markdoc @@ -137,7 +137,10 @@ const execution = await functions.createExecution({ async: true }); // execution.status will be "waiting" or "processing" -// poll later with functions.getExecution(functionId, execution.$id) +// poll later with functions.getExecution({ +// functionId, +// executionId: execution.$id +// }) ``` HTTP-triggered functions always behave synchronously from the HTTP client's perspective: the connection stays open until the function returns a response or times out. diff --git a/src/routes/blog/post/appwrite-indexes/+page.markdoc b/src/routes/blog/post/appwrite-indexes/+page.markdoc index cf32465c960..4b717ee63ad 100644 --- a/src/routes/blog/post/appwrite-indexes/+page.markdoc +++ b/src/routes/blog/post/appwrite-indexes/+page.markdoc @@ -13,13 +13,13 @@ faqs: - question: "What is a database index in Appwrite?" answer: "An index is a separate, sorted data structure that the database (MariaDB under the hood) maintains alongside your table. It stores the values of one or more columns with pointers back to the matching rows, so queries can jump directly to results instead of scanning the entire table. Indexes are essential for keeping queries fast as your data grows." - question: "Which index types does Appwrite support?" - answer: "Appwrite supports four index types: key indexes for equality, range, and ordering queries; unique indexes which add a uniqueness constraint on top of a key index; fulltext indexes which are required for Query.search on string columns; and spatial indexes for geographic Point, Line, and Polygon columns used with geo queries." + answer: "Appwrite supports four index types: key indexes for equality, range, and ordering queries; unique indexes which add a uniqueness constraint on top of a key index; fulltext indexes which are required for Query.search on text columns; and spatial indexes for geographic Point, Line, and Polygon columns used with geo queries." - question: "Which columns should I index?" - answer: "Index columns you filter on with Query.equal, Query.greaterThan, Query.between, and similar operators, columns you sort on with Query.orderAsc or Query.orderDesc, columns that must be unique like emails or slugs, and string columns you run Query.search against. Spatial columns used in geo queries should have a spatial index." + answer: "Index columns you filter on with Query.equal, Query.greaterThan, Query.between, and similar operators, columns you sort on with Query.orderAsc or Query.orderDesc, columns that must be unique like emails or slugs, and text columns you run Query.search against. Spatial columns used in geo queries should have a spatial index." - question: "Can adding too many indexes hurt performance?" answer: "Yes. Every index has to be updated on inserts, updates, and deletes, so a table with many redundant indexes pays a write-time cost that scales with the index count. Add indexes for queries you actually run, drop ones you do not use, and prefer composite indexes over multiple single-column indexes when several columns are queried together." - question: "How do I create an index in Appwrite?" - answer: "You can create indexes from the Console by opening the table and using the Indexes tab, or programmatically with the Databases API by calling createIndex with a key, type, attributes array, and orders array. Indexes are built asynchronously and report a processing status until ready, which can take time on large tables." + answer: "You can create indexes from the Console by opening the table and using the Indexes tab, or programmatically with the TablesDB API by calling createIndex with a key, type, columns array, and orders array. Indexes are built asynchronously and report a processing status until ready, which can take time on large tables." --- If your Appwrite queries are slow, or getting slower as your data grows, the most likely fix is an index. Without indexes, every filtered or sorted query requires a full table scan: the database reads every row and checks whether it matches your conditions. That is fine for a table with 100 rows. It becomes a serious problem at 100,000. @@ -55,7 +55,7 @@ Unique indexes are enforced at the database level, not just in your application ## Fulltext index -A fulltext index is required for [`Query.search`](/docs/products/databases/queries). It enables word-level text search across string columns. Without a fulltext index on the target column, `Query.search` returns an error. +A fulltext index is required for [`Query.search`](/docs/products/databases/queries). It enables word-level text search across text columns. Without a fulltext index on the target column, `Query.search` returns an error. Fulltext indexes are suited for content columns like `title`, `body`, or `description`. They are not appropriate for short identifiers or numeric fields. @@ -67,7 +67,7 @@ Without a spatial index, geo queries will still run but will perform a full scan # Creating an index -You can create indexes from the Appwrite Console or programmatically using the Databases API. In the Console, navigate to your database, open the table, and go to the Indexes tab. Select the index type, choose the column, and save. +You can create indexes from the Appwrite Console or programmatically using the TablesDB API. In the Console, navigate to your database, open the table, and go to the Indexes tab. Select the index type, choose the column, and save. Using the Appwrite Node.js SDK: @@ -86,8 +86,9 @@ await tablesDB.createIndex({ tableId: "", key: "status_index", // Index ID type: "key", // Index type: "key", "unique", or "fulltext" - attributes: ["status"], // Columns to index + columns: ["status"], // Columns to index orders: ["ASC"], // Sort order per column + lengths: [32], // Prefix length for text columns }); ``` @@ -99,8 +100,9 @@ await tablesDB.createIndex({ tableId: "", key: "email_unique", type: "unique", - attributes: ["email"], + columns: ["email"], orders: ["ASC"], + lengths: [255], }); ``` @@ -134,8 +136,9 @@ await tablesDB.createIndex({ tableId: "", key: "status_author_index", type: "key", - attributes: ["status", "authorId"], + columns: ["status", "authorId"], orders: ["ASC", "ASC"], + lengths: [32, 36], }); ``` diff --git a/src/routes/blog/post/appwrite-query-api/+page.markdoc b/src/routes/blog/post/appwrite-query-api/+page.markdoc index be23431ca03..9a282c8b6da 100644 --- a/src/routes/blog/post/appwrite-query-api/+page.markdoc +++ b/src/routes/blog/post/appwrite-query-api/+page.markdoc @@ -180,7 +180,7 @@ A few practical rules when working with the Query API: # Start building with Appwrite Databases -The Query API covers the full range of what you need for data retrieval: precise filtering, flexible sorting, and two pagination strategies suited to different scales. The same `Query` class works across all Appwrite Database APIs. +The Query API covers the full range of what you need for data retrieval: precise filtering, flexible sorting, and two pagination strategies suited to different scales. The same `Query` class works across Appwrite TablesDB APIs. To go further: 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 d3eb6939d17..9fbaa36bae9 100644 --- a/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc +++ b/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc @@ -10,13 +10,13 @@ category: tutorial lastUpdated: 2025-10-14 faqs: - question: "What is Appwrite Realtime, and how does it work?" - answer: "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." + answer: "Appwrite Realtime lets your app respond instantly to changes, like new database entries 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." - question: "How does Realtime improve Flutter app performance?" answer: "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." - question: "Can I use Appwrite Realtime without a database?" answer: "Yes. While databases are a common use case, Realtime can also subscribe to other Appwrite services, like authentication sessions, storage changes, or function executions. You can listen to almost any event happening inside your Appwrite project." - question: "Is Realtime secure?" - answer: "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." + answer: "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." - question: "Can I use Appwrite Realtime with other frameworks or just Flutter?" answer: "Realtime works across all SDKs that Appwrite supports, including Web, Android, and iOS. Flutter is just one example. The same logic applies universally: subscribe to channels and react to incoming data." --- @@ -39,24 +39,24 @@ On the databases page, click on the **Create database** button. 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.avif) +![Create table](/images/blog/appwrite-realtime-with-flutter/2.avif) -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: Text +- Column Key: name - Size: 25 - Default: null - Required: true - Array: false - ![Create String](/images/blog/appwrite-realtime-with-flutter/3.avif) + ![Create text column](/images/blog/appwrite-realtime-with-flutter/3.avif) -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. +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.avif) @@ -96,7 +96,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 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: ```dart import 'package:flutter/material.dart'; @@ -184,7 +184,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; @@ -206,33 +206,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); + 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); @@ -240,14 +240,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. ```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().toString() ]); // listen to changes @@ -269,15 +269,15 @@ Now, we will set up our subscribe function, which will listen to changes to docu } ``` -Finally, let's modify our `_addItem` function to add items to Appwrite's database and see how the view updates in real time. +Finally, let's modify our `_addItem` function to add items to Appwrite's table and see how the view updates in real time. ```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) { @@ -294,10 +294,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'], ); }, ), @@ -342,27 +342,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); + 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); @@ -373,7 +373,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().toString() ]); // listen to changes @@ -412,10 +412,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'], ); }, ), @@ -459,10 +459,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) { diff --git a/src/routes/blog/post/appwrite-server-sdk-vs-client-sdk/+page.markdoc b/src/routes/blog/post/appwrite-server-sdk-vs-client-sdk/+page.markdoc index 16c7c9971be..c443f2e55ff 100644 --- a/src/routes/blog/post/appwrite-server-sdk-vs-client-sdk/+page.markdoc +++ b/src/routes/blog/post/appwrite-server-sdk-vs-client-sdk/+page.markdoc @@ -64,7 +64,10 @@ const account = new Account(client); const tablesDB = new TablesDB(client); // Log in -await account.createEmailPasswordSession("user@example.com", "password"); +await account.createEmailPasswordSession({ + email: "user@example.com", + password: "password" +}); // Read data, scoped to what this user can access const docs = await tablesDB.listRows({ diff --git a/src/routes/blog/post/appwrite-storage-file-manager/+page.markdoc b/src/routes/blog/post/appwrite-storage-file-manager/+page.markdoc index 355cd505a4e..8e2ab5fb6dd 100644 --- a/src/routes/blog/post/appwrite-storage-file-manager/+page.markdoc +++ b/src/routes/blog/post/appwrite-storage-file-manager/+page.markdoc @@ -53,13 +53,15 @@ const client = new Client() const storage = new Storage(client); const bucket = await storage.createBucket( - ID.unique(), // bucketId - 'User Uploads', // name - ['any'], // permissions (covered below) - true, // fileSecurity - true, // enabled - 10000000, // maximumFileSize (10 MB) - ['image/jpeg', 'image/png', 'application/pdf'] // allowedFileExtensions (MIME types or extensions, e.g. 'jpg') + { + bucketId: ID.unique(), + name: 'User Uploads', + permissions: ['any'], + fileSecurity: true, + enabled: true, + maximumFileSize: 10000000, + allowedFileExtensions: ['image/jpeg', 'image/png', 'application/pdf'] +} // allowedFileExtensions (MIME types or extensions, e.g. 'jpg') ); ``` diff --git a/src/routes/blog/post/appwrite-vs-convex-ai-agents/+page.markdoc b/src/routes/blog/post/appwrite-vs-convex-ai-agents/+page.markdoc index ebebd403ab8..8b15d3a8a95 100644 --- a/src/routes/blog/post/appwrite-vs-convex-ai-agents/+page.markdoc +++ b/src/routes/blog/post/appwrite-vs-convex-ai-agents/+page.markdoc @@ -37,7 +37,7 @@ Around that, Convex layers a broader platform: - HTTP actions for webhook endpoints and custom clients. - A document-style database with optional schemas, indexes, and paginated queries. - Realtime that is automatic as long as you read data through query functions and client libraries. -- File Storage with upload, store, serve, and delete, referenced from database documents. +- File Storage with upload, store, serve, and delete, referenced from database rows. - Scheduled Functions and Cron Jobs for durable scheduling, plus higher-level Workpool, Workflow, and Cron components for larger scale. - Authentication via Clerk, WorkOS AuthKit, Auth0, Custom OIDC, or the beta Convex Auth library. - Self-hosting under an FSL Apache 2.0 license that converts to Apache 2.0 after two years. diff --git a/src/routes/blog/post/april-product-update-mongodb-support-appwrite-190-realtime-upgrades-and-ai-tooling/+page.markdoc b/src/routes/blog/post/april-product-update-mongodb-support-appwrite-190-realtime-upgrades-and-ai-tooling/+page.markdoc index 6ac50347a8f..25f7daa1cd0 100644 --- a/src/routes/blog/post/april-product-update-mongodb-support-appwrite-190-realtime-upgrades-and-ai-tooling/+page.markdoc +++ b/src/routes/blog/post/april-product-update-mongodb-support-appwrite-190-realtime-upgrades-and-ai-tooling/+page.markdoc @@ -10,7 +10,7 @@ category: product featured: false faqs: - question: "What's new in Appwrite 1.9.0?" - answer: "Appwrite 1.9.0 adds MongoDB as a supported database option for self-hosted deployments, better handling of large integers, new string column types, configurable caching, and improved database observability. Real-time, auth, and infrastructure all got updates too." + answer: "Appwrite 1.9.0 adds MongoDB as a supported database option for self-hosted deployments, better handling of large integers, new text column types, configurable caching, and improved database observability. Real-time, auth, and infrastructure all got updates too." - question: "Can I now use MongoDB with Appwrite?" answer: "Yes, on self-hosted Appwrite starting with version 1.9.0. You can run Appwrite directly on your existing MongoDB infrastructure while keeping the same SDKs and APIs. Backups, monitoring, and scaling tools you've built around MongoDB carry over." - question: "How does the new message-based Realtime work?" @@ -49,7 +49,7 @@ You can run Appwrite directly on your existing MongoDB infrastructure while keep Reuse your existing backups, monitoring, and scaling setup while staying fully open source and self hosted. -This release also includes improvements across Databases, Realtime, Auth, and infrastructure. You get better handling of large integers, new string column types, configurable caching, and improved observability for database usage. +This release also includes improvements across Databases, Realtime, Auth, and infrastructure. You get better handling of large integers, new text column types, configurable caching, and improved observability for database usage. [Read the announcement](https://appwrite.io/blog/post/appwrite-mongodb-partnership-self-hosted) diff --git a/src/routes/blog/post/backend-as-a-service/+page.markdoc b/src/routes/blog/post/backend-as-a-service/+page.markdoc index 4984d5af125..4d1474e802e 100644 --- a/src/routes/blog/post/backend-as-a-service/+page.markdoc +++ b/src/routes/blog/post/backend-as-a-service/+page.markdoc @@ -27,7 +27,7 @@ faqs: Typical BaaS bundles expose these capabilities through HTTP APIs and SDKs: - **Auth:** accounts, sessions, OAuth, MFA, password recovery, and sometimes SAML/SCIM on enterprise tiers. -- **Database:** managed logical data store with per-row/collection permissions: SQL (Postgres) or document/NoSQL APIs depending on vendor. +- **Database:** managed logical data store with per-row or resource-level permissions: SQL (Postgres) or NoSQL APIs depending on vendor. - **Storage:** object/file storage with signed URLs, access rules, and often image transforms. - **Functions:** event-driven or HTTP-invoked compute for business logic and integrations. - **APIs:** REST and/or GraphQL surfaces generated or maintained by the platform, plus realtime channels where offered. diff --git a/src/routes/blog/post/backend-mistakes-that-quietly-cost-small-teams-weeks/+page.markdoc b/src/routes/blog/post/backend-mistakes-that-quietly-cost-small-teams-weeks/+page.markdoc index 0f2aecfccdd..aed7b7ac164 100644 --- a/src/routes/blog/post/backend-mistakes-that-quietly-cost-small-teams-weeks/+page.markdoc +++ b/src/routes/blog/post/backend-mistakes-that-quietly-cost-small-teams-weeks/+page.markdoc @@ -66,7 +66,7 @@ Manual deployments. Manual backups. Manual log reviews. These feel manageable wh # 6. Letting backend inconsistency accumulate across features -In fast-moving teams, backend inconsistencies creep in gradually: different naming conventions across collections, different data modeling patterns for similar concepts, different permission logic applied to features built three sprints apart. Each one is minor in isolation. Together, they mean every new feature requires a developer to interpret what came before rather than extend a consistent pattern, and interpretation costs time. +In fast-moving teams, backend inconsistencies creep in gradually: different naming conventions across tables, different data modeling patterns for similar concepts, different permission logic applied to features built three sprints apart. Each one is minor in isolation. Together, they mean every new feature requires a developer to interpret what came before rather than extend a consistent pattern, and interpretation costs time. **What to do instead:** Define backend standards early. Naming conventions, data modeling guidelines, permission defaults, environment structure. Consistency reduces cognitive load across the entire team, and that compounds into real velocity over time. @@ -86,7 +86,7 @@ For small teams, Appwrite directly addresses the backend mistakes outlined above - **Consistent backend foundation across projects.** Appwrite's project-based structure gives every application the same set of backend primitives from day one, no stack debates, no rebuilding auth or storage from scratch. - **Integrated backend services without glue code.** Auth, databases, storage, functions, and messaging are built to work together out of the box, reducing the integration surface area that accumulates across multi-vendor backends. -- **Granular permissions as a first-class primitive.** Appwrite's permission system lets you define access at the collection, document, bucket, and file level, making it practical to enforce least-privilege access from the start rather than retrofitting it later. +- **Granular permissions as a first-class primitive.** Appwrite's permission system lets you define access at the table, row, bucket, and file level, making it practical to enforce least-privilege access from the start rather than retrofitting it later. - **Open-source and fully transparent.** Every backend infrastructure decision Appwrite makes can be verified directly in the source code. No vendor trust required. - **Self-hosting for teams with data residency requirements.** Deploy Appwrite within your own infrastructure when required, without switching backend stacks. diff --git a/src/routes/blog/post/best-pagination-technique/+page.markdoc b/src/routes/blog/post/best-pagination-technique/+page.markdoc index a195b324f10..15f1d12434a 100644 --- a/src/routes/blog/post/best-pagination-technique/+page.markdoc +++ b/src/routes/blog/post/best-pagination-technique/+page.markdoc @@ -58,9 +58,9 @@ To get the second page, we keep the limit at 10 (this doesn't change since we wa In the SQL world, such a query would be written as `SELECT * FROM posts OFFSET 10 LIMIT 10`. -Some websites implementing offset pagination also show the page number of the last page. How do they do it? Alongside results for each page, they also tend to return a `sum` attribute telling you how many rows there are in total. Using `limit`, `sum`, and a bit of math, you can calculate last page number using `lastPage = ceil(sum / limit)` +Some websites implementing offset pagination also show the page number of the last page. How do they do it? Alongside results for each page, they also tend to return a `sum` field telling you how many rows there are in total. Using `limit`, `sum`, and a bit of math, you can calculate last page number using `lastPage = ceil(sum / limit)` -As convenient as this feature is for the user, developers struggle to scale this type of pagination. Looking at `sum` attribute, we can already see that it can take quite some time to count all rows in a database to the exact number. **Alongside that, the `offset` in databases is implemented in a way that loops through rows to know how many should be skipped.** That means that the higher our offset is, the longer our database query will take. +As convenient as this feature is for the user, developers struggle to scale this type of pagination. Looking at `sum`, we can already see that it can take quite some time to count all rows in a database to the exact number. **Alongside that, the `offset` in databases is implemented in a way that loops through rows to know how many should be skipped.** That means that the higher our offset is, the longer our database query will take. Another downside of offset pagination is that it doesn't play well with real-time data or data that changes often. Offset says how many rows we want to skip but doesn't account for row deletion or new rows being created. Such an offset can result in showing duplicate data or missing data. @@ -94,47 +94,45 @@ 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 it. 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(); -client +const client = new 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 I want 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 @@ -142,7 +140,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. @@ -174,7 +172,7 @@ for(let i = 0; i < 100; i++) { index++; } - const query = `INSERT INTO _project_${config.projectId}_collection_posts (_uid, _read, _write) VALUES ${queryValues.join(", ")}`; + const query = `INSERT INTO posts (_uid, _read, _write) VALUES ${queryValues.join(", ")}`; promises.push(connection.execute(query)); } @@ -188,9 +186,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 @@ -211,7 +209,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}/tablesdb/main/tables/posts/rows?queries[]=${offset}&queries[]=${limit}`, { headers: { 'content-type': 'application/json', 'X-Appwrite-Project': config.projectId @@ -256,9 +254,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.avif) -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 @@ -279,7 +277,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}/tablesdb/main/tables/posts/rows?queries[]=${cursor}&queries[]=${limit}`, { headers: { 'content-type': 'application/json', 'X-Appwrite-Project': config.projectId @@ -346,4 +344,4 @@ Do you know what is better than reading about pagination? Trying it out! I would - [Is offset pagination dead? Why cursor pagination is taking over](https://uxdesign.cc/why-facebook-says-cursor-pagination-is-the-greatest-d6b98d86b6c0) - [Offset pagination vs Cursor pagination](https://stackoverflow.com/questions/55744926/offset-pagination-vs-cursor-pagination) - [How to implement cursors for pagination in an api](https://stackoverflow.com/questions/18314687/how-to-implement-cursors-for-pagination-in-an-api) -- [How to Implement Cursor Pagination Like a Pro](https://medium.com/swlh/how-to-implement-cursor-pagination-like-a-pro-513140b65f32) \ No newline at end of file +- [How to Implement Cursor Pagination Like a Pro](https://medium.com/swlh/how-to-implement-cursor-pagination-like-a-pro-513140b65f32) diff --git a/src/routes/blog/post/build-fullstack-notes-app-cursor-appwrite-tanstack-start/+page.markdoc b/src/routes/blog/post/build-fullstack-notes-app-cursor-appwrite-tanstack-start/+page.markdoc index 4a3c9b2f57a..13be91be303 100644 --- a/src/routes/blog/post/build-fullstack-notes-app-cursor-appwrite-tanstack-start/+page.markdoc +++ b/src/routes/blog/post/build-fullstack-notes-app-cursor-appwrite-tanstack-start/+page.markdoc @@ -53,7 +53,7 @@ The **Appwrite MCP server** gives Cursor access to your Appwrite project so it c ## 1. Create an API Key in Appwrite Cloud * Log in to [Appwrite Cloud](https://cloud.appwrite.io). * Create a **new API key** with the following scopes: - - `databases.read` and `databases.write` (or `tables.read` and `tables.write`) + - `tables.read`, `tables.write`, `rows.read`, and `rows.write` - `users.read` and `users.write` * Copy the key. @@ -260,12 +260,18 @@ export async function login(email: string, password: string) { await deleteAllSessions(); try { - await account.createEmailPasswordSession(email, password); + await account.createEmailPasswordSession({ + email: email, + password: password + }); } catch (err: any) { const msg = err?.message?.toLowerCase() ?? ''; if (msg.includes('session') && msg.includes('active')) { await deleteAllSessions(); - await account.createEmailPasswordSession(email, password); + await account.createEmailPasswordSession({ + email: email, + password: password + }); } else { throw err; } @@ -275,7 +281,12 @@ export async function login(email: string, password: string) { } export async function signup(email: string, password: string, name?: string) { - await account.create(ID.unique(), email, password, name); + await account.create({ + userId: ID.unique(), + email, + password, + name + }); return await login(email, password); } 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 141897c4819..a4e7e10a209 100644 --- a/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc +++ b/src/routes/blog/post/build-fullstack-svelte-appwrite/+page.markdoc @@ -15,9 +15,9 @@ faqs: - question: "Can I use Appwrite with TypeScript in Svelte?" answer: "Yes. The Appwrite Web SDK ships with full TypeScript types. The tutorial uses JavaScript for simplicity, but you can swap in TypeScript by picking that option in sv create and importing types directly from the appwrite package." - question: "How do I enforce that users only see their own data?" - answer: "Enable Document Security on your collection and set per-document permissions when creating rows. Use the user's ID as the read/write permission so other users can't access their data. [Appwrite Databases](/docs/products/databases) handles the permission checks automatically." - - question: "What's the difference between collection-level and document-level permissions?" - answer: "Collection-level permissions apply to all documents in the collection. Document-level (Document Security) lets each row carry its own permissions, which is what you want for multi-tenant data like a personal expense tracker. Enable Document Security to use it." + answer: "Enable row security on your table and set per-row permissions when creating rows. Use the user's ID as the read/write permission so other users can't access their data. [Appwrite Databases](/docs/products/databases) handles the permission checks automatically." + - question: "What's the difference between table-level and row-level permissions?" + answer: "Table-level permissions apply to all rows in the table. Row-level (Row security) lets each row carry its own permissions, which is what you want for multi-tenant data like a personal expense tracker. Enable Row security to use it." - question: "Do I need to host Svelte and Appwrite separately?" answer: "Not necessarily. You can deploy the SvelteKit frontend on [Appwrite Sites](/docs/products/sites) and use the same project for backend services, keeping everything under one console. Or you can deploy SvelteKit elsewhere (Netlify, Vercel) and point at Appwrite for the backend." --- @@ -74,7 +74,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. @@ -85,28 +85,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` (Text, required) - `amount` (Float, required) - `category` (Enum, required) - Elements: "food", "rent", "transportation", "entertainment", "shopping", "healthcare", "utilities", "education", "other" -- `description` (String, required) +- `description` (Text, 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.avif) +![permissions-row-security](/images/blog/build-fullstack-svelte-appwrite/permissions-document-security.avif) ## Project structure @@ -173,12 +173,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() @@ -186,17 +186,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 @@ -270,7 +270,9 @@ Finally, we'll add a `logout` function to the `auth.js` file to handle user logo ```js export async function logout() { try { - await account.deleteSession('current') + await account.deleteSession({ + sessionId: 'current' + }) user.set(null) } catch (error) { console.error('Logout error:', error) @@ -546,7 +548,7 @@ We'll start with our imports and state management: @@ -89,15 +89,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 tablesDB = 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 = tablesDB.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); @@ -108,7 +108,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 @@ -127,7 +127,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/integrate-custom-auth-sveltekit/+page.markdoc b/src/routes/blog/post/integrate-custom-auth-sveltekit/+page.markdoc index 048c918af96..2b0f840dc47 100644 --- a/src/routes/blog/post/integrate-custom-auth-sveltekit/+page.markdoc +++ b/src/routes/blog/post/integrate-custom-auth-sveltekit/+page.markdoc @@ -146,7 +146,7 @@ async function authLogic(email, password) { if (password === '123456') { return await getUser(email); } else { - returns null; + return null; } } catch (err) { console.error(err); @@ -262,7 +262,9 @@ We will create a file `./src/routes/+page.svelte` and add the following code: token = ''; session = ''; currentState = state[0]; - await account.deleteSession('current'); + await account.deleteSession({ + sessionId: 'current' + }); } onMount(async () => { @@ -317,5 +319,5 @@ And with that, our demo project to try custom token authentication in Appwrite i Additionally, if you would like to learn more about Appwrite Auth, here are some resources: -- [Appwrite Auth docs](https://appwrite.io/docs/products/auth): These documents provide more information on how to use Appwrite Auth. +- [Appwrite Auth docs](https://appwrite.io/docs/products/auth): Learn more about how to use Appwrite Auth. - [Appwrite Discord](https://discord.com/invite/appwrite): Connect with other developers and the Appwrite team for discussion, questions, and collaboration. diff --git a/src/routes/blog/post/introducing-functions-ecosystem/+page.markdoc b/src/routes/blog/post/introducing-functions-ecosystem/+page.markdoc index e35fb16fd16..8e4626c7a09 100644 --- a/src/routes/blog/post/introducing-functions-ecosystem/+page.markdoc +++ b/src/routes/blog/post/introducing-functions-ecosystem/+page.markdoc @@ -45,7 +45,7 @@ To combat this, we've introduced automatically generated, short-lived API keys f Here's how you can use them: ```jsx -import { Client, Databases } from 'node-appwrite'; +import { Client, TablesDB } from 'node-appwrite'; export default async ({ req, res }) => { const client = new Client() @@ -53,7 +53,7 @@ export default async ({ req, res }) => { .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID) .setKey(req.headers['x-appwrite-key']); - const databases = new Databases(client); + const tablesDB = new TablesDB(client); // Your function logic here diff --git a/src/routes/blog/post/introducing-new-appwrite-cli/+page.markdoc b/src/routes/blog/post/introducing-new-appwrite-cli/+page.markdoc index e70b235977b..dd531d54e23 100644 --- a/src/routes/blog/post/introducing-new-appwrite-cli/+page.markdoc +++ b/src/routes/blog/post/introducing-new-appwrite-cli/+page.markdoc @@ -14,7 +14,7 @@ faqs: - question: "How do I test an Appwrite Function locally?" answer: "Install Docker, run `appwrite run` against your function, and the CLI will spin up the runtime container on your machine. You can iterate without redeploying after every change. See [Appwrite Functions](/docs/products/functions)." - question: "Does the CLI overwrite my database when I push?" - answer: "No. The new push command syncs changes in a non destructive way, which is a big improvement over the previous CLI behaviour where deploying collections wiped existing data." + answer: "No. The new push command syncs changes in a non destructive way, which is a big improvement over the previous CLI behaviour where deploying tables wiped existing data." - question: "Can I use the Appwrite CLI in CI/CD?" answer: "Yes. Headless login lets you authenticate non interactively using an API key, so the CLI works inside GitHub Actions, GitLab CI, or any other automation system without prompting for input." - question: "What is `appwrite.config.json`?" @@ -22,15 +22,15 @@ faqs: - question: "Can I work with multiple Appwrite accounts from one CLI?" answer: "Yes. Running `appwrite login` again adds a second account, and you can switch between them as needed. This is useful when you have separate accounts for personal projects and work." --- -We're excited to announce the new Appwrite CLI. This iteration focuses on local development and an enhanced CI/CD experience. Now, you can test changes to your functions locally, and easily apply changes to your Appwrite collection. +We're excited to announce the new Appwrite CLI. This iteration focuses on local development and an enhanced CI/CD experience. Now, you can test changes to your functions locally, and easily apply changes to your Appwrite table. Let’s dive into the updates to the new Appwrite CLI and how it will improve your building experience. # Understanding past limitations -Appwrite developers use the current generation of Appwrite CLI to initialize functions and collections and deploy those resources. +Appwrite developers use the current generation of Appwrite CLI to initialize functions and tables and deploy those resources. -When deploying collections, the only option is to override and delete all the collection's existing data, which is not the use case for most scenarios. +When deploying tables, the only option is to override and delete all the table's existing data, which is not the use case for most scenarios. The only way to test an Appwrite function is to continue deploying the function changes to the Appwrite instance, which can be aggravating for small changes. @@ -102,7 +102,7 @@ Appwrite CLI is now in listening mode. Try changing your code and seeing how the GitOps is a common way of tracking and migrating database changes. The latest Appwrite CLI generation includes a few features to help you migrate your database changes easily. -When running `appwrite push collection`, the CLI will compare your local `appwrite.config.json` collection definition against the currently deployed remote collection and will present you with a detailed table awaiting your decision, for example: +When running `appwrite push table`, the CLI will compare your local `appwrite.config.json` table definition against the currently deployed remote table and will present you with a detailed table awaiting your decision, for example: ``` Key │ Action │ Reason @@ -113,24 +113,24 @@ time │ adding │ Field isn't available on the remote server ──────────┼────────────┼───────────────────────────────────────────── timezone │ recreating │ size changed from 256 to 255 -ℹ Info: Attribute deletion will cause loss of data -ℹ Info: Attribute recreation will cause loss of data +ℹ Info: Column deletion will cause loss of data +ℹ Info: Column recreation will cause loss of data ? Would you like to apply these changes? Type "YES" to confirm. ``` -In this example, we can see that because we've renamed the attribute `times` to `time,` it will get deleted and read. We must also recreate the `timezone` attribute because we've changed its size from 256 to 255. +In this example, we can see that because we've renamed the column `times` to `time,` it will get deleted and re-added. We must also recreate the `timezone` column because we've changed its size from 256 to 255. To help with the decision, you can notice two warnings: deleting or recreating a field will cause data loss. -It's important to know that the data loss will affect only the recreated/deleted attribute and not the whole collection. +It's important to know that the data loss will affect only the recreated/deleted column and not the whole table. -As you can read in the next section, when pushing collections in CI/CD pipelines, you'll need to add the `--force` flag. +As you can read in the next section, when pushing tables in CI/CD pipelines, you'll need to add the `--force` flag. # CI/CD Adapting CI/CD pipelines ensures robust deployments. To support this, we have rewritten many parts of our CLI to fully accommodate non-interactive actions for all deployment-related commands. -You can add the `--force` flag to any command that may ask you questions, such as `appwrite push collections,` to pre-answer all of them with `YES.` Additionally, you can use the `--all` flag to push/pull all services' available resources. +You can add the `--force` flag to any command that may ask you questions, such as `appwrite push tables,` to pre-answer all of them with `YES.` Additionally, you can use the `--all` flag to push/pull all services' available resources. Till this generation, Appwrite CLI supported non-interactive login for API-key-based authorization only, as follows: 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 aee6ee8eda9..80b66694b10 100644 --- a/src/routes/blog/post/introducing-new-database-operators/+page.markdoc +++ b/src/routes/blog/post/introducing-new-database-operators/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post title: "Introducing new Database operators: or & contains query methods" -description: What is a database without queries? Not a lot. That's why we’re happy to announce a new set of query methods, array contains, string contains, and OR operators to Appwrite Databases. +description: What is a database without queries? Not a lot. That's why we’re happy to announce a new set of query methods, array contains, text contains, and OR operators to Appwrite Databases. date: 2024-02-29 cover: /images/blog/db-operators-overview.avif timeToRead: 5 @@ -10,9 +10,9 @@ category: product, announcement featured: false faqs: - question: "What does the contains query do?" - answer: "`Query.contains` matches a substring inside a string attribute or an element inside an array attribute. It complements `startsWith` and `endsWith` and is the right choice when you don't know where in the value the match will appear. See [Appwrite Databases](/docs/products/databases)." + answer: "`Query.contains` matches a substring inside a text column or an element inside an array column. It complements `startsWith` and `endsWith` and is the right choice when you don't know where in the value the match will appear. See [Appwrite Databases](/docs/products/databases)." - question: "How does the OR operator work in Appwrite queries?" - answer: "Pass two or more queries inside `Query.or([...])` and any document that satisfies at least one of them is returned. You can nest it with other queries to build expressions like 'category equals X AND (name contains Y OR rating greater than 4)'." + answer: "Pass two or more queries inside `Query.or([...])` and any row that satisfies at least one of them is returned. You can nest it with other queries to build expressions like 'category equals X AND (name contains Y OR rating greater than 4)'." - question: "Can I combine contains and OR in the same request?" answer: "Yes. Both operators are regular query expressions, so you can pass several of them in the same queries array, or nest `Query.contains` calls inside a `Query.or` block." - question: "Does contains do a full text search?" @@ -20,14 +20,14 @@ faqs: - question: "When did these operators ship?" answer: "Both `contains` and `or` shipped as part of the Appwrite 1.5 release on Appwrite Cloud and self hosted Appwrite in March 2024. They are available across every SDK." - question: "Are there performance implications when using contains?" - answer: "Substring matches can't always use an index, so very large collections will be slower than equality queries. Where possible, narrow the result set first with indexed attributes (equality, ranges) and then apply contains to filter further." + answer: "Substring matches can't always use an index, so very large tables will be slower than equality queries. Where possible, narrow the result set first with indexed columns (equality, ranges) and then apply contains to filter further." --- 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. These two query methods have been highly requested by the Appwrite community, and we’re excited to show you how to use them, so let’s jump in and take a look! -- `contains` - partial text matches on string attributes, array element matching on array attributes +- `contains` - partial text matches on text columns, array element matching on array columns - `or` - write logical OR queries # Contains operator @@ -35,16 +35,16 @@ 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']) ] }); ``` -It’s important to note that the contains operator also works on array attributes as well. For example, if we set a string attribute to act as an array, you could search this array in the same way you would search any other string. +It’s important to note that the contains operator also works on array columns as well. For example, if we set a text column to act as an array, you could search this array in the same way you would search any other text value. ```js Query.contains('tags', ['mystery', 'comedy', 'PG-13']) @@ -58,9 +58,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/manage-user-permissions-with-labels-and-teams/+page.markdoc b/src/routes/blog/post/manage-user-permissions-with-labels-and-teams/+page.markdoc index b72f9795092..d41a29e4d8b 100644 --- a/src/routes/blog/post/manage-user-permissions-with-labels-and-teams/+page.markdoc +++ b/src/routes/blog/post/manage-user-permissions-with-labels-and-teams/+page.markdoc @@ -14,21 +14,21 @@ faqs: - question: "Can I create and modify Labels from the client SDK?" answer: "No, Labels can only be created or modified from the Appwrite Console or a Server SDK. Teams, on the other hand, can be created and modified from both Client and Server SDKs. This is a deliberate restriction to prevent users from granting themselves additional permissions from the client." - question: "How do permissions work with Teams in Appwrite?" - answer: "You assign permissions to a Team (or specific roles within a Team) on any resource like a document, file, or function. Every user added to that Team inherits those permissions automatically. If a user has a role inside the Team, they also inherit any permissions granted to that role. Learn more in the [Appwrite Auth docs](/docs/products/auth)." + answer: "You assign permissions to a Team (or specific roles within a Team) on any resource like a row, file, or function. Every user added to that Team inherits those permissions automatically. If a user has a role inside the Team, they also inherit any permissions granted to that role. Learn more in the [Appwrite Auth docs](/docs/products/auth)." - question: "When should I use a Label instead of a Team?" answer: "Use a Label when you need to flag individual users with an attribute that controls access, like a subscription tier or paid course access. Removing the Label revokes access without removing the user from any group. Labels are ideal for per user gating, while Teams shine when multiple users collaborate on shared resources." - question: "Can a user belong to multiple Teams and have multiple Labels?" answer: "Yes, a user can be a member of many Teams (with different roles in each) and can carry multiple Labels at the same time. Appwrite combines permissions from all their Teams, roles, and Labels when checking access on a resource. This lets you compose access rules without writing custom permission logic." - - question: "How do I assign Team or Label permissions on a document or file?" + - question: "How do I assign Team or Label permissions on a row or file?" answer: "In the Appwrite Console, open the resource's settings and add a permission row, then choose a Team or Label as the role. You can also do this from any [Appwrite SDK](/docs/sdks) by setting the permissions array when creating or updating the resource. Use role helpers like Role.team(id) or Role.label(name) for clarity." --- -Teams and Labels allow us to categorize and group users together, allowing us to set permissions to resources at the Team and label level instead of at the individual user level. Grouping users together makes managing permissions to documents, files and functions much more efficient this way. +Teams and Labels allow us to categorize and group users together, allowing us to set permissions to resources at the Team and label level instead of at the individual user level. Grouping users together makes managing permissions to rows, files and functions much more efficient this way. ![Labels vs Teams](/images/blog/manage-user-permissions-with-labels-and-teams/labels3.avif) -To think of this in real-world terms, imagine for a second we were building the next social media application. Teams can be used to create admins and moderators, and these admins and moderators would have permission to delete and flag posts that don’t meet community guidelines. The owner at the document level would have the ability to update and delete this post, but anyone who is on the moderator Team would also have the ability to delete the post, something that is normally restricted to only the owner of the post. We can also change which users have these permissions at any point by adding and removing users and updating the group-level permissions. +To think of this in real-world terms, imagine for a second we were building the next social media application. Teams can be used to create admins and moderators, and these admins and moderators would have permission to delete and flag posts that don’t meet community guidelines. The owner at the row level would have the ability to update and delete this post, but anyone who is on the moderator Team would also have the ability to delete the post, something that is normally restricted to only the owner of the post. We can also change which users have these permissions at any point by adding and removing users and updating the group-level permissions. Another example we can take a look at would be a streaming service like Amazon Video. How do we give users access to a movie or show they paid for? This is where Teams and Labels make our lives easier. By simply adding Labels to users we could decide which users have access to specific resources like movies or shows in our application. @@ -91,7 +91,7 @@ Let's start with creating a new Team, adding members, and assigning roles to tho 2 - Once your Team is created, you can add a Member to the Team by selecting the “Members” tab and clicking “Create Membership”. Here you will enter the user's email address (name can be left blank) and assign a role to the member. Roles are optional, so you can leave this part blank. -That’s it for creating a Team and adding members from the appwrite console. Now you can assign document, storage, and function permissions to your Teams. As an example, for collection level permissions, you can go to the “settings'' tab in a collection, and in the “permissions” section, choose “Select Teams'' to give permissions to an entire Team or “Custom permissions” if you want to assign permission to only users with a particular role within the Team. +That’s it for creating a Team and adding members from the Appwrite Console. Now you can assign row, storage, and function permissions to your Teams. As an example, for table-level permissions, you can go to the “settings'' tab in a table, and in the “permissions” section, choose “Select Teams'' to give permissions to an entire Team or “Custom permissions” if you want to assign permission to only users with a particular role within the Team. ## Labels @@ -115,11 +115,11 @@ Client SDK - Yes ```javascript import { Client, Teams } from "appwrite"; -const client = //... +const client = new Client(); // ... -const Teams = new Teams(client); +const teams = new Teams(client); -const promise = Teams.create({ +const promise = teams.create({ teamId: '', name: '' }); @@ -135,7 +135,7 @@ Client SDK - No ```javascript const sdk = require('node-appwrite'); -const client = //... +const client = new sdk.Client(); // ... const users = new sdk.Users(client); diff --git a/src/routes/blog/post/messaging-explained/+page.markdoc b/src/routes/blog/post/messaging-explained/+page.markdoc index b6070c21bee..109a40d18a1 100644 --- a/src/routes/blog/post/messaging-explained/+page.markdoc +++ b/src/routes/blog/post/messaging-explained/+page.markdoc @@ -17,7 +17,7 @@ faqs: - question: "Can I schedule messages with Appwrite Messaging?" answer: "Yes, when you create a message you can include a scheduled at timestamp. An internal scheduler checks for due messages every minute and queues them for delivery. This works for emails, SMS, and push notifications across topics and direct targets." - question: "How does Appwrite send messages under the hood?" - answer: "When you call the API, Appwrite validates the input, creates a message document, and creates a schedule document for delivery timing. A worker picks up due messages, resolves topic targets, selects the right provider for each target, and dispatches the message. Failures are logged so you can debug delivery issues from the Console." + answer: "When you call the API, Appwrite validates the input, creates a message record, and creates a schedule record for delivery timing. A worker picks up due messages, resolves topic targets, selects the right provider for each target, and dispatches the message. Failures are logged so you can debug delivery issues from the Console." - question: "What are common use cases for Appwrite Messaging?" answer: "Common use cases include transactional notifications (payment receipts, password resets), marketing campaigns through topics, real time alerts to drive engagement, and custom security flows like OTP delivery. Combining Messaging with [Appwrite Functions](/docs/products/functions) lets you trigger messages from app events without managing extra infrastructure." --- @@ -76,18 +76,16 @@ const client = new sdk.Client() .setKey('919c2d18fb5d4...a2ae413da83346ad2'); // Your secret API key const messaging = new sdk.Messaging(client); -. -. -. -const message = await messaging.createSms( - sdk.ID.unique(), - 'Hello from Appwrite Messaging!', - [smsTopic], // topic to send SMS to - [], - [], - false, - '' -); + +const message = await messaging.createSms({ + messageId: sdk.ID.unique(), + content: 'Hello from Appwrite Messaging!', + topics: [smsTopic], + users: [], + targets: [], + draft: false, + scheduledAt: '' +}); ``` You can also watch the [product tour](https://www.youtube.com/watch?v=QdDgPeuBZ1I) on YouTube to get a full overview of Messaging from the Console. @@ -100,10 +98,10 @@ Here's a step-by-step breakdown of how Appwrite Messaging works: 1. Validate input on API request - The process starts when the Appwrite API is called to create a message, which validates the input. -2. Create message document - - A document containing details about the message, including recipients and content, is created on the internal database. -3. Create schedule document - - A schedule document is created on the internal database, specifying when the message should be sent. This document is necessary for the message scheduler. +2. Create message record + - A record containing details about the message, including recipients and content, is created in the internal database. +3. Create schedule record + - A schedule record is created in the internal database, specifying when the message should be sent. This record is necessary for the message scheduler. 4. Queue the message via the message scheduler - Every minute, the scheduler checks the internal database for scheduled messages. - For each scheduled message, it checks if the message should be sent at that time. @@ -123,4 +121,4 @@ We hope this blog improved your understanding of how Appwrite Messaging works. W - [Best practices for sending push notifications](https://appwrite.io/blog/post/push-notifications-best-practices) - [How Twilio simplifies messaging for developers](https://appwrite.io/blog/post/simplify-messaging-twilio) - [Documentation](https://appwrite.io/docs/products/messaging) -- [Product video tour](https://www.youtube.com/watch?v=QdDgPeuBZ1I) \ No newline at end of file +- [Product video tour](https://www.youtube.com/watch?v=QdDgPeuBZ1I) diff --git a/src/routes/blog/post/migrate-firebase-projects-to-appwrite/+page.markdoc b/src/routes/blog/post/migrate-firebase-projects-to-appwrite/+page.markdoc index 63aba59da41..23f2833bbd4 100644 --- a/src/routes/blog/post/migrate-firebase-projects-to-appwrite/+page.markdoc +++ b/src/routes/blog/post/migrate-firebase-projects-to-appwrite/+page.markdoc @@ -11,11 +11,11 @@ faqs: - question: "How do I migrate from Firebase to Appwrite?" answer: "Use Appwrite Migrations from the Console. Create a new project, open the Migrations tab in Project Settings, select Firebase as the source, paste your Firebase Admin SDK service account JSON, choose which resources to import, and start the migration. The process runs in the background and handles users and files automatically." - question: "What gets migrated automatically from Firebase to Appwrite?" - answer: "Appwrite Migrations automatically moves user accounts and files from Firebase. Documents and cloud functions need extra work because their data models and runtimes differ. Nested documents need to be transformed into Appwrite relationships, usually with a small migration script." + answer: "Appwrite Migrations automatically moves user accounts and files from Firebase. Firestore data and cloud functions need extra work because their data models and runtimes differ. Nested Firestore records need to be transformed into Appwrite relationships, usually with a small migration script." - question: "Do I need to rewrite my Firebase Cloud Functions?" answer: "Yes, [Appwrite Functions](/docs/products/functions) use different syntax, runtimes, and deployment methods, so most Firebase Cloud Functions need partial rewrites. The pattern is similar to standard HTTP controllers, so the lift is manageable. Appwrite supports more runtimes and flexible execution triggers than Firebase." - question: "How does Appwrite handle nested data compared to Firebase?" - answer: "Firebase stores nested documents directly, while Appwrite uses [database relationships](/docs/products/databases) (one to one, one to many, many to many) to model linked data. You typically restructure nested Firebase data into separate Appwrite collections connected by relationships. This makes querying more predictable and easier to optimize." + answer: "Firebase stores nested records directly, while Appwrite uses [database relationships](/docs/products/databases) (one to one, one to many, many to many) to model linked data. You typically restructure nested Firebase data into separate Appwrite tables connected by relationships. This makes querying more predictable and easier to optimize." - question: "Is Appwrite Migrations free?" answer: "Yes, the Migrations tool is included with every Appwrite project and there is no extra fee for running migrations. You pay for the storage and usage of the resulting resources just like any other project. Self hosted Appwrite users get the same Migrations functionality at no cost." - question: "What do I do after the migration finishes?" @@ -31,8 +31,8 @@ Moving is frustrating. Packing, unpacking, renting a truck, wondering if your be Just like moving houses, not every piece of old furniture will suit your new house. You’ll need some creative rearranging or maybe some new furniture. Appwrite is different from Firebase in a similar sense. -- Appwrite automatically moves user accounts or files, but documents and cloud functions will require some work to move. -- Nested documents will require some clever engineering to be transformed into Appwrite relationships. In a future version, they might be added automatically, but you will need to migrate them with a script for now. +- Appwrite automatically moves user accounts or files, but Firestore data and cloud functions require some work to move. +- Nested Firestore records require some planning to be transformed into Appwrite relationships. In a future version, they might be added automatically, but you need to migrate them with a script for now. - Appwrite Functions also use different syntax and deployment methods than Firebase’s Cloud Functions. These will need the most work to migrate and likely need to be partially rewritten. ## Prepare for your move @@ -68,4 +68,4 @@ Finally, learn about [Appwrite Functions](/docs/products/functions/quick-start) ## Join the discussion -We’re always having a blast on [Discord](https://appwrite.io/discord). With members in the community from all over the world, you’ll always find someone to support and share your Appwrite journey. \ No newline at end of file +We’re always having a blast on [Discord](https://appwrite.io/discord). With members in the community from all over the world, you’ll always find someone to support and share your Appwrite journey. diff --git a/src/routes/blog/post/new-string-types/+page.markdoc b/src/routes/blog/post/new-string-types/+page.markdoc index a5ac49411c9..2b17e37912f 100644 --- a/src/routes/blog/post/new-string-types/+page.markdoc +++ b/src/routes/blog/post/new-string-types/+page.markdoc @@ -1,7 +1,7 @@ --- layout: post -title: "Introducing new string column types for Appwrite Databases" -description: Appwrite Databases now supports varchar, text, mediumtext, and longtext column types, giving you explicit control over how string data is stored and indexed. The legacy string type is now deprecated. +title: "Introducing new text column types for Appwrite Databases" +description: Appwrite Databases now supports varchar, text, mediumtext, and longtext column types, giving you explicit control over how text data is stored and indexed. The legacy string type is now deprecated. date: 2026-02-12 cover: /images/blog/new-string-types/cover.avif timeToRead: 5 @@ -9,25 +9,25 @@ author: eldad-fux category: announcement featured: false faqs: - - question: "What new string types does Appwrite Databases support?" - answer: "[Appwrite Databases](/docs/products/databases) now supports four explicit string column types: varchar, text, mediumtext, and longtext. Each has a defined maximum size and indexing behavior, so you can pick the right one based on how the data is used. The legacy string type is deprecated but still supported." + - question: "What new text types does Appwrite Databases support?" + answer: "[Appwrite Databases](/docs/products/databases) now supports four explicit text column types: varchar, text, mediumtext, and longtext. Each has a defined maximum size and indexing behavior, so you can pick the right one based on how the data is used. The legacy string type is deprecated but still supported." - question: "What is the difference between varchar and text columns?" answer: "varchar is stored inline in the row and counts toward the 64KB row size budget, but it can be fully indexed if the size is under 768 characters. text is stored off page with just a 20 byte pointer in the row, so it doesn't consume row space, but it only supports prefix indexing. Use varchar for short queryable fields and text for longer content." - question: "When should I use mediumtext or longtext?" answer: "Use mediumtext for content up to about 4 million characters, like article bodies or longer notes. Use longtext for content up to about 1 billion characters, like full logs or serialized payloads. Both are stored off page and support prefix indexing only." - - question: "Is the old string column type still supported?" - answer: "Yes, the string type is deprecated but Appwrite will continue to maintain full backward support for it. Existing string columns will keep working without changes. For new columns, use the explicit varchar, text, mediumtext, or longtext types to make storage and indexing intentions clear." - - question: "Why did Appwrite introduce explicit string types?" + - question: "Is the legacy string type still supported?" + answer: "Yes, the legacy string type is deprecated but Appwrite will continue to maintain full backward support for existing schemas. For new columns, use the explicit varchar, text, mediumtext, or longtext types to make storage and indexing intentions clear." + - question: "Why did Appwrite introduce explicit text types?" answer: "The single string type hid important storage details, including how large values were stored and which row width limits applied. This made it hard for developers and AI agents to make informed schema decisions. Explicit types expose those tradeoffs so the right choice is visible at design time." - - question: "Can I migrate existing string columns to the new types?" - answer: "Existing string columns continue to work, and you can add new columns using the explicit types alongside them. There is no forced migration path. If you need to switch, plan a data migration by creating new columns with the desired type and copying data over with a script." + - question: "Can I migrate existing legacy string fields to the new types?" + answer: "Existing legacy schemas continue to work, and you can add new columns using the explicit types alongside them. There is no forced migration path. If you need to switch, plan a data migration by creating new columns with the desired type and copying data over with a script." --- Until now, Appwrite offered a single `string` column type that abstracted away four different storage types, based on the size you specified. This meant you had no visibility into how your data was actually stored, how it could be indexed, or why certain size limits existed. -Today, we're introducing four explicit string column types: `varchar`, `text`, `mediumtext`, and `longtext`. +Today, we're introducing four explicit text column types: `varchar`, `text`, `mediumtext`, and `longtext`. -# The new string types +# The new text types Each type has a clear purpose and a defined maximum size: @@ -57,10 +57,10 @@ When we originally designed the `string` type, the intent was to simplify things The issue was that the `string` type hid storage behavior that developers needed to see in order to make informed decisions about their data structures. This led to problems for both human engineers and AI agents alike. - **Hidden storage behavior.** The `string` type required you to specify a size, but gave no indication of how that size mapped to actual storage. There was no way to know that `size: 10000` would produce a `VARCHAR(10000)` stored inline, while `size: 20000` would quietly switch to a `TEXT` column stored off-page. Developers were making sizing decisions without seeing the consequences. -- **Invisible row width limits.** Appwrite uses MariaDB under the hood, which has a fixed 64KB maximum row width. Varchar columns are stored inline and count towards that limit. A developer creating a `string` column with `size: 10000` (a Varchar internally) for something like blog content would unknowingly consume a large chunk of the row budget, limiting how many other columns the table could have. +- **Invisible row width limits.** Appwrite uses MariaDB under the hood, which has a fixed 64KB maximum row width. Varchar columns are stored inline and count towards that limit. A developer creating a `string` column with `size: 10000` (a Varchar internally) for something like blog content would unknowingly consume a large chunk of the row budget, limiting how many other columns the table could have. - **Difficult for AI agents to reason about.** When AI agents provision database schemas, they need clear type semantics to make good decisions. The `string` type gave them nothing to work with, no signal about storage trade-offs, no way to differentiate between a short identifier and a long-form text field. -We saw both sides of this problem firsthand while building [Imagine](https://imagine.dev), our AI-powered builder for web apps, which uses Appwrite under the hood for out-of-the-box server functionality. Imagine runs an Appwrite Cloud agent that provisions database resources on your behalf using the Appwrite CLI and `appwrite.json` as the schema. The agent is trained to build sophisticated architectures, and it would easily create just enough string columns to hit the row width limit. +We saw both sides of this problem firsthand while building [Imagine](https://imagine.dev), our AI-powered builder for web apps, which uses Appwrite under the hood for out-of-the-box server functionality. Imagine runs an Appwrite Cloud agent that provisions database resources on your behalf using the Appwrite CLI and `appwrite.json` as the schema. The agent is trained to build sophisticated architectures, and it would easily create just enough inline text columns to hit the row width limit. We could have fixed this with better prompting for Imagine, but that would only solve it for one tool. Instead, we made the types themselves more explicit, so the right choice is obvious whether you're writing code by hand, using the Console, or letting an AI agent provision your schema. With the new types, both developers and agents can now see exactly what they're working with and make better decisions around their data structures. @@ -81,30 +81,36 @@ const tablesDB = new sdk.TablesDB(client); await tablesDB.createTable({ databaseId: '', tableId: '', - name: 'articles', - columns: [ - { - key: 'title', - type: 'varchar', - size: 255, - required: true - }, - { - key: 'summary', - type: 'text', - required: false - }, - { - key: 'body', - type: 'mediumtext', - required: false - }, - { - key: 'raw_data', - type: 'longtext', - required: false - } - ] + name: 'articles' +}); + +await tablesDB.createVarcharColumn({ + databaseId: '', + tableId: '', + key: 'title', + size: 255, + required: true +}); + +await tablesDB.createTextColumn({ + databaseId: '', + tableId: '', + key: 'summary', + required: false +}); + +await tablesDB.createMediumtextColumn({ + databaseId: '', + tableId: '', + key: 'body', + required: false +}); + +await tablesDB.createLongtextColumn({ + databaseId: '', + tableId: '', + key: 'raw_data', + required: false }); ``` diff --git a/src/routes/blog/post/offline-first-journal/+page.markdoc b/src/routes/blog/post/offline-first-journal/+page.markdoc index 22a49356594..8d8a32e3539 100644 --- a/src/routes/blog/post/offline-first-journal/+page.markdoc +++ b/src/routes/blog/post/offline-first-journal/+page.markdoc @@ -12,8 +12,8 @@ faqs: - question: "What is RxDB and why use it with Appwrite?" answer: "RxDB is a local-first, reactive NoSQL database for JavaScript apps. Pairing it with [Appwrite Databases](/docs/products/databases) gives you local storage with automatic two way sync, so your app keeps working offline and pushes changes to the server when connectivity returns." - question: "Does RxDB officially support Appwrite?" - answer: "Yes. RxDB ships a replication plugin specifically for Appwrite Databases. It handles the push and pull replication loop, so you only need to configure the database, collection, and a few schema fields." - - question: "Why does the entries collection need a `deleted` boolean field?" + answer: "Yes. RxDB ships a replication plugin specifically for Appwrite Databases. It handles the push and pull replication loop, so you only need to configure the database, table, and a few schema fields." + - question: "Why does the entries table need a `deleted` boolean field?" answer: "RxDB does not hard delete records. It uses soft deletes via a `deleted` flag so the deletion can replicate to other clients without losing data in offline scenarios. Without this field, sync will not work correctly." - question: "Can I use a framework other than SvelteKit for this pattern?" answer: "Yes. RxDB runs in any JavaScript environment, including React, Vue, Angular, React Native, and Electron. The replication plugin for Appwrite is framework agnostic, so the same sync logic carries over." @@ -55,21 +55,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` | Text | 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. +> **Note:** The `deleted` column 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 | | --- | --- | --- | --- | --- | @@ -116,7 +116,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: @@ -127,14 +127,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() @@ -221,7 +221,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 } }); @@ -251,7 +251,7 @@ const setupReplication = async (db) => { replicationIdentifier: 'journals-replication', client, databaseId: appwriteConfig.databaseId, - collectionId: appwriteConfig.collectionId, + collectionId: appwriteConfig.tableId, deletedField: 'deleted', collection: db.entries, pull: { @@ -889,4 +889,4 @@ Learn more about RxDB and Appwrite: - [RxDB docs for Appwrite](https://rxdb.info/replication-appwrite.html#do-other-things-with-the-replication-state) - [Appwrite offline sync docs](/docs/products/databases/offline) -- [RxDB in the Integrations catalog](/integrations/replication-rxdb) \ No newline at end of file +- [RxDB in the Integrations catalog](/integrations/replication-rxdb) diff --git a/src/routes/blog/post/open-source-contributors-16/+page.markdoc b/src/routes/blog/post/open-source-contributors-16/+page.markdoc index bec9f102a1a..b0196b1501d 100644 --- a/src/routes/blog/post/open-source-contributors-16/+page.markdoc +++ b/src/routes/blog/post/open-source-contributors-16/+page.markdoc @@ -45,7 +45,7 @@ We appreciate the work of everyone involved, from coding and translations to doc - removed unnecessary plus icon in [#1283](https://github.com/appwrite/console/pull/1283) - deleted expired tags per project in [#8239](https://github.com/appwrite/appwrite/pull/8239) - added tests to validate headers aren’t being overridden in [#8228](https://github.com/appwrite/appwrite/pull/8228) - - added `default` to collection attributes in [#8271](https://github.com/appwrite/appwrite/pull/8271) + - added `default` to table columns in [#8271](https://github.com/appwrite/appwrite/pull/8271) - fixed document APIs that don't support redirects in [#8233](https://github.com/appwrite/appwrite/pull/8233) - fixed the domain check in [#8472](https://github.com/appwrite/appwrite/pull/8472) - updated Appwrite versions in [#884](https://github.com/appwrite/sdk-generator/pull/884) diff --git a/src/routes/blog/post/open-source-vercel-alternative/+page.markdoc b/src/routes/blog/post/open-source-vercel-alternative/+page.markdoc index 13e4e80039d..065d8e97239 100644 --- a/src/routes/blog/post/open-source-vercel-alternative/+page.markdoc +++ b/src/routes/blog/post/open-source-vercel-alternative/+page.markdoc @@ -40,7 +40,7 @@ One of the biggest differences between Appwrite Sites and Vercel is how they fit **Vercel** focuses primarily on frontend deployment. It excels at handling static assets and serverless-rendered content, especially from Next.js. It also supports other frontend frameworks like Astro, SvelteKit, Nuxt, and Remix, although it's deeply optimized for Next.js and includes native support for features like ISR (Incremental Static Regeneration) and Edge Middleware. -**Appwrite Sites**, on the other hand, is part of a **complete development platform**. You're not just deploying your frontend. You're deploying it in the same environment that already knows about your database collections, users, sessions, cloud functions, storage buckets, and APIs. That means the same platform handles everything from user auth to file uploads to server-side logic, and your frontend can interact with it all without any manual configuration. +**Appwrite Sites**, on the other hand, is part of a **complete development platform**. You're not just deploying your frontend. You're deploying it in the same environment that already knows about your database tables, users, sessions, cloud functions, storage buckets, and APIs. That means the same platform handles everything from user auth to file uploads to server-side logic, and your frontend can interact with it all without any manual configuration. This level of integration also means less risk of misconfiguration when connecting your services. Your environment variables, domain rules, CORS policies, and secrets are all managed within one project scope, reducing the friction between your frontend and backend. @@ -142,7 +142,7 @@ Both platforms offer strong environment variable support, with scope control and These built-in variables make it much easier to integrate your front end with the rest of your Appwrite services. You don't have to manually pass your API endpoint or project ID because it's already available. -Appwrite also provides a **dynamic API key automatically**, scoped and injected for both the build process and runtime (for SSR). This makes it easier to create or configure collections at build time, or securely perform server-to-server operations during SSR. If you were deploying on Vercel, you’d need to manually generate an Appwrite API key, store it as a secret, and rotate it regularly to maintain security. +Appwrite also provides a **dynamic API key automatically**, scoped and injected for both the build process and runtime (for SSR). This makes it easier to create or configure tables at build time, or securely perform server-to-server operations during SSR. If you were deploying on Vercel, you’d need to manually generate an Appwrite API key, store it as a secret, and rotate it regularly to maintain security. # Domains, CORS, and routing diff --git a/src/routes/blog/post/personal-chatbot-gpt-4o/+page.markdoc b/src/routes/blog/post/personal-chatbot-gpt-4o/+page.markdoc index c6ec52c0456..f8b6200fe40 100644 --- a/src/routes/blog/post/personal-chatbot-gpt-4o/+page.markdoc +++ b/src/routes/blog/post/personal-chatbot-gpt-4o/+page.markdoc @@ -284,5 +284,5 @@ And with that, you have successfully deployed your personal chatbot powered by G Additionally, if you would like to learn more about Appwrite Functions, here are some resources: -- [Appwrite Functions docs](https://appwrite.io/docs/functions): These documents provide more information on how to use Appwrite Functions. +- [Appwrite Functions docs](https://appwrite.io/docs/functions): Learn more about how to use Appwrite Functions. - [Appwrite Discord](https://discord.com/invite/appwrite): Connect with other developers and the Appwrite team for discussion, questions, and collaboration. diff --git a/src/routes/blog/post/planetscale-databases-alternative/+page.markdoc b/src/routes/blog/post/planetscale-databases-alternative/+page.markdoc index 0b58d97c825..754aa322897 100644 --- a/src/routes/blog/post/planetscale-databases-alternative/+page.markdoc +++ b/src/routes/blog/post/planetscale-databases-alternative/+page.markdoc @@ -13,7 +13,7 @@ faqs: - question: "Can I run raw SQL queries on Appwrite Databases?" answer: "No. Appwrite exposes a query API and SDKs instead of direct SQL access. You can model tables, indexes, and relationships, and run filtered, paginated queries, but the underlying SQL layer is not exposed to users." - question: "How do permissions work in Appwrite Databases?" - answer: "You set permissions at the database, collection, and document level using roles like `any`, `users`, `team`, or specific user IDs. This means access control lives next to the data rather than in a separate rules engine." + answer: "You set permissions at the database, table, and row level using roles like `any`, `users`, `team`, or specific user IDs. This means access control lives next to the data rather than in a separate rules engine." - question: "Does Appwrite handle backups automatically?" answer: "Yes. Appwrite Cloud takes automated backups, and there is a manual database backup feature for on demand snapshots. You can restore from backups without downtime, including on the free plan." - question: "Can I self-host Appwrite Databases?" diff --git a/src/routes/blog/post/product-update-august-2025/+page.markdoc b/src/routes/blog/post/product-update-august-2025/+page.markdoc index 7afcf0ea5fc..3d3d5a4e4fa 100644 --- a/src/routes/blog/post/product-update-august-2025/+page.markdoc +++ b/src/routes/blog/post/product-update-august-2025/+page.markdoc @@ -14,9 +14,9 @@ faqs: - question: "What does Appwrite Cloud GA mean for existing users?" answer: "GA means the beta tag is gone and Appwrite Cloud is now a generally available product backed by formal availability and support commitments. Nothing changes for existing projects, but you get the assurance that the platform is production stable." - question: "What is TablesDB?" - answer: "TablesDB is a new API layer for [Appwrite Databases](/docs/products/databases) that uses relational terminology: tables, rows, and columns instead of collections, documents, and attributes. It also ships with a spreadsheet style console UI for inline editing and bulk actions." - - question: "Do I need to migrate from the document API to TablesDB?" - answer: "No. The document based methods continue to work and remain backwards compatible. They are deprecated for major upgrades, but they still receive security patches. New projects should adopt TablesDB to access future improvements." + answer: "TablesDB is a new API layer for [Appwrite Databases](/docs/products/databases) that uses relational terminology: tables, rows, and columns. It also ships with a spreadsheet style console UI for inline editing and bulk actions." + - question: "Do I need to migrate existing projects to TablesDB?" + answer: "No. Existing projects continue to work and remain backwards compatible. New projects should adopt TablesDB to access future improvements." - question: "Where is the new San Francisco cloud region available?" answer: "San Francisco (SFO) is available on all Free, Pro, and Enterprise plans. You can create new projects there or migrate existing projects using Appwrite Cloud migrations." - question: "What are atomic numeric operations?" @@ -77,17 +77,13 @@ Want to switch to a new region? Here are some resources to help you: Appwrite Databases now feels more relational. We’ve updated the terminology to match the relational model that developers are most familiar with. -Here’s what’s changed: - -- Collections are now Tables -- Documents are now Rows -- Attributes are now Columns +The updated wording now centers Appwrite Databases around tables, rows, and columns. To support the terminology change, we’ve added the TablesDB API. A new API layer that helps you work with relational database concepts like tables, columns, and rows, without requiring any changes to your existing apps. -The old document-based methods are now deprecated and will no longer receive major upgrades. They will continue to receive security patches and essential maintenance, and we are ensuring backwards compatibility with those methods for the API, so there’s no need to migrate immediately if a specific issue arises. +The older Databases methods are now deprecated and will no longer receive major upgrades. They will continue to receive security patches and essential maintenance, and we are ensuring backwards compatibility for the API, so there’s no need to migrate immediately if a specific issue arises. However, we recommend adopting the new TablesDB API for future projects to take advantage of ongoing improvements and new capabilities. @@ -113,7 +109,7 @@ You can now: This new database feature optimizes your app’s performance and saves you time. -You can safely update numeric fields (likes, retries, stock, or scores) directly on the server, without conflicts or race conditions. No need to fetch the full document just for a single increment/decrement operation. +You can safely update numeric fields (likes, retries, stock, or scores) directly on the server, without conflicts or race conditions. No need to fetch the full row just for a single increment/decrement operation. [Learn more](/blog/post/announcing-atomic-numeric-operations) diff --git a/src/routes/blog/post/product-update-december-2024/+page.markdoc b/src/routes/blog/post/product-update-december-2024/+page.markdoc index ab54b921285..b40a39acb73 100644 --- a/src/routes/blog/post/product-update-december-2024/+page.markdoc +++ b/src/routes/blog/post/product-update-december-2024/+page.markdoc @@ -59,7 +59,7 @@ Integrate with popular AI tools like Pinecone, OpenAI and Perplexity, among othe A CLI can be your best friend if done right. The [new version](https://appwrite.io/blog/post/introducing-new-appwrite-cli) was a more than welcome addition and has made developing applications with Appwrite a lot more enjoyable. -No more overwriting or deleting existing collection data during deployments. With the new Appwrite CLI, you can test functions locally and push or pull data between Appwrite and your CLI seamlessly without affecting existing data. +No more overwriting or deleting existing table data during deployments. With the new Appwrite CLI, you can test functions locally and push or pull data between Appwrite and your CLI seamlessly without affecting existing data. ## Improved SSR support - Implement SSR authentication with ease @@ -162,4 +162,3 @@ If you'd like to participate in next month's Community Recognitions, [join our D As we look to 2025, we are grateful to our community and are focused on improving Appwrite to help developers build solutions that solve real problems. Here’s to another year of making software development accessible and enjoyable for all developers. Follow us on [X](https://x.com/appwrite) and check our [Changelog](https://appwrite.io/changelog) regularly, as we will release more information in the coming weeks. - diff --git a/src/routes/blog/post/product-update-jan-2025/+page.markdoc b/src/routes/blog/post/product-update-jan-2025/+page.markdoc index e770e0e15cc..8a18d34dd07 100644 --- a/src/routes/blog/post/product-update-jan-2025/+page.markdoc +++ b/src/routes/blog/post/product-update-jan-2025/+page.markdoc @@ -77,7 +77,7 @@ Push Notifications just got a major upgrade this month. Now, you can run backgro ## Billing improvements, UI fixes, and performance updates -We believe little things make big things happen. That’s why we rolled out small yet impactful updates, including a clearer billing system, UI improvements, and fixes for collections and mobile experiences, all aimed at improving your overall Appwrite experience. +We believe little things make big things happen. That’s why we rolled out small yet impactful updates, including a clearer billing system, UI improvements, and fixes for tables and mobile experiences, all aimed at improving your overall Appwrite experience. [Go to changelog](https://appwrite.io/changelog/entry/2025-01-03) diff --git a/src/routes/blog/post/product-update-july-2025/+page.markdoc b/src/routes/blog/post/product-update-july-2025/+page.markdoc index d3d67814683..fe7db8c3708 100644 --- a/src/routes/blog/post/product-update-july-2025/+page.markdoc +++ b/src/routes/blog/post/product-update-july-2025/+page.markdoc @@ -11,15 +11,15 @@ featured: false callToAction: true faqs: - question: "What major features did Appwrite ship in July 2025?" - answer: "July included the general availability of [Appwrite Sites](/docs/products/sites), CSV import for Databases, the Bulk API, Database Upsert, encrypted string attribute support, and auto-increment columns. The release also included Console and deployment improvements plus richer OTP email previews." + answer: "July included the general availability of [Appwrite Sites](/docs/products/sites), CSV import for Databases, the Bulk API, Database Upsert, encrypted text column support, and auto-increment columns. The release also included Console and deployment improvements plus richer OTP email previews." - question: "What is the Bulk API in Appwrite Databases?" - answer: "The Bulk API lets you create, update, delete, or upsert many documents in a single request instead of looping through individual calls. It is useful for migrations, seeding, and syncing data between systems." + answer: "The Bulk API lets you create, update, delete, or upsert many rows in a single request instead of looping through individual calls. It is useful for migrations, seeding, and syncing data between systems." - question: "What is a database upsert and when should I use it?" - answer: "Upsert is a single operation that creates a document if it does not exist or updates it if it does. It removes the need to do a read followed by a conditional write, which simplifies idempotent sync flows and reduces round trips." + answer: "Upsert is a single operation that creates a row if it does not exist or updates it if it does. It removes the need to do a read followed by a conditional write, which simplifies idempotent sync flows and reduces round trips." - question: "What does CSV import do in [Appwrite Databases](/docs/products/databases)?" - answer: "CSV import lets you bring a CSV file directly into an Appwrite collection through the migration APIs. It is built for seeding collections, onboarding legacy data, and bulk loading without writing a custom script." - - question: "How does encrypted string attribute support work?" - answer: "Appwrite can encrypt string attributes at rest in [Databases](/docs/products/databases) and [Storage](/docs/products/storage), so you do not have to implement encryption logic in your application code. You enable it on the attribute from the Console." + answer: "CSV import lets you bring a CSV file directly into an Appwrite table through the migration APIs. It is built for seeding tables, onboarding legacy data, and bulk loading without writing a custom script." + - question: "How does encrypted text column support work?" + answer: "Appwrite can encrypt text columns at rest in [Databases](/docs/products/databases), so you do not have to implement encryption logic in your application code. You enable encryption on the column from the Console." - question: "What changed with OTP email previews?" answer: "Appwrite now surfaces the OTP code in the email preview, so end users can read the code from the notification without opening the message. This reduces friction on phones and improves login completion rates." --- @@ -49,7 +49,7 @@ You can now host your applications directly from the Appwrite console, making Ap ![CSV imports](/images/blog/announcing-csv-imports/cover.avif) -We started July with CSV import. Built on top of Appwrite's migration APIs, this Databases feature makes it easy to bring in large datasets, seed collections, or migrate structured data using only a CSV file. +We started July with CSV import. Built on top of Appwrite's migration APIs, this Databases feature makes it easy to bring in large datasets, seed tables, or migrate structured data using only a CSV file. [Learn more](/blog/post/announcing-csv-imports) @@ -57,7 +57,7 @@ We started July with CSV import. Built on top of Appwrite's migration APIs, thi ![Bulk API](/images/blog/announcing-bulk-api/cover.avif) -You can now handle heavy data workloads with greater ease. With Bulk API, you can create, update, delete, or upsert multiple documents in a single API call. +You can now handle heavy data workloads with greater ease. With Bulk API, you can create, update, delete, or upsert multiple rows in a single API call. This is especially useful for managing large datasets or syncing data between systems without multiple API calls. @@ -67,15 +67,15 @@ This is especially useful for managing large datasets or syncing data between sy ![Database Upsert](/images/blog/announcing-database-upsert/cover.avif) -Database Upsert lets you create or update a document with a single API call. If the document exists, it’s updated; if not, it’s created. This removes the need for manual existence checks, reduces extra requests, and simplifies your logic. +Database Upsert lets you create or update a row with a single API call. If the row exists, it’s updated; if not, it’s created. This removes the need for manual existence checks, reduces extra requests, and simplifies your logic. [Learn more](/blog/post/announcing-database-upsert) -# Encrypted string attribute support +# Encrypted text column support ![Encrypted ](/images/blog/announcing-encrypted-string-attributes/cover.avif) -We’ve added an extra layer of security to Appwrite Databases and Storage. This feature allows you to encrypt string attributes at rest directly from the Appwrite console, without needing to implement manual encryption. +We’ve added an extra layer of security to Appwrite Databases. This feature allows you to encrypt text columns at rest directly from the Appwrite console, without needing to implement manual encryption. [Learn more](/blog/post/announcing-encrypted-string-attributes) @@ -83,7 +83,7 @@ We’ve added an extra layer of security to Appwrite Databases and Storage. This ![Auto-increment](/images/blog/announcing-auto-increment-support/cover.avif) -Appwrite Databases now support auto-increment fields. A `$sequence` column is automatically managed in your collection, increasing with each new document. This helps keep your data ordered without requiring manual handling. +Appwrite Databases now support auto-increment fields. A `$sequence` column is automatically managed in your table, increasing with each new row. This helps keep your data ordered without requiring manual handling. [Learn more](/blog/post/announcing-auto-increment-support) @@ -134,8 +134,8 @@ If you'd like to participate in next month's Community Recognitions, [join our D **Read** - [Building with Appwrite Sites templates](/blog/post/building-with-sites-templates) -- [Using $sequence to track document order in Appwrite](/blog/post/track-document-order-with-sequence) -- [Secure sensitive database fields with encrypted string attributes](/blog/post/encrypted-attributes-for-sensitive-fields) +- [Using $sequence to track row order in Appwrite](/blog/post/track-document-order-with-sequence) +- [Secure sensitive database fields with encrypted columns](/blog/post/encrypted-attributes-for-sensitive-fields) **Watch** diff --git a/src/routes/blog/post/product-update-june/+page.markdoc b/src/routes/blog/post/product-update-june/+page.markdoc index 83d5bc4b488..d2cda72075f 100644 --- a/src/routes/blog/post/product-update-june/+page.markdoc +++ b/src/routes/blog/post/product-update-june/+page.markdoc @@ -79,7 +79,7 @@ Big shoutout to [Manjunath0408](https://github.com/Manjunath0408) and [Darshan]( - Improved Console loading bar - [Throw an error if invalid domain is added to a function](https://github.com/appwrite/console/pull/1115) by [Manjunath0408](https://github.com/Manjunath0408) - [Fix for inconsistent build logs](https://github.com/appwrite/appwrite/issues/6237) by [Darshan](https://github.com/ItzNotABug) -- [Fix for migration of collections, attributes and indexes of Databases](https://github.com/appwrite/appwrite/issues/8044) +- [Fix for Databases migration](https://github.com/appwrite/appwrite/issues/8044) # Open source community recognitions @@ -114,4 +114,4 @@ If you’ve checked out our public roadmap, you might have seen what we’ve bee - An updated version of [Pink design](https://pink.appwrite.io/) is in the works - A new release means a new Init… keep an eye out for the announcement! -Follow us on [socials](https://x.com/appwrite) and check our [Changelog](https://appwrite.io/changelog) regularly, as we will release more information in the coming weeks. \ No newline at end of file +Follow us on [socials](https://x.com/appwrite) and check our [Changelog](https://appwrite.io/changelog) regularly, as we will release more information in the coming weeks. diff --git a/src/routes/blog/post/product-update-september/+page.markdoc b/src/routes/blog/post/product-update-september/+page.markdoc index 7af11fd9dd4..470297cbc33 100644 --- a/src/routes/blog/post/product-update-september/+page.markdoc +++ b/src/routes/blog/post/product-update-september/+page.markdoc @@ -15,8 +15,8 @@ faqs: answer: "CCPA is a California privacy law that gives consumers rights over how their personal data is collected and used. Appwrite being CCPA compliant means the platform meets those requirements, which makes it easier for you to satisfy your own obligations to California users." - question: "What landed in Appwrite 1.6 for self-hosted?" answer: "Self-hosted 1.6 includes local development tooling, the new Appwrite CLI, binary executions, dynamic keys, delayed executions, the Go SDK and runtime, and session alerts. These are the same features that shipped on Cloud during Init." - - question: "Can I update existing collection attribute names and sizes in Appwrite?" - answer: "Yes. You can rename an attribute or change its size directly from the Console without recreating the attribute or running a manual migration. This makes schema iteration much less painful." + - question: "Can I update existing table column names and sizes in Appwrite?" + answer: "Yes. You can rename a column or change its size directly from the Console without recreating the column or running a manual migration. This makes schema iteration much less painful." - question: "What was the 1.5.11 security patch about?" answer: "An issue in open-runtimes/executor caused error stack traces to leak into build and execution logs. Cloud was patched and affected keys were rotated by the team, and self-hosted users should upgrade to 1.5.11 to apply the fix." --- @@ -94,11 +94,11 @@ If you are using a self-hosted Appwrite instance, we strongly recommend upgradin [Check out the changelog for more details.](https://appwrite.io/changelog/entry/2024-09-17) -## Updating collection attribute names and sizes +## Updating table column names and sizes -You can now update attribute names and sizes directly from your Cloud Console. +You can now update column names and sizes directly from your Cloud Console. -This update provides better flexibility in managing your data models, allowing you to adjust attributes without needing to recreate them or handle complicated migrations. +This update provides better flexibility in managing your data models, allowing you to adjust columns without needing to recreate them or handle complicated migrations. This is just one in a series of updates we plan to release for Appwrite databases over the next few weeks. @@ -138,4 +138,4 @@ October will be jam-packed with announcements and new features, so stay tuned! - A certain Backups teaser made waves on our [Discord channel](https://appwrite.io/discord)… - And we filmed some exciting new stuff while at our Camp in Barcelona! -Follow us on [X](https://x.com/appwrite) and check our [Changelog](https://appwrite.io/changelog) regularly, as we will release more information in the coming weeks. \ No newline at end of file +Follow us on [X](https://x.com/appwrite) and check our [Changelog](https://appwrite.io/changelog) regularly, as we will release more information in the coming weeks. diff --git a/src/routes/blog/post/react-protected-routes/+page.markdoc b/src/routes/blog/post/react-protected-routes/+page.markdoc index 30a0c762564..6ba1aea9014 100644 --- a/src/routes/blog/post/react-protected-routes/+page.markdoc +++ b/src/routes/blog/post/react-protected-routes/+page.markdoc @@ -18,7 +18,7 @@ faqs: - question: "How can I plug Appwrite Auth into a React protected route?" answer: "Call [`account.get()`](/docs/products/auth) inside the wrapper component, store the result in state, and render `` if it succeeds or `` if it throws. Wrap the call in a loading state to avoid a flash of redirect while the request is in flight." - question: "Should I check authentication on the client only?" - answer: "No. Client-side protected routes are about UX, not security. Always enforce permissions on the backend, either through [Appwrite document permissions](/docs/products/databases) or your own server logic, since anyone can bypass a client-only check." + answer: "No. Client-side protected routes are about UX, not security. Always enforce permissions on the backend, either through [Appwrite row permissions](/docs/products/databases) or your own server logic, since anyone can bypass a client-only check." --- In this tutorial, we will explore a straightforward method for implementing protected routes in a React application. The aim is to ensure that users can only access certain pages, such as home and profile, after passing an authentication check. If a user is not authenticated, they will be redirected to the login page. @@ -33,13 +33,13 @@ First, create a new file named `ProtectedRoutes.jsx`. In this file, you will imp Below is a basic structure for the `ProtectedRoutes` component: ```jsx - -import { Outlet, navigate } from 'react-router-dom'; +import { Outlet, Navigate } from 'react-router-dom'; const ProtectedRoutes = () => { const user = null; // Simulate an unauthenticated user - return user ? : // Redirect to login if not authenticated - + return user ? : ; // Redirect to login if not authenticated +}; + export default ProtectedRoutes; ``` @@ -49,25 +49,26 @@ export default ProtectedRoutes; With the `ProtectedRoutes` component created, the next step is to wrap the routes we want to protect. We can nest all child routes by using the standard `` component and by passing in `` as the element into the parent route. ```jsx -//App.jsx -... import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import ProtectedRoutes from './utils/ProtectedRoutes'; - -function App(){ - return - - - } path="/login"'/> - - {/* 👇 Wrap your protected routes */} - }> - <Route element={} path="/"/> - <Route element= {} path="/profile"/> - - - - +import Login from './Login'; +import Home from './Home'; +import Profile from './Profile'; + +function App() { + return ( + + + } path="/login" /> + + {/* Wrap your protected routes */} + }> + } path="/" /> + } path="/profile" /> + + + + ); } ``` @@ -87,4 +88,4 @@ In summary, you have implemented protected routes in your React application. By - [Protected routes in React video tutorial](https://www.youtube.com/watch?v=pyfwQUc5Ssk) - [React quick start with Appwrite](https://appwrite.io/docs/quick-starts/react-native) - [Set up Google auth in React](https://appwrite.io/blog/post/set-up-google-auth-appwrite-react) -- [Build a cross-platform application in React Native](https://appwrite.io/blog/post/building-cross-platform-applications-with-react-native) \ No newline at end of file +- [Build a cross-platform application in React Native](https://appwrite.io/blog/post/building-cross-platform-applications-with-react-native) diff --git a/src/routes/blog/post/save-weeks-managed-backends/+page.markdoc b/src/routes/blog/post/save-weeks-managed-backends/+page.markdoc index 5d8e432a6b9..f87b52cea35 100644 --- a/src/routes/blog/post/save-weeks-managed-backends/+page.markdoc +++ b/src/routes/blog/post/save-weeks-managed-backends/+page.markdoc @@ -70,7 +70,7 @@ Setting up a queue-based job processing system or serverless function infrastruc With a managed backend platform like Appwrite, the same components are available on day one: - **Authentication** is configured in the console and available via SDK. Adding OAuth providers takes minutes per provider. MFA, password policies, and session limits are toggle configurations. -- **Database** is set up by creating collections with defined schema and indexes. The query API is available immediately. No API layer to build. +- **Database** is set up by creating tables with defined schema and indexes. The query API is available immediately. No API layer to build. - **File storage** is configured in a storage bucket with permission settings. The SDK handles chunking, validation, and serving files. - **Messaging** (push notifications, email, SMS) is available via the Messaging service with configured providers. - **Serverless functions** deploy from a connected Git repository and are triggered by HTTP, events, or schedules. diff --git a/src/routes/blog/post/serverless-functions-best-practices/+page.markdoc b/src/routes/blog/post/serverless-functions-best-practices/+page.markdoc index eb53de4d9ad..e1c5db67f1a 100644 --- a/src/routes/blog/post/serverless-functions-best-practices/+page.markdoc +++ b/src/routes/blog/post/serverless-functions-best-practices/+page.markdoc @@ -144,7 +144,7 @@ Large deployment packages slow down the function initialization process and incr Ensure only authorized users can execute your functions. Appwrite provides built-in authentication and authorization features that you can leverage to secure your functions. This is important for protecting sensitive data and preventing your functions from being misused or easily exploited. -You must ensure that authorization is enforced on your serverless functions and not solely on the client side. Client-side authorization can be bypassed, leading to security vulnerabilities. Appwrite Databases and Storage services can be configured to enforce access control rules. For example, you can restrict read and write access to documents by navigating to your database collection settings in the Appwrite Console and setting the appropriate permissions. It looks like this: +You must ensure that authorization is enforced on your serverless functions and not solely on the client side. Client-side authorization can be bypassed, leading to security vulnerabilities. Appwrite Databases and Storage services can be configured to enforce access control rules. For example, you can restrict read and write access to rows by navigating to your database table settings in the Appwrite Console and setting the appropriate permissions. It looks like this: ![Functions-authorization](/images/blog/serverless-functions/3.avif) diff --git a/src/routes/blog/post/set-up-google-auth-appwrite-react/+page.markdoc b/src/routes/blog/post/set-up-google-auth-appwrite-react/+page.markdoc index 2f002935b56..1837c1be959 100644 --- a/src/routes/blog/post/set-up-google-auth-appwrite-react/+page.markdoc +++ b/src/routes/blog/post/set-up-google-auth-appwrite-react/+page.markdoc @@ -146,7 +146,9 @@ export const loginWithGoogle = async () => { export const logoutUser = async () => { try { - await account.deleteSession('current') + await account.deleteSession({ + sessionId: 'current' + }) } catch (error) { console.error(error) } diff --git a/src/routes/blog/post/setting-up-google-signin/+page.markdoc b/src/routes/blog/post/setting-up-google-signin/+page.markdoc index 7309a9ad238..a0a0ff42c10 100644 --- a/src/routes/blog/post/setting-up-google-signin/+page.markdoc +++ b/src/routes/blog/post/setting-up-google-signin/+page.markdoc @@ -124,9 +124,11 @@ const account = new Account(client); // Go to OAuth provider login page account.createOAuth2Session( - OAuthProvider.Google, // provider - 'https://example.com/success', // redirect here on success - 'https://example.com/failed', // redirect here on failure + { + provider: OAuthProvider.Google, + success: 'https://example.com/success', + failure: 'https://example.com/failed' +}, // redirect here on failure ); ``` @@ -137,7 +139,9 @@ If you're facing problems where cookies are not being set after authentication, You can use the following method to get more information about the current logged-in session. ```jsx -const session = await account.getSession('current'); +const session = await account.getSession({ + sessionId: 'current' +}); ``` The `getSession()` method will provide you with all the information about the session of the current logged-in user. diff --git a/src/routes/blog/post/simplify-messaging-twilio/+page.markdoc b/src/routes/blog/post/simplify-messaging-twilio/+page.markdoc index 11964bb62e6..98acd5d0596 100644 --- a/src/routes/blog/post/simplify-messaging-twilio/+page.markdoc +++ b/src/routes/blog/post/simplify-messaging-twilio/+page.markdoc @@ -98,12 +98,14 @@ client ; const promise = await messaging.updateTwilioProvider( - '', // providerId - '', // name (optional) - false, // enabled (optional) - '', // accountSid (optional) - '', // authToken (optional) - '' // from (optional) + { + providerId: '', + name: '', + enabled: false, + accountSid: '', + authToken: '', + from: '' +} // from (optional) ); ``` 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 61aa9d53f3b..7e8a5431d9b 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,7 +1,7 @@ --- 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 cover: /images/blog/simplify-your-data-management-with-relationships/cover.avif timeToRead: 6 @@ -9,22 +9,22 @@ author: jake-barnby category: accessibility faqs: - question: "What relationship types does Appwrite Databases support?" - answer: "Four: one-to-one, one-to-many, many-to-one, and many-to-many. You pick the type when creating the relationship attribute, and Appwrite enforces the cardinality so you do not have to model it manually with join tables or ID arrays." + answer: "Four: one-to-one, one-to-many, many-to-one, and many-to-many. You pick the type when creating the relationship column, and Appwrite enforces the cardinality so you do not have to model it manually with join tables or ID arrays." - question: "When should I use a one-way vs two-way relationship?" answer: "Use a one-way relationship when only one side needs to know about the other (for example, a movie that lists its reviews but reviews do not need to expose the movie). Use two-way when both sides need to traverse the link in queries. Two-way costs slightly more storage and write overhead, so do not enable it by default." - question: "Does Appwrite support relationships across [Databases](/docs/products/databases) products?" - answer: "Relationships are defined within a single Appwrite database between tables in that database. You cannot create a relationship attribute that points to a row in a different database, so model related entities in the same database." + answer: "Relationships are defined within a single Appwrite database between tables in that database. You cannot create a relationship column that points to a row in a different database, so model related entities in the same database." - question: "What are the deletion behaviors for related rows?" answer: "Appwrite lets you choose how related rows behave when a parent is deleted: restrict (block the delete), cascade (delete the children), or set null (clear the relationship). Pick based on your data model. Cascade is convenient but irreversible, restrict is safer when child data has independent value." - question: "Should I model everything with relationships or denormalize?" - answer: "Use relationships when the related data has its own lifecycle, is shared across parents, or is too large to embed. Denormalize (duplicate small, rarely-changing fields onto the parent) when you mainly need a label or a couple of attributes in a list view and want to avoid the extra lookup. Both patterns coexist comfortably in Appwrite." + answer: "Use relationships when the related data has its own lifecycle, is shared across parents, or is too large to embed. Denormalize (duplicate small, rarely-changing fields onto the parent) when you mainly need a label or a couple of columns in a list view and want to avoid the extra lookup. Both patterns coexist comfortably in Appwrite." - question: "Do relationships replace the need to write joins?" answer: "For most reads, yes. Appwrite resolves related rows in the response when you query the parent, so you do not need to perform a separate fetch and merge in your code. For complex aggregations or filters across multiple relationships, you may still want to break the work into multiple queries." --- -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, 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 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 @@ -32,25 +32,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.avif) -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 row in one table is associated with only one row 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.avif) -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 row in one table can be associated with multiple rows 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.avif) -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 rows in one table can be associated with a single row 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.avif) -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 rows in one table can be associated with multiple rows 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. @@ -60,7 +60,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: @@ -68,7 +68,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": [ @@ -79,7 +79,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": [ @@ -92,13 +92,13 @@ The movie response: ``` -The review response, notably with no review attribute: +The review response, notably with no review column: ```json { "$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": [], @@ -109,7 +109,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: @@ -117,7 +117,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": [], @@ -126,7 +126,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": [], @@ -137,13 +137,13 @@ The movie response: ``` -The review response, now with the movie attribute: +The review response, now with the movie column: ```json { "$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": [], @@ -151,7 +151,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": [], @@ -161,7 +161,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 @@ -169,23 +169,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 row, 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 rows 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/products/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/the-fastest-way-to-launch-your-next-side-project/+page.markdoc b/src/routes/blog/post/the-fastest-way-to-launch-your-next-side-project/+page.markdoc index 7789ad3855f..ec6621dad75 100644 --- a/src/routes/blog/post/the-fastest-way-to-launch-your-next-side-project/+page.markdoc +++ b/src/routes/blog/post/the-fastest-way-to-launch-your-next-side-project/+page.markdoc @@ -16,7 +16,7 @@ faqs: - question: "How can Appwrite speed up side project development?" answer: "Appwrite gives you the backend pieces most side projects need out of the box, including [Auth](/docs/products/auth), [Databases](/docs/products/databases), [Storage](/docs/products/storage), [Functions](/docs/products/functions), [Messaging](/docs/products/messaging), and [Sites](/docs/products/sites) for hosting. You can have a working backend in minutes instead of spending days on infrastructure." - question: "How long does it take to launch a side project with Appwrite?" - answer: "A realistic timeline is around three days. Day one for project setup, auth, and database collections. Day two for the core feature. Day three for a basic UI, deployment, and sharing with a small group of users. The point is to learn fast, not ship perfect." + answer: "A realistic timeline is around three days. Day one for project setup, auth, and database tables. Day two for the core feature. Day three for a basic UI, deployment, and sharing with a small group of users. The point is to learn fast, not ship perfect." - question: "When should I launch my side project?" answer: "Launch as soon as the core feature works and a real user can do something meaningful with it. If your project feels polished and complete before launch, you probably waited too long. Real users will reshape your roadmap in ways you cannot predict from the inside." - question: "Should I worry about scale on day one of a side project?" @@ -79,7 +79,7 @@ Launching quickly doesn't mean cutting corners. It means being deliberate about A realistic fast-launch timeline using Appwrite: ## Day 1: Foundation -Define the problem and the MVP scope. Set up your Appwrite project, configure authentication, and create your database collections. At the end of day one, users can sign up and your data layer is ready. +Define the problem and the MVP scope. Set up your Appwrite project, configure authentication, and create your database tables. At the end of day one, users can sign up and your data layer is ready. ## Day 2: Core feature Build the one feature that proves your idea has value. With authentication and database already handled, your entire focus goes into product logic, the thing only you are building. diff --git a/src/routes/blog/post/the-future-of-coding-cursor-ai-and-the-rise-of-backend-automation-with-appwrite/+page.markdoc b/src/routes/blog/post/the-future-of-coding-cursor-ai-and-the-rise-of-backend-automation-with-appwrite/+page.markdoc index be867f6d53a..4bc963aebb4 100644 --- a/src/routes/blog/post/the-future-of-coding-cursor-ai-and-the-rise-of-backend-automation-with-appwrite/+page.markdoc +++ b/src/routes/blog/post/the-future-of-coding-cursor-ai-and-the-rise-of-backend-automation-with-appwrite/+page.markdoc @@ -14,9 +14,9 @@ faqs: - question: "What is the Model Context Protocol (MCP)?" answer: "MCP is an open standard that lets editors, agents, and developer tools communicate securely with external services. It removes the need to write custom connectors or manage tokens manually, so AI assistants can talk directly to platforms like Appwrite." - question: "How does the Appwrite MCP server work with Cursor?" - answer: "The Appwrite MCP server lets Cursor interact with your Appwrite project using natural language. You can create projects, add database collections, configure storage, and scaffold functions by describing what you need. The MCP layer handles the API communication." + answer: "The Appwrite MCP server lets Cursor interact with your Appwrite project using natural language. You can create projects, add database tables, configure storage, and scaffold functions by describing what you need. The MCP layer handles the API communication." - question: "What can I do with Appwrite MCP through an AI editor?" - answer: "You can create and modify backend resources by talking to your editor, including spinning up [Databases](/docs/products/databases) collections, configuring [Auth](/docs/products/auth), uploading files to [Storage](/docs/products/storage), and deploying [Functions](/docs/products/functions). It is faster than clicking through dashboards or writing boilerplate code." + answer: "You can create and modify backend resources by talking to your editor, including spinning up [Databases](/docs/products/databases) tables, configuring [Auth](/docs/products/auth), uploading files to [Storage](/docs/products/storage), and deploying [Functions](/docs/products/functions). It is faster than clicking through dashboards or writing boilerplate code." - question: "Do I still need to write code if I use Cursor and Appwrite MCP?" answer: "Yes. The AI accelerates scaffolding and configuration, but you still review, refine, and direct the work. Treat the combination as a fast pair programmer and infrastructure assistant, not a replacement for understanding your codebase." - question: "Is the Appwrite MCP server secure to use with AI tools?" @@ -55,7 +55,7 @@ MCP is an open standard that allows editors, agents, and other developer tools t This means you can use plain English in your editor to create or modify backend resources: - “Create a new Appwrite project.” -- “Add a database collection for products.” +- “Add a database table for products.” - “Generate a function to handle user registration.” All of this happens without leaving Cursor. The MCP layer handles the communication between your editor and Appwrite's API securely and consistently. @@ -91,4 +91,3 @@ We're witnessing a fundamental shift in how software gets built. Tools like Curs This isn't just about faster development; it's about removing friction between thinking and building. Developers can experiment more freely, iterate faster, and focus on what matters: solving real problems for real users. Ready to experience this workflow yourself? [Set up the Appwrite MCP server in Cursor](https://appwrite.io/docs/tooling/mcp/cursor) and start building your next project with AI-powered backend automation. - diff --git a/src/routes/blog/post/the-top-3-claude-features-you-are-probably-not-using/+page.markdoc b/src/routes/blog/post/the-top-3-claude-features-you-are-probably-not-using/+page.markdoc index ec43fab85b8..97371f249d9 100644 --- a/src/routes/blog/post/the-top-3-claude-features-you-are-probably-not-using/+page.markdoc +++ b/src/routes/blog/post/the-top-3-claude-features-you-are-probably-not-using/+page.markdoc @@ -127,7 +127,7 @@ Extended thinking, Projects, and prompt caching address different layers of the For extended thinking and prompt caching, the [Anthropic documentation](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) has complete implementation details and model-specific notes worth reading before you ship anything to production. -If you want to go further and connect Claude directly to a live backend, the [Appwrite MCP server](/docs/tooling/mcp) lets Claude interact with your Appwrite project through natural language in Claude Code or Claude Desktop. You can create users, query collections, manage files, and trigger functions without writing a single line of glue code. It's a practical way to bring agentic workflows into a real application stack. +If you want to go further and connect Claude directly to a live backend, the [Appwrite MCP server](/docs/tooling/mcp) lets Claude interact with your Appwrite project through natural language in Claude Code or Claude Desktop. You can create users, query tables, manage files, and trigger functions without writing a single line of glue code. It's a practical way to bring agentic workflows into a real application stack. # More resources diff --git a/src/routes/blog/post/the-underrated-value-of-great-sdk-design/+page.markdoc b/src/routes/blog/post/the-underrated-value-of-great-sdk-design/+page.markdoc index d1b42482a1d..c048024a69d 100644 --- a/src/routes/blog/post/the-underrated-value-of-great-sdk-design/+page.markdoc +++ b/src/routes/blog/post/the-underrated-value-of-great-sdk-design/+page.markdoc @@ -58,7 +58,7 @@ This matters more than it might seem. Passing a plain string where an enum value Appwrite SDKs ship with [enumeration classes](/docs/sdks#enums) for predefined values used across authentication factors, storage compression algorithms, query operators, runtime environments, and more. These enums are available across all supported languages, not just TypeScript. -The [type generation feature](/docs/products/databases/type-generation) takes this further. Run `appwrite types` and the CLI generates typed model classes directly from your database collections, in your project's language. Schema changes propagate to your types automatically, without any manual mapping. +The [type generation feature](/docs/products/databases/type-generation) takes this further. Run `appwrite types` and the CLI generates typed model classes directly from your database tables, in your project's language. Schema changes propagate to your types automatically, without any manual mapping. Developers who want to go even further can use the [`generate` command](/docs/tooling/command-line/generate) to generate a fully typed SDK for their project. Unlike the generic SDKs, a generated SDK reflects your project's specific configuration, giving you end-to-end type safety from your database schema all the way to your client code. diff --git a/src/routes/blog/post/three-important-steps-you-need-to-complete-with-appwrite/+page.markdoc b/src/routes/blog/post/three-important-steps-you-need-to-complete-with-appwrite/+page.markdoc index cf5fd0ab2aa..971f9870d6b 100644 --- a/src/routes/blog/post/three-important-steps-you-need-to-complete-with-appwrite/+page.markdoc +++ b/src/routes/blog/post/three-important-steps-you-need-to-complete-with-appwrite/+page.markdoc @@ -9,13 +9,13 @@ author: dennis-ivy category: product faqs: - question: "What are the three steps to complete before using a new Appwrite project?" - answer: "Add a platform (or generate an API key with scopes for server SDKs), add a hostname if you are using the Web SDK, and set proper permissions on your database collections and storage buckets. Skipping any of these usually leads to errors when making your first request." + answer: "Add a platform (or generate an API key with scopes for server SDKs), add a hostname if you are using the Web SDK, and set proper permissions on your database tables and storage buckets. Skipping any of these usually leads to errors when making your first request." - question: "Why do I need to add a platform in Appwrite?" answer: "Platforms tell Appwrite which clients are allowed to connect to your project. You add a platform from the project overview to register a Web, Flutter, Android, Apple, or server SDK. Without it, your requests are rejected for security." - question: "Why am I getting a CORS error when calling Appwrite from my web app?" answer: "Most CORS errors happen because you have not added the right hostname to your web platform settings in Appwrite. Add the exact origin you are connecting from (including localhost during development) to fix the error. There is a deeper walkthrough in the [CORS error post](/blog/post/cors-error)." - question: "How do permissions work in Appwrite?" - answer: "Permissions control who can read or write data in [Databases](/docs/products/databases) collections and [Storage](/docs/products/storage) buckets. You set them at the collection, bucket, or document level. Without the right permissions, even authenticated users see errors or empty data." + answer: "Permissions control who can read or write data in [Databases](/docs/products/databases) tables and [Storage](/docs/products/storage) buckets. You set them at the table, row, bucket, or file level. Without the right permissions, even authenticated users see errors or empty data." - question: "What is the difference between client and server SDKs in Appwrite?" answer: "Client SDKs run in your app and act on behalf of end users, so they use sessions and respect permissions. Server SDKs run in trusted environments and use API keys with explicit scopes. Pick the SDK that matches where your code runs and what level of access it needs." - question: "What scopes should I give my Appwrite API key?" @@ -42,7 +42,7 @@ When using the Web SDK, you’ll need to set a hostname. This just tells Appwrit # 3 - Permissions -Before you can read and write data from an Appwrite database collection or storage bucket, you’ll need to ensure you have the proper permissions set. This can be set from the “Settings” tab under the “Permissions” section inside a collection or storage bucket. +Before you can read and write data from an Appwrite database table or storage bucket, you’ll need to ensure you have the proper permissions set. This can be set from the “Settings” tab under the “Permissions” section inside a table or storage bucket. ![Permissions overview](/images/blog/three-important-steps-you-need-to-complete-with-appwrite/permissions.avif) diff --git a/src/routes/blog/post/top-7-prompts-every-developer-should-use-to-get-better-results/+page.markdoc b/src/routes/blog/post/top-7-prompts-every-developer-should-use-to-get-better-results/+page.markdoc index c79ee05bb0d..3cd00d65b4b 100644 --- a/src/routes/blog/post/top-7-prompts-every-developer-should-use-to-get-better-results/+page.markdoc +++ b/src/routes/blog/post/top-7-prompts-every-developer-should-use-to-get-better-results/+page.markdoc @@ -177,7 +177,7 @@ For a look at what to avoid, [7 prompting mistakes you need to stop making right # Taking this further with Appwrite -If you're building with AI assistants and using Appwrite as your backend, the [Appwrite MCP server](/docs/tooling/mcp) lets AI tools interact directly with your project. You can query collections, manage functions, and run operations through natural language inside tools like Claude Code, Cursor, or Windsurf. +If you're building with AI assistants and using Appwrite as your backend, the [Appwrite MCP server](/docs/tooling/mcp) lets AI tools interact directly with your project. You can query tables, manage functions, and run operations through natural language inside tools like Claude Code, Cursor, or Windsurf. This means the prompts you've refined for code generation can also drive your backend. Instead of switching context between your AI tool and the Appwrite console, the model can do it directly. 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 3edb3c8ce62..3b9564f4593 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,32 +1,32 @@ --- 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 column to track row order in your database. date: 2025-07-16 cover: /images/blog/track-document-order-with-sequence/cover.avif timeToRead: 6 author: ebenezer-don category: tutorial faqs: - - question: "What is the $sequence attribute in Appwrite?" - answer: "$sequence is a system attribute that Appwrite assigns to every document on creation. It is a unique, auto-incrementing integer scoped to the collection, so the first document gets 1, the second gets 2, and so on. It is read only and cannot be modified or reused." - - question: "Why not just use a timestamp to order documents?" - answer: "Timestamps can collide when documents are created within the same millisecond, which leads to unstable sort order. $sequence is monotonically increasing and unique, so it gives you a deterministic order even under high concurrency." + - question: "What is the $sequence column in Appwrite?" + answer: "$sequence is a system column that Appwrite assigns to every row on creation. It is a unique, auto-incrementing integer scoped to the table, so the first row gets 1, the second gets 2, and so on. It is read only and cannot be modified or reused." + - question: "Why not just use a timestamp to order rows?" + answer: "Timestamps can collide when rows are created within the same millisecond, which leads to unstable sort order. $sequence is monotonically increasing and unique, so it gives you a deterministic order even under high concurrency." - question: "Can I sort and paginate by $sequence in Appwrite?" answer: "Yes. $sequence works with the standard Query API, so you can use Query.orderAsc('$sequence') or Query.orderDesc('$sequence') and combine it with cursor pagination. See the [Appwrite Databases docs](/docs/products/databases) for the full query reference." - question: "Do I have to create $sequence manually?" - answer: "No. Appwrite assigns it automatically when the document is created, alongside other system attributes like $id and $createdAt. You should not define a sequence attribute yourself in your collection schema." + answer: "No. Appwrite assigns it automatically when the row is created, alongside other system columns like $id and $createdAt. You should not define a sequence column yourself in your table schema." - question: "Is $sequence safe to expose in URLs or UI?" answer: "Yes for things like ticket numbers, order numbers, or invoice numbers where a predictable counter is the desired behavior. Avoid using it where you need unguessable IDs (for example, share links), because the value is enumerable. Use $id for those cases." - - question: "Does $sequence reset if I delete documents?" - answer: "No. The counter is monotonic and does not reuse values, so deleting document 42 does not free up the sequence number 42. The next inserted document still gets the next unused integer." + - question: "Does $sequence reset if I delete rows?" + answer: "No. The counter is monotonic and does not reuse values, so deleting row 42 does not free up the sequence number 42. The next inserted row still gets the next unused integer." --- 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 `$sequence` column 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. @@ -38,35 +38,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 row 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: `text`, required: `yes`, size: 256 +- `body` - type: `text`, required: `yes`, size: 1000 -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 @@ -226,16 +226,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 %} @@ -255,10 +255,10 @@ form.addEventListener('submit', async (e) => { if (!title || !body) return try { - await databases.createDocument({ + await tablesDB.createRow({ databaseId, - collectionId, - documentId: ID.unique(), + tableId, + rowId: ID.unique(), data: { title, body, @@ -274,7 +274,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 `'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 %} @@ -285,9 +285,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'), ] @@ -298,16 +298,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 = ` @@ -347,16 +347,16 @@ 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.avif) -- 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 -There was no need to write any logic to generate numbers, track counters, or manage collisions. Appwrite's `$sequence` attribute handled the sequence internally, which was great for our use case. +There was no need to write any logic to generate numbers, track counters, or manage collisions. Appwrite's `$sequence` column handled the sequence internally, which was great for our use case. ## Conclusion -This tutorial introduced the auto-incrementing `$sequence` attribute and demonstrated how to use it in a small, working app. It offers a solution to a common need: preserving the order of inserts in a predictable, integer-based way. +This tutorial introduced the auto-incrementing `$sequence` column and demonstrated how to use it in a small, working app. It offers a solution to a common need: preserving the order of inserts in a predictable, integer-based way. Because the value is assigned by your database, `$sequence` remains stable even as your application grows. You can filter by it, paginate through it, or display it directly in user interfaces. And since it is read-only and immutable, we avoid the risk of errors that often come with custom counters or timestamp sorting. @@ -368,6 +368,6 @@ Looking forward to seeing what you build with this! ## Resources -- [Appwrite Database Documentation](/docs/products/databases) +- [Appwrite Databases documentation](/docs/products/databases) - [Pink Design System](https://pink.appwrite.io/) -- [Secure sensitive database fields with encrypted string attributes](https://appwrite.io/blog/post/encrypted-attributes-for-sensitive-fields) +- [Secure sensitive database fields with encrypted columns](https://appwrite.io/blog/post/encrypted-attributes-for-sensitive-fields) diff --git a/src/routes/blog/post/uber-clone-nextjs-appwrite/+page.markdoc b/src/routes/blog/post/uber-clone-nextjs-appwrite/+page.markdoc index 9ca613c7ff2..29bef3f7518 100644 --- a/src/routes/blog/post/uber-clone-nextjs-appwrite/+page.markdoc +++ b/src/routes/blog/post/uber-clone-nextjs-appwrite/+page.markdoc @@ -10,11 +10,11 @@ category: tutorial, product featured: false faqs: - question: "What are geo queries in Appwrite?" - answer: "Geo queries let you filter and sort documents based on geographic coordinates stored in a point column. Operators like distanceLessThan return documents within a radius of a reference point. They require a spatial index on the point column to perform well. See the [Appwrite Databases docs](/docs/products/databases) for the supported geo query operators." + answer: "Geo queries let you filter and sort rows based on geographic coordinates stored in a point column. Operators like distanceLessThan return rows within a radius of a reference point. They require a spatial index on the point column to perform well. See the [Appwrite Databases docs](/docs/products/databases) for the supported geo query operators." - question: "Do I need a spatial index for location based queries?" answer: "Yes, if you want them to be fast. Without a spatial index, Appwrite has to scan every row and compute distance, which does not scale past a few hundred rows. Adding a spatial index to your point column is the single most impactful change for geo query performance." - question: "How do realtime subscriptions work in this app?" - answer: "[Appwrite Realtime](/docs/products/databases) pushes events over websockets when documents are created, updated, or deleted. The rider subscribes to their own ride row and reacts when status changes or the driver's location updates, while the driver subscribes to pending rides in their area." + answer: "[Appwrite Realtime](/docs/products/databases) pushes events over websockets when rows are created, updated, or deleted. The rider subscribes to their own ride row and reacts when status changes or the driver's location updates, while the driver subscribes to pending rides in their area." - question: "Why use a one time password (OTP) for ride verification?" answer: "An OTP confirms that the driver who arrives is the one matched in the app, and that the rider getting in is the one who booked. It is a simple anti fraud measure used by most production ride hailing apps." - question: "Can I run this app on self hosted Appwrite?" diff --git a/src/routes/blog/post/understand-data-queries/+page.markdoc b/src/routes/blog/post/understand-data-queries/+page.markdoc index 0a9ff65d778..2ca3b5e0d8a 100644 --- a/src/routes/blog/post/understand-data-queries/+page.markdoc +++ b/src/routes/blog/post/understand-data-queries/+page.markdoc @@ -80,47 +80,47 @@ 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 Appwrite Databases 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. Appwrite supports 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([ +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([ +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/blog/post/using-nextjs-wrong/+page.markdoc b/src/routes/blog/post/using-nextjs-wrong/+page.markdoc index adccce504eb..e02151e9612 100644 --- a/src/routes/blog/post/using-nextjs-wrong/+page.markdoc +++ b/src/routes/blog/post/using-nextjs-wrong/+page.markdoc @@ -169,8 +169,10 @@ export default function LoginPage() { "use server"; const { account } = await createAdminClient(); const session = await account.createEmailPasswordSession( - formData.get("email") as string, - formData.get("password") as string + { + email: formData.get("email") as string, + password: formData.get("password") as string + } ); (await cookies()).set("session", session.secret, { diff --git a/src/routes/blog/post/vibe-coding-security-best-practices/+page.markdoc b/src/routes/blog/post/vibe-coding-security-best-practices/+page.markdoc index 352f1d3226d..4b96b5e92ea 100644 --- a/src/routes/blog/post/vibe-coding-security-best-practices/+page.markdoc +++ b/src/routes/blog/post/vibe-coding-security-best-practices/+page.markdoc @@ -84,7 +84,7 @@ app.get('/search', async (req, res) => { }) ``` -Using an **ORM** like Prisma, Sequelize, or TypeORM can further reduce risks by abstracting query handling. Alternatively, [Appwrite's Database API](https://appwrite.io/docs/references/1.6.x/client-web/databases) provides built-in query filtering, eliminating the need to construct raw SQL queries. +Using an **ORM** like Prisma, Sequelize, or TypeORM can further reduce risks by abstracting query handling. Alternatively, [Appwrite's TablesDB API](https://appwrite.io/docs/references/cloud/client-web/tablesDB) provides built-in query filtering, eliminating the need to construct raw SQL queries. Beyond SQL injection, user inputs should be validated for **length, type, and format**. Use libraries like **Zod (TypeScript)** or **Joi (JavaScript)** to enforce strict validation rules before processing any user data. Sanitization is equally important. Removing or escaping dangerous characters in user inputs prevents attacks like XSS and command injection. @@ -169,7 +169,7 @@ To prevent this, implement **role-based access control (RBAC)** to restrict acce Every function that modifies or deletes data should enforce strict authorization rules. Always verify user roles and enforce **least privilege access**, ensuring users only have permissions necessary for their role. -If using Appwrite, ensure that your database collections are [configured with the correct permissions](https://appwrite.io/docs/products/databases/permissions) and that you are using the correct roles to restrict access. +If using Appwrite, ensure that your database tables are [configured with the correct permissions](https://appwrite.io/docs/products/databases/permissions) and that you are using the correct roles to restrict access. # 7. Secure all communications with HTTPS @@ -438,4 +438,4 @@ By staying up to date, you can use AI safely while mitigating risks. Vibe coding can be a great advantage if done correctly, but AI-generated code **must never be blindly trusted**. Security remains a human responsibility, requiring carefulness, un-rushed review, and adherence to best practices to ensure applications are protected. Many software applications can seriously affect people's lives, and therefore, security should never be taken lightly. -By following these **security principles**, you can take proper advantage of vibe coding and ai-assisted development. \ No newline at end of file +By following these **security principles**, you can take proper advantage of vibe coding and ai-assisted development. diff --git a/src/routes/blog/post/when-custom-backend-stops-being-worth-it/+page.markdoc b/src/routes/blog/post/when-custom-backend-stops-being-worth-it/+page.markdoc index 6aa8c937b54..21193682521 100644 --- a/src/routes/blog/post/when-custom-backend-stops-being-worth-it/+page.markdoc +++ b/src/routes/blog/post/when-custom-backend-stops-being-worth-it/+page.markdoc @@ -60,7 +60,7 @@ The problem is that most small teams don't meet these criteria. They have a cust The fear of migrating away from a custom backend often outweighs the reality. Modern backend platforms support: - **Gradual migration.** You don't have to swap everything at once. Authentication can move first, then storage, then databases. Each service you migrate reduces your maintenance burden. -- **Data export and import.** Appwrite's databases are standard JSON/document stores. Exporting your existing data and importing it is straightforward for most data models. +- **Data export and import.** Appwrite's databases store standard JSON row data. Exporting your existing data and importing it is straightforward for most data models. - **API compatibility.** Most backend platforms provide REST and SDK-based APIs that your existing front-end can call with modest changes. # Appwrite as your managed backend diff --git a/src/routes/docs/advanced/platform/database-reads-and-writes/+page.markdoc b/src/routes/docs/advanced/platform/database-reads-and-writes/+page.markdoc index a01378c52fc..5b14dea24f0 100644 --- a/src/routes/docs/advanced/platform/database-reads-and-writes/+page.markdoc +++ b/src/routes/docs/advanced/platform/database-reads-and-writes/+page.markdoc @@ -7,7 +7,7 @@ description: Learn how Appwrite handles database reads and writes and their asso Updated pricing will take effect on April 10th, 2025. Check out this [blog post](/blog/post/announcing-database-reads-and-writes-pricing) for more information. {% /info %} -Appwrite provides powerful database capabilities through its [Database API](/docs/products/databases), allowing you to perform read and write operations across your application data. Understanding how these operations are counted and billed is essential for planning your application's scalability. +Appwrite provides powerful database capabilities through [TablesDB](/docs/products/databases), allowing you to perform read and write operations across your application data. Understanding how these operations are counted and billed is essential for planning your application's scalability. ## Database Operations diff --git a/src/routes/docs/advanced/platform/permissions/+page.markdoc b/src/routes/docs/advanced/platform/permissions/+page.markdoc index e0b2d1c12f1..3c85af6fe1b 100644 --- a/src/routes/docs/advanced/platform/permissions/+page.markdoc +++ b/src/routes/docs/advanced/platform/permissions/+page.markdoc @@ -68,7 +68,7 @@ The following examples are using the [Appwrite Web SDK](https://github.com/appwr In the following example, we are creating a row that can be read by anyone, edited by writers or admins, and deleted by administrators or a user with the user ID `user:5c1f88b42259e`. ```client-web -import { Client, TablesDB, Permission, Role } from "appwrite"; +import { Client, ID, TablesDB, Permission, Role } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') @@ -76,18 +76,19 @@ const client = new Client() const tablesDB = new TablesDB(client); -let promise = tablesDB.createRow( - '', - '', - {'actorName': 'Chris Evans', 'height': 183}, - [ +let promise = tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: ID.unique(), + data: {'actorName': 'Chris Evans', 'height': 183}, + permissions: [ Permission.read(Role.any()), // Anyone can view this row Permission.update(Role.team("writers")), // Writers can update this row Permission.update(Role.team("admin")), // Admins can update this row Permission.delete(Role.user("5c1f88b42259e")), // User 5c1f88b42259e can delete this row Permission.delete(Role.team("admin")) // Admins can delete this row ] -); +}); promise.then(function (response) { console.log(response); @@ -101,7 +102,7 @@ promise.then(function (response) { In the following example, we are creating a row that can be read by members of the team with ID `5c1f88b87435e` and can only be edited or deleted by members of the same team that possess the team role `owner`. ```client-web -import { Client, TablesDB, Permission, Role } from "appwrite"; +import { Client, ID, TablesDB, Permission, Role } from "appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') @@ -109,16 +110,17 @@ const client = new Client() const tablesDB = new TablesDB(client); -let promise = tablesDB.createRow( - '', - '', - {'actorName': 'Chris Evans', 'height': 183}, - [ +let promise = tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: ID.unique(), + data: {'actorName': 'Chris Evans', 'height': 183}, + permissions: [ Permission.read(Role.team("5c1f88b87435e")), // Only users of team 5c1f88b87435e can read the row Permission.update(Role.team("5c1f88b87435e", "owner")), // Only users of team 5c1f88b87435e with the role owner can update the row Permission.delete(Role.team("5c1f88b87435e", "owner")) // Only users of team 5c1f88b87435e with the role owner can delete the row ] -); +}); promise.then(function (response) { console.log(response); @@ -147,7 +149,7 @@ When creating rows in your application, set row-level permissions to restrict ac {% multicode %} ```client-web -import { Client, TablesDB, Permission, Role } from "appwrite"; +import { Client, ID, TablesDB, Permission, Role } from "appwrite"; const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') @@ -155,16 +157,17 @@ const client = new Client() const tablesDB = new TablesDB(client); -let promise = tablesDB.createRow( - '', - '', - { 'title': 'My Private Row' }, - [ +let promise = tablesDB.createRow({ + databaseId: '', + tableId: '', + rowId: ID.unique(), + data: { 'title': 'My Private Row' }, + permissions: [ Permission.read(Role.user('')), // Only this user can read Permission.update(Role.user('')), // Only this user can update Permission.delete(Role.user('')) // Only this user can delete ] -); +}); promise.then(function (response) { console.log(response); @@ -261,4 +264,4 @@ suspend fun main() { 2. When a row is created, we set permissions for only the creator 3. These row-level permissions ensure only the creator can read, update, or delete their rows 4. Other users can create their own rows but cannot access rows they didn't create -{% /info %} \ No newline at end of file +{% /info %} diff --git a/src/routes/docs/advanced/self-hosting/production/backups/+page.markdoc b/src/routes/docs/advanced/self-hosting/production/backups/+page.markdoc index 34237bc60ec..129426670bc 100644 --- a/src/routes/docs/advanced/self-hosting/production/backups/+page.markdoc +++ b/src/routes/docs/advanced/self-hosting/production/backups/+page.markdoc @@ -16,7 +16,7 @@ Self-hosted Appwrite requires manual backup procedures to protect your data. Your Appwrite installation has several components that need backing up: -1. **Database** - User data, documents, and configuration +1. **Database** - User data, rows, and configuration 2. **Storage volumes** - Uploaded files and function code 3. **Environment variables** - Configuration in `.env` 4. **System snapshots** - Complete server state (alternative approach) diff --git a/src/routes/docs/products/ai/tutorials/object-detection/+page.markdoc b/src/routes/docs/products/ai/tutorials/object-detection/+page.markdoc index 675e7d8c7fb..18f6f75ec93 100644 --- a/src/routes/docs/products/ai/tutorials/object-detection/+page.markdoc +++ b/src/routes/docs/products/ai/tutorials/object-detection/+page.markdoc @@ -169,8 +169,8 @@ Before we can save our detection result to the Appwrite Database, we need to cre Navigate to the Appwrite Console and click on **Database** in the left sidebar, then click on the **Create database** button, you can call this database anything you like, for this example we'll call it `AI`. Once you've created the database, click on the **Create table** button and create a new table, once again you can call it anything you want but for this example we'll call it `Image Labels`. -Add two string columns to our table, `image` and `labels`. The `image` column will store the ID of the image we're detecting objects in and the `labels` column will store the detection result. -Both of these columns should be `required` with `image` having the size of `256` and `labels` having the size of around `4096`. +Add two text columns to our table, `image` and `labels`. The `image` column will store the ID of the image we're detecting objects in and the `labels` column will store the detection result. +Both of these columns should be `required`. {% only_dark %} ![Object detection database](/images/docs/ai/tutorials/object-detection/dark/database.avif) @@ -187,9 +187,8 @@ With the image classified, we can now save the result to the Appwrite Database. To begin with we're going to add a new function to the `appwrite.js` file we created earlier which will create these records in the database. ```js -async createImageLabels(databaseId, tableId, imageId, labels) -{ - await this.tablesDB.createRow({ +async function createImageLabels(databaseId, tableId, imageId, labels) { + await tablesDB.createRow({ databaseId, tableId, rowId: ID.unique(), diff --git a/src/routes/docs/products/auth/custom-token/+page.markdoc b/src/routes/docs/products/auth/custom-token/+page.markdoc index 9ff8a435a88..f65623efbc2 100644 --- a/src/routes/docs/products/auth/custom-token/+page.markdoc +++ b/src/routes/docs/products/auth/custom-token/+page.markdoc @@ -18,7 +18,7 @@ import { Client, Users } from "node-appwrite"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID + .setProject('') // Your project ID .setKey(''); // Your project API key const users = new Users(client); @@ -58,7 +58,7 @@ client = Client() users = Users(client) -token = users.create_token('') +token = users.create_token(user_id='') secret = token.secret ``` @@ -74,7 +74,7 @@ client = Client.new users = Users.new(client) -token = users.create_token('') +token = users.create_token(user_id: '') secret = token['secret'] ``` @@ -264,4 +264,4 @@ mutation { {% /multicode %} -When the session is successfully created, the session is stored in a persistent manner and you can now do requests as authorized user from the application. \ No newline at end of file +When the session is successfully created, the session is stored in a persistent manner and you can now do requests as authorized user from the application. diff --git a/src/routes/docs/products/auth/labels/+page.markdoc b/src/routes/docs/products/auth/labels/+page.markdoc index 1a209b272a1..8dcda6e3ff5 100644 --- a/src/routes/docs/products/auth/labels/+page.markdoc +++ b/src/routes/docs/products/auth/labels/+page.markdoc @@ -17,10 +17,10 @@ const client = new sdk.Client() const users = new sdk.Users(client); -const promise = users.updateLabels( - '', - [ 'subscriber' ] -); +const promise = users.updateLabels({ + userId: '', + labels: [ 'subscriber' ] +}); promise.then(function (response) { console.log(response); // Success @@ -65,8 +65,8 @@ client = Client() users = Users(client) result = users.update_labels( - '', - [ 'subscriber' ] + user_id='', + labels=[ 'subscriber' ] ); ``` ```ruby @@ -97,10 +97,10 @@ let client = new sdk.Client() let users = new sdk.Users(client); -const promise = users.updateLabels( - '', - [ 'subscriber' ] -); +const promise = users.updateLabels({ + userId: '', + labels: [ 'subscriber' ] +}); promise.then(function (response) { console.log(response); // Success @@ -208,4 +208,4 @@ This would correspond with the permissions below. {% arrow_link href="/docs/advanced/platform/permissions" %} Learn more about permissions -{% /arrow_link %} \ No newline at end of file +{% /arrow_link %} diff --git a/src/routes/docs/products/auth/magic-url/+page.markdoc b/src/routes/docs/products/auth/magic-url/+page.markdoc index f7ff3cceade..8759132e5bf 100644 --- a/src/routes/docs/products/auth/magic-url/+page.markdoc +++ b/src/routes/docs/products/auth/magic-url/+page.markdoc @@ -21,9 +21,11 @@ const client = new Client() const account = new Account(client); const token = await account.createMagicURLToken( - ID.unique(), - 'email@example.com', - 'https://example.com/verify' + { + userId: ID.unique(), + email: 'email@example.com', + url: 'https://example.com/verify' +} ); ``` diff --git a/src/routes/docs/products/auth/multi-tenancy/+page.markdoc b/src/routes/docs/products/auth/multi-tenancy/+page.markdoc index 9b8957a2939..b6bd1759f80 100644 --- a/src/routes/docs/products/auth/multi-tenancy/+page.markdoc +++ b/src/routes/docs/products/auth/multi-tenancy/+page.markdoc @@ -42,9 +42,11 @@ const teams = new Teams(client); // Create team for a new tenant const tenantTeam = await teams.create( - 'example_corp', // Team ID for tenant - 'Example Corp', // Tenant name - ['owner', 'admin', 'member'] // Tenant roles + { + teamId: 'example_corp', + name: 'Example Corp', + roles: ['owner', 'admin', 'member'] +} // Tenant roles ); ``` ```client-flutter @@ -115,12 +117,12 @@ const teams = new Teams(client); // Invite a member to the tenant const membership = await teams.createMembership( - 'example_corp', // Team/tenant ID - ['admin'], // Member's role in the tenant - 'user@example.com', // User's email - undefined, // userId (optional) - undefined, // phone (optional) - 'https://example.com/accept-invite' // Redirect URL after accepting + { + teamId: 'example_corp', + roles: ['admin'], + email: 'user@example.com', + url: 'https://example.com/accept-invite' +} // Redirect URL after accepting ); ``` ```client-flutter @@ -192,25 +194,25 @@ const client = new Client() const tablesDB = new TablesDB(client); // Create a row that only members of "Example Corp" tenant can access -const row = await tablesDB.createRow( - 'invoices_db', - 'invoices', - ID.unique(), - { +const row = await tablesDB.createRow({ + databaseId: 'invoices_db', + tableId: 'invoices', + rowId: ID.unique(), + data: { title: 'Q2 Invoice', amount: 2500.00, customer: 'Example Customer', status: 'pending', tenant_id: 'example_corp' }, - [ + permissions: [ // All Example Corp team members can read Permission.read(Role.team('example_corp')), // Only admins can update Permission.write(Role.team('example_corp', ['admin'])) ] -); +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -323,19 +325,19 @@ const client = new Client() const tablesDB = new TablesDB(client); // Current user will only see invoices they have access to -const rows = await tablesDB.listRows( - 'invoices_db', - 'invoices' -); +const rows = await tablesDB.listRows({ + databaseId: 'invoices_db', + tableId: 'invoices' +}); // For specific tenant data, you can add a query filter -const tenantDocuments = await tablesDB.listRows( - 'invoices_db', - 'invoices', - [ +const tenantRows = await tablesDB.listRows({ + databaseId: 'invoices_db', + tableId: 'invoices', + queries: [ Query.equal('tenant_id', 'example_corp') ] -); +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; diff --git a/src/routes/docs/products/auth/preferences/+page.markdoc b/src/routes/docs/products/auth/preferences/+page.markdoc index dd48d27e73b..9a797271d66 100644 --- a/src/routes/docs/products/auth/preferences/+page.markdoc +++ b/src/routes/docs/products/auth/preferences/+page.markdoc @@ -188,12 +188,14 @@ const client = new Client() const teams = new Teams(client); const promise = teams.updatePrefs( - '', { + teamId: '', + prefs: { theme: 'corporate', notificationsEnabled: true, defaultView: 'kanban' } +} ); promise.then(function (response) { @@ -273,7 +275,9 @@ const client = new Client() const teams = new Teams(client); -const promise = teams.getPrefs(''); +const promise = teams.getPrefs({ + teamId: '' +}); promise.then(function (prefs) { console.log(prefs); // Team preferences diff --git a/src/routes/docs/products/auth/server-side-rendering/+page.markdoc b/src/routes/docs/products/auth/server-side-rendering/+page.markdoc index a77e9bec0e7..484a87b6b5c 100644 --- a/src/routes/docs/products/auth/server-side-rendering/+page.markdoc +++ b/src/routes/docs/products/auth/server-side-rendering/+page.markdoc @@ -247,7 +247,7 @@ def login(): account = Account(admin_client) # Create the session using the Appwrite client - session = account.create_email_password_session(email, password) + session = account.create_email_password_session(email=email, password=password) resp = make_response(jsonify({'success': True})) # Set the session cookie @@ -592,9 +592,9 @@ def oauth(): account = Account(admin_client) redirect_url = account.create_o_auth2_token( - OAuthProvider.Github, # Provider - 'https://example.com/oauth/success', # Success URL - 'https://example.com/oauth/failure', # Failure URL + provider=OAuthProvider.Github, + success='https://example.com/oauth/success', + failure='https://example.com/oauth/failure', ) return redirect(redirect_url) @@ -644,7 +644,7 @@ app.get('/oauth/success', async (req, res) => { httpOnly: true, secure: true, sameSite: 'strict', - maxAge: sesion.expire + maxAge: session.expire, path: '/', }); @@ -699,7 +699,7 @@ def oauth_success(): try: # Create the session using the Appwrite client - session = account.create_session(user_id, secret) + session = account.create_session(user_id=user_id, secret=secret) # Set the session cookie res = make_response(jsonify({'success': True})) diff --git a/src/routes/docs/products/auth/team-invites/+page.markdoc b/src/routes/docs/products/auth/team-invites/+page.markdoc index c16614d1f34..69da06bf99a 100644 --- a/src/routes/docs/products/auth/team-invites/+page.markdoc +++ b/src/routes/docs/products/auth/team-invites/+page.markdoc @@ -28,14 +28,12 @@ const client = new Client() const teams = new Teams(client); // Create membership with email invite -const membership = await teams.createMembership( - '', - ['developer'], // roles - 'user@example.com', // email - undefined, // userId (optional) - undefined, // phone (optional) - 'https://yourapp.com/accept-invite' // url - redirect after email click -); +const membership = await teams.createMembership({ + teamId: '', + roles: ['developer'], + email: 'user@example.com', + url: 'https://yourapp.com/accept-invite' +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -110,12 +108,12 @@ const client = new Client() const teams = new Teams(client); // Accept the invitation using the membership ID and secret -const response = await teams.updateMembershipStatus( - '', - '', - '', - '' -); +const response = await teams.updateMembershipStatus({ + teamId: '', + membershipId: '', + userId: '', + secret: '' +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -193,12 +191,12 @@ const client = new sdk.Client() const teams = new sdk.Teams(client); // Create membership directly with userId -const membership = await teams.createMembership( - '', - ['developer'], - '', - 'John Doe' // optional name -); +const membership = await teams.createMembership({ + teamId: '', + roles: ['developer'], + userId: '', + name: 'John Doe' // optional +}); ``` ```server-python from appwrite.client import Client @@ -457,7 +455,7 @@ teams_list = teams.list() // Iterate through teams to find memberships for team in teams_list['teams']: - response = teams.list_memberships(team['$id']) + response = teams.list_memberships(team_id=team['$id']) // Find membership for specific user user_membership = next( @@ -583,10 +581,10 @@ const client = new Client() const teams = new Teams(client); -await teams.deleteMembership( - '', - '' -); +await teams.deleteMembership({ + teamId: '', + membershipId: '' +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -652,11 +650,11 @@ const client = new Client() const teams = new Teams(client) // Update member roles -await teams.updateMembership( - '', - '', - ['admin', 'developer'] -) +await teams.updateMembership({ + teamId: '', + membershipId: '', + roles: ['admin', 'developer'] +}) ``` ```client-flutter import 'package:appwrite/appwrite.dart' @@ -796,4 +794,4 @@ See how to grant row and file access to team roles in the [permissions](/docs/ad {% arrow_link href="/docs/products/auth/teams" %} Learn more about team management -{% /arrow_link %} \ No newline at end of file +{% /arrow_link %} diff --git a/src/routes/docs/products/auth/teams/+page.markdoc b/src/routes/docs/products/auth/teams/+page.markdoc index 7df0d562003..9e8f0567d95 100644 --- a/src/routes/docs/products/auth/teams/+page.markdoc +++ b/src/routes/docs/products/auth/teams/+page.markdoc @@ -33,9 +33,11 @@ client ; const promise = teams.create( - 'teachers', - 'Teachers', - ['maths', 'sciences', 'arts', 'literature'] + { + teamId: 'teachers', + name: 'Teachers', + roles: ['maths', 'sciences', 'arts', 'literature'] +} ); promise.then(function (response) { @@ -121,9 +123,11 @@ client ; const promise = teams.createMembership( - 'teachers', - ["maths"], - "david@example.com" + { + teamId: 'teachers', + roles: ["maths"], + email: "david@example.com" +} ); promise.then(function (response) { diff --git a/src/routes/docs/products/avatars/qr-codes/+page.markdoc b/src/routes/docs/products/avatars/qr-codes/+page.markdoc index 39963a84d88..fc84d90f467 100644 --- a/src/routes/docs/products/avatars/qr-codes/+page.markdoc +++ b/src/routes/docs/products/avatars/qr-codes/+page.markdoc @@ -121,7 +121,9 @@ QR codes are commonly used for two-factor authentication (2FA). When setting up {% multicode %} ```client-web // After creating an MFA authenticator -const authenticator = await account.createMfaAuthenticator('totp'); +const authenticator = await account.createMfaAuthenticator({ + type: 'totp' +}); const qrCode = avatars.getQR({ text: authenticator.uri, size: 400, diff --git a/src/routes/docs/products/databases/+layout.svelte b/src/routes/docs/products/databases/+layout.svelte index 87f1ffb5a32..3463bb88af2 100644 --- a/src/routes/docs/products/databases/+layout.svelte +++ b/src/routes/docs/products/databases/+layout.svelte @@ -163,7 +163,7 @@ if (shouldShowSubtitle) { headerSectionInfoAlert.set({ title: 'New API', - description: `This is a relatively new API. For details on the previous version and its terminology, see the legacy Collections API documentation.` + description: `This is a relatively new API. For details on the previous version and its terminology, see the legacy API documentation.` }); } else { headerSectionInfoAlert.set(null); diff --git a/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc b/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc index 9de1237ed7e..b2b439e226b 100644 --- a/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc +++ b/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc @@ -513,24 +513,24 @@ For complex updates that include both atomic operations and regular field change {% multicode %} ```client-web // First, increment the likes atomically -const likeResult = await tablesDB.incrementRowColumn( - '', - '', - '', - 'likes', // column - 1 // value -); +const likeResult = await tablesDB.incrementRowColumn({ + databaseId: '', + tableId: '', + rowId: '', + column: 'likes', + value: 1 +}); // Then, update other fields -const updateResult = await tablesDB.updateRow( - '', - '', - '', - { +const updateResult = await tablesDB.updateRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { lastLikedBy: userId, lastLikedAt: new Date().toISOString() } -); +}); ``` ```client-flutter // First, increment the likes atomically @@ -597,24 +597,24 @@ val updateResult = tablesDB.updateRow( ``` ```server-nodejs // First, increment the likes atomically -const likeResult = await tablesDB.incrementRowColumn( - '', - '', - '', - 'likes', // column - 1 // value -); +const likeResult = await tablesDB.incrementRowColumn({ + databaseId: '', + tableId: '', + rowId: '', + column: 'likes', + value: 1 +}); // Then, update other fields -const updateResult = await tablesDB.updateRow( - '', - '', - '', - { +const updateResult = await tablesDB.updateRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { lastLikedBy: userId, lastLikedAt: new Date().toISOString() } -); +}); ``` ```server-python # First, increment the likes atomically diff --git a/src/routes/docs/products/databases/bulk-operations/+page.markdoc b/src/routes/docs/products/databases/bulk-operations/+page.markdoc index 07a93db88c0..d1574894303 100644 --- a/src/routes/docs/products/databases/bulk-operations/+page.markdoc +++ b/src/routes/docs/products/databases/bulk-operations/+page.markdoc @@ -56,10 +56,10 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.createRows( - '', - '', - [ +const result = await tablesDB.createRows({ + databaseId: '', + tableId: '', + rows: [ { $id: sdk.ID.unique(), name: 'Row 1' @@ -69,7 +69,7 @@ const result = await tablesDB.createRows( name: 'Row 2' } ] -); +}); ``` ```server-python @@ -155,16 +155,16 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.updateRows( - '', - '', - { +const result = await tablesDB.updateRows({ + databaseId: '', + tableId: '', + data: { status: 'published' }, - [ + queries: [ sdk.Query.equal('status', 'draft') ] -); +}); ``` ```server-python @@ -242,10 +242,10 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.upsertRows( - '', - '', - [ +const result = await tablesDB.upsertRows({ + databaseId: '', + tableId: '', + rows: [ { $id: sdk.ID.unique(), name: 'New Row 1' @@ -255,7 +255,7 @@ const result = await tablesDB.upsertRows( name: 'New Row 2' } ] -); +}); ``` ```server-python @@ -342,13 +342,13 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.deleteRows( - '', - '', - [ +const result = await tablesDB.deleteRows({ + databaseId: '', + tableId: '', + queries: [ sdk.Query.equal('status', 'archived') ] -); +}); ``` ```server-python diff --git a/src/routes/docs/products/databases/csv-imports/+page.markdoc b/src/routes/docs/products/databases/csv-imports/+page.markdoc index 59d3244c23a..4efa897d60f 100644 --- a/src/routes/docs/products/databases/csv-imports/+page.markdoc +++ b/src/routes/docs/products/databases/csv-imports/+page.markdoc @@ -145,4 +145,4 @@ CSV imports run as background tasks. The Console displays a floating progress ba - [Appwrite CLI](/docs/command-line) - [Database Permissions](/docs/products/databases/permissions) -- [Database API Reference](/docs/references/cloud/client-web/databases) +- [TablesDB API Reference](/docs/references/cloud/client-web/tablesDB) diff --git a/src/routes/docs/products/databases/databases/+page.markdoc b/src/routes/docs/products/databases/databases/+page.markdoc index cc3147838b7..9468cefac01 100644 --- a/src/routes/docs/products/databases/databases/+page.markdoc +++ b/src/routes/docs/products/databases/databases/+page.markdoc @@ -100,7 +100,7 @@ client = Client() tablesDB = TablesDB(client) -result = tablesDB.create('', '') +result = tablesDB.create(database_id='', name='') ``` ```ruby require 'Appwrite' diff --git a/src/routes/docs/products/databases/operators/+page.markdoc b/src/routes/docs/products/databases/operators/+page.markdoc index 53ebfaa1ed7..f50f0180c41 100644 --- a/src/routes/docs/products/databases/operators/+page.markdoc +++ b/src/routes/docs/products/databases/operators/+page.markdoc @@ -204,10 +204,10 @@ client = Client() tablesDB = TablesDB(client) result = tablesDB.update_row( - '', - '', - '', - { 'letters': Operator.arrayAppend(['c']) } + database_id='', + table_id='', + row_id='', + data={ 'letters': Operator.arrayAppend(['c']) } ) ``` ```server-dotnet @@ -246,10 +246,10 @@ client tablesDB = TablesDB.new(client) result = tablesDB.update_row( - '', - '', - '', - { 'letters' => Operator.arrayAppend(['c']) } + database_id: '', + table_id: '', + row_id: '', + data: { 'letters' => Operator.arrayAppend(['c']) } ) ``` ```server-java @@ -1331,10 +1331,10 @@ client = Client() tablesDB = TablesDB(client) result = tablesDB.update_row( - '', - '', - '', - { 'upvotes': Operator.increment(1) } + database_id='', + table_id='', + row_id='', + data={ 'upvotes': Operator.increment(1) } ) ``` ```server-dotnet @@ -1373,10 +1373,10 @@ client tablesDB = TablesDB.new(client) result = tablesDB.update_row( - '', - '', - '', - { 'upvotes' => Operator.increment(1) } + database_id: '', + table_id: '', + row_id: '', + data: { 'upvotes' => Operator.increment(1) } ) ``` ```server-java @@ -1618,10 +1618,10 @@ client = Client() tablesDB = TablesDB(client) result = tablesDB.update_row( - '', - '', - '', - { 'books': Operator.arrayAppend(['The Great Gatsby']) } + database_id='', + table_id='', + row_id='', + data={ 'books': Operator.arrayAppend(['The Great Gatsby']) } ) ``` ```server-dotnet @@ -1660,10 +1660,10 @@ client tablesDB = TablesDB.new(client) result = tablesDB.update_row( - '', - '', - '', - { 'books' => Operator.arrayAppend(['The Great Gatsby']) } + database_id: '', + table_id: '', + row_id: '', + data: { 'books' => Operator.arrayAppend(['The Great Gatsby']) } ) ``` ```server-java @@ -1905,10 +1905,10 @@ client = Client() tablesDB = TablesDB(client) result = tablesDB.update_row( - '', - '', - '', - { 'scheduledDeletion': Operator.dateAddDays(30) } + database_id='', + table_id='', + row_id='', + data={ 'scheduledDeletion': Operator.dateAddDays(30) } ) ``` ```server-dotnet @@ -1947,10 +1947,10 @@ client tablesDB = TablesDB.new(client) result = tablesDB.update_row( - '', - '', - '', - { 'scheduledDeletion' => Operator.dateAddDays(30) } + database_id: '', + table_id: '', + row_id: '', + data: { 'scheduledDeletion' => Operator.dateAddDays(30) } ) ``` ```server-java @@ -2042,7 +2042,10 @@ await tablesDB.updateRow({ }); // Commit the transaction -await tablesDB.updateTransaction(tx.$id, 'commit'); +await tablesDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); ``` ```server-nodejs const sdk = require('node-appwrite'); @@ -2071,7 +2074,10 @@ await tablesDB.updateRow({ }); // Commit the transaction -await tablesDB.updateTransaction(tx.$id, 'commit'); +await tablesDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -2270,18 +2276,18 @@ tx = tablesDB.create_transaction() # Update row with operators inside the transaction result = tablesDB.update_row( - '', - '', - '', - { - 'upvotes': Operator.increment(1), - 'lastModified': Operator.dateSetNow() - }, - transaction_id=tx.id + database_id='', + table_id='', + row_id='', + data={ + 'upvotes': Operator.increment(1), + 'lastModified': Operator.dateSetNow() + }, + transaction_id=tx.id ) # Commit the transaction -tablesDB.update_transaction(tx.id, 'commit') +tablesDB.update_transaction(transaction_id=tx.id, commit=True) ``` ```server-dotnet using Appwrite; @@ -2332,18 +2338,18 @@ tx = tablesDB.create_transaction # Update row with operators inside the transaction result = tablesDB.update_row( - '', - '', - '', - { - 'upvotes' => Operator.increment(1), - 'lastModified' => Operator.dateSetNow() - }, - transaction_id: tx['$id'] + database_id: '', + table_id: '', + row_id: '', + data: { + 'upvotes' => Operator.increment(1), + 'lastModified' => Operator.dateSetNow() + }, + transaction_id: tx['$id'] ) # Commit the transaction -tablesDB.update_transaction(tx['$id'], 'commit') +tablesDB.update_transaction(transaction_id: tx['$id'], commit: true) ``` ```server-java import io.appwrite.Client; @@ -2481,7 +2487,10 @@ await tablesDB.createOperations({ }); // Commit the transaction -await tablesDB.updateTransaction(tx.$id, 'commit'); +await tablesDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); ``` ```server-nodejs const sdk = require('node-appwrite'); @@ -2524,7 +2533,10 @@ await tablesDB.createOperations({ }); // Commit the transaction -await tablesDB.updateTransaction(tx.$id, 'commit'); +await tablesDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -2818,7 +2830,7 @@ tablesDB.create_operations( ) # Commit the transaction -tablesDB.update_transaction(tx.id, 'commit') +tablesDB.update_transaction(transaction_id=tx.id, commit=True) ``` ```server-dotnet using Appwrite; @@ -2914,7 +2926,7 @@ tablesDB.create_operations( ) # Commit the transaction -tablesDB.update_transaction(tx['$id'], 'commit') +tablesDB.update_transaction(transaction_id: tx['$id'], commit: true) ``` ```server-java import io.appwrite.Client; @@ -3035,4 +3047,4 @@ async fn main() -> Result<(), Box> { Ok(()) } ``` -{% /multicode %} \ No newline at end of file +{% /multicode %} diff --git a/src/routes/docs/products/databases/pagination/+page.markdoc b/src/routes/docs/products/databases/pagination/+page.markdoc index a138a220146..089bac7e3fc 100644 --- a/src/routes/docs/products/databases/pagination/+page.markdoc +++ b/src/routes/docs/products/databases/pagination/+page.markdoc @@ -30,24 +30,24 @@ const client = new Client() const tablesDB = new TablesDB(client); // Page 1 -const page1 = await tablesDB.listRows( - '', - '', - [ +const page1 = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ Query.limit(25), Query.offset(0) ] -); +}); // Page 2 -const page2 = await tablesDB.listRows( - '', - '', - [ +const page2 = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ Query.limit(25), Query.offset(25) ] -); +}); ``` ```client-flutter import 'package:appwrite/appwrite.dart'; @@ -166,25 +166,25 @@ const client = new Client() const tablesDB = new TablesDB(client); // Page 1 -const page1 = await tablesDB.listRows( - '', - '', - [ +const page1 = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ Query.limit(25), ] -); +}); const lastId = page1.rows[page1.rows.length - 1].$id; // Page 2 -const page2 = await tablesDB.listRows( - '', - '', - [ +const page2 = await tablesDB.listRows({ + databaseId: '', + tableId: '', + queries: [ Query.limit(25), Query.cursorAfter(lastId), ] -); +}); ``` ```client-flutter diff --git a/src/routes/docs/products/databases/queries/+page.markdoc b/src/routes/docs/products/databases/queries/+page.markdoc index 48628e4df81..7b501d7b0fd 100644 --- a/src/routes/docs/products/databases/queries/+page.markdoc +++ b/src/routes/docs/products/databases/queries/+page.markdoc @@ -256,9 +256,9 @@ client = Client() tablesDB = TablesDB(client) result = tablesDB.list_rows( - '', - '', - [ + database_id='', + table_id='', + queries=[ Query.equal('title', ['Avatar', 'Lord of the Rings']), Query.greater_than('year', 1999) ] @@ -337,7 +337,7 @@ By default, rows return only their own fields: {% multicode %} ```client-web -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -345,7 +345,7 @@ const doc = await tablesDB.getRow({ }); ``` ```client-flutter -final doc = await tablesDB.getRow( +final row = await tablesDB.getRow( databaseId: '', tableId: '', rowId: '', @@ -353,19 +353,23 @@ final doc = await tablesDB.getRow( ); ``` ```server-python -doc = tablesDB.get_row( - '', '', '', - [Query.select(["name", "age"])] +row = tablesDB.get_row( + database_id='', + table_id='', + row_id='', + queries=[Query.select(["name", "age"])] ) ``` ```server-ruby -doc = tablesDB.get_row( - '', '', '', - [Query.select(["name", "age"])] +row = tablesDB.get_row( + database_id: '', + table_id: '', + row_id: '', + queries: [Query.select(["name", "age"])] ) ``` ```server-nodejs -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -373,13 +377,13 @@ const doc = await tablesDB.getRow({ }); ``` ```server-php -$doc = $tablesDB->getRow( +$row = $tablesDB->getRow( '', '', '', [Query::select(["name", "age"])] ); ``` ```server-rust -let doc = tables_db.get_row( +let row = tables_db.get_row( "", "", "", @@ -388,7 +392,7 @@ let doc = tables_db.get_row( ).await?; ``` ```client-apple -let doc = try await tablesDB.getRow( +let row = try await tablesDB.getRow( databaseId: "", tableId: "", rowId: "", @@ -407,7 +411,7 @@ Use the `*` wildcard to load all fields from related rows: {% multicode %} ```client-web -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -415,7 +419,7 @@ const doc = await tablesDB.getRow({ }); ``` ```client-flutter -final doc = await tablesDB.getRow( +final row = await tablesDB.getRow( databaseId: '', tableId: '', rowId: '', @@ -423,19 +427,23 @@ final doc = await tablesDB.getRow( ); ``` ```server-python -doc = tablesDB.get_row( - '', '', '', - [Query.select(["*", "reviews.*"])] +row = tablesDB.get_row( + database_id='', + table_id='', + row_id='', + queries=[Query.select(["*", "reviews.*"])] ) ``` ```server-ruby -doc = tablesDB.get_row( - '', '', '', - [Query.select(["*", "reviews.*"])] +row = tablesDB.get_row( + database_id: '', + table_id: '', + row_id: '', + queries: [Query.select(["*", "reviews.*"])] ) ``` ```server-nodejs -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -443,13 +451,13 @@ const doc = await tablesDB.getRow({ }); ``` ```server-php -$doc = $tablesDB->getRow( +$row = $tablesDB->getRow( '', '', '', [Query::select(["*", "reviews.*"])] ); ``` ```server-rust -let doc = tables_db.get_row( +let row = tables_db.get_row( "", "", "", @@ -458,7 +466,7 @@ let doc = tables_db.get_row( ).await?; ``` ```client-apple -let doc = try await tablesDB.getRow( +let row = try await tablesDB.getRow( databaseId: "", tableId: "", rowId: "", @@ -478,7 +486,7 @@ For precise control, select only specific fields from related rows: {% multicode %} ```client-web -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -486,7 +494,7 @@ const doc = await tablesDB.getRow({ }); ``` ```client-flutter -final doc = await tablesDB.getRow( +final row = await tablesDB.getRow( databaseId: '', tableId: '', rowId: '', @@ -494,19 +502,23 @@ final doc = await tablesDB.getRow( ); ``` ```server-python -doc = tablesDB.get_row( - '', '', '', - [Query.select(["name", "age", "reviews.author", "reviews.rating"])] +row = tablesDB.get_row( + database_id='', + table_id='', + row_id='', + queries=[Query.select(["name", "age", "reviews.author", "reviews.rating"])] ) ``` ```server-ruby -doc = tablesDB.get_row( - '', '', '', - [Query.select(["name", "age", "reviews.author", "reviews.rating"])] +row = tablesDB.get_row( + database_id: '', + table_id: '', + row_id: '', + queries: [Query.select(["name", "age", "reviews.author", "reviews.rating"])] ) ``` ```server-nodejs -const doc = await tablesDB.getRow({ +const row = await tablesDB.getRow({ databaseId: '', tableId: '', rowId: '', @@ -515,13 +527,13 @@ const doc = await tablesDB.getRow({ // Result: { name: "John", age: 30, reviews: [{ author: "...", rating: 5 }] } ``` ```server-php -$doc = $tablesDB->getRow( +$row = $tablesDB->getRow( '', '', '', [Query::select(["name", "age", "reviews.author", "reviews.rating"])] ); ``` ```server-rust -let doc = tables_db.get_row( +let row = tables_db.get_row( "", "", "", @@ -530,7 +542,7 @@ let doc = tables_db.get_row( ).await?; ``` ```client-apple -let doc = try await tablesDB.getRow( +let row = try await tablesDB.getRow( databaseId: "", tableId: "", rowId: "", @@ -985,11 +997,11 @@ Query::is_not_null("name").to_string() ``` {% /multicode %} -## String operations {% #string-operations %} +## Text operations {% #text-operations %} ### Starts with {% #starts-with %} -Returns rows if a string column starts with a substring. +Returns rows if a text column starts with a substring. {% multicode %} ```client-web @@ -1026,7 +1038,7 @@ Query::starts_with("name", "Once upon a time").to_string() ### Not starts with {% #not-starts-with %} -Returns rows if a string column does not start with a substring. +Returns rows if a text column does not start with a substring. {% multicode %} ```client-web @@ -1072,7 +1084,7 @@ Query::not_starts_with("name", "Once upon a time").to_string() ### Ends with {% #ends-with %} -Returns rows if a string column ends with a substring. +Returns rows if a text column ends with a substring. {% multicode %} ```client-web @@ -1109,7 +1121,7 @@ Query::ends_with("name", "happily ever after.").to_string() ### Not ends with {% #not-ends-with %} -Returns rows if a string column does not end with a substring. +Returns rows if a text column does not end with a substring. {% multicode %} ```client-web @@ -1155,62 +1167,62 @@ Query::not_ends_with("name", "happily ever after.").to_string() ### Contains {% #contains %} -Returns rows if the array column contains the specified elements or if a string column contains the specified substring. Also supported for spatial types. +Returns rows if the array column contains the specified elements or if a text column contains the specified substring. Also supported for spatial types. {% multicode %} ```client-web // For arrays Query.contains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.contains("name", "Tom") ``` ```client-flutter // For arrays Query.contains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.contains("name", "Tom") ``` ```server-python # For arrays Query.contains("ingredients", ['apple', 'banana']) -# For strings +# For text columns Query.contains("name", "Tom") ``` ```server-ruby # For arrays Query.contains("ingredients", ['apple', 'banana']) -# For strings +# For text columns Query.contains("name", "Tom") ``` ```server-deno // For arrays Query.contains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.contains("name", "Tom") ``` ```server-php // For arrays Query::contains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query::contains("name", "Tom") ``` ```client-apple // For arrays Query.contains("ingredients", value: ["apple", "banana"]) -// For strings +// For text columns Query.contains("name", value: "Tom") ```server-go // For arrays query.Contains("ingredients", []string{"apple", "banana"}) -// For strings +// For text columns query.Contains("name", "Tom") ``` ```server-rust @@ -1220,14 +1232,14 @@ Query::contains("ingredients", Value::Array(vec![ Value::String("banana".to_string()), ])).to_string() -// For strings +// For text columns Query::contains("name", "Tom").to_string() ``` ```http # For arrays {"method":"contains","column":"ingredients","values":["apple","banana"]} -# For strings +# For text columns {"method":"contains","column":"name","values":["Tom"]} ``` {% /multicode %} @@ -1235,7 +1247,7 @@ Query::contains("name", "Tom").to_string() ### Not contains {% #not-contains %} Returns rows if the array column does not contain the specified -elements, or if a string column does not contain the specified +elements, or if a text column does not contain the specified substring. Also supported for spatial types. {% multicode %} @@ -1243,111 +1255,111 @@ substring. Also supported for spatial types. // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```client-flutter // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```client-react-native // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```client-apple // For arrays Query.notContains("ingredients", value: ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", value: "Tom") ``` ```client-android-kotlin // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```client-android-java // For arrays Query.notContains("ingredients", Arrays.asList("apple", "banana")) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```server-python # For arrays Query.not_contains("ingredients", ['apple', 'banana']) -# For strings +# For text columns Query.not_contains("name", "Tom") ``` ```server-ruby # For arrays Query.not_contains("ingredients", ['apple', 'banana']) -# For strings +# For text columns Query.not_contains("name", "Tom") ``` ```server-deno // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```server-nodejs // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```server-php // For arrays Query::notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query::notContains("name", "Tom") ``` ```server-dotnet // For arrays Query.NotContains("ingredients", new List { "apple", "banana" }) -// For strings +// For text columns Query.NotContains("name", "Tom") ``` ```server-go // For arrays query.NotContains("ingredients", []string{"apple", "banana"}) -// For strings +// For text columns query.NotContains("name", "Tom") ```server-dart // For arrays Query.notContains("ingredients", ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```server-swift // For arrays Query.notContains("ingredients", value: ['apple', 'banana']) -// For strings +// For text columns Query.notContains("name", value: "Tom") ``` ```server-kotlin // For arrays Query.notContains("ingredients", listOf("apple", "banana")) -// For strings +// For text columns Query.notContains("name", "Tom") ``` ```server-rust @@ -1357,24 +1369,24 @@ Query::not_contains("ingredients", Value::Array(vec![ Value::String("banana".to_string()), ])).to_string() -// For strings +// For text columns Query::not_contains("name", "Tom").to_string() ``` ```http # For arrays {"method":"notContains","column":"ingredients","values":["apple","banana"]} -# For strings +# For text columns {"method":"notContains","column":"name","values":["Tom"]} ``` {% /multicode %} ### Search {% #search %} -Searches string columns for provided keywords. Requires a [full-text index](/docs/products/databases/tables#indexes) on queried columns. The search string must be at least **3 characters** to perform a search. +Searches text columns for provided keywords. Requires a [full-text index](/docs/products/databases/tables#indexes) on queried columns. The search string must be at least **3 characters** to perform a search. {% info title="Searching values with hyphens" %} -The hyphen (`-`) is treated as a stop character by the underlying search engine. To search for exact values that contain hyphens (for example, ticket or SKU codes like `SWT-2621-44`), wrap the value in quotes inside the search string: `Query.search(attribute, '"SWT-2621-44"')`. +The hyphen (`-`) is treated as a stop character by the underlying search engine. To search for exact values that contain hyphens (for example, ticket or SKU codes like `SWT-2621-44`), wrap the value in quotes inside the search string: `Query.search(column, '"SWT-2621-44"')`. {% /info %} {% multicode %} @@ -1412,12 +1424,12 @@ Query::search("text", "key words").to_string() ### Not search {% #not-search %} -Returns rows if a string column does not match the full-text search +Returns rows if a text column does not match the full-text search query. Requires a [full-text index](/docs/products/databases/tables#indexes) on queried columns. The search string must be at least **3 characters** to perform a search. {% info title="Searching values with hyphens" %} -The hyphen (`-`) is treated as a stop character by the underlying search engine. To exclude exact values that contain hyphens (for example, ticket or SKU codes like `SWT-2621-44`), wrap the value in quotes inside the search string: `Query.notSearch(attribute, '"SWT-2621-44"')`. +The hyphen (`-`) is treated as a stop character by the underlying search engine. To exclude exact values that contain hyphens (for example, ticket or SKU codes like `SWT-2621-44`), wrap the value in quotes inside the search string: `Query.notSearch(column, '"SWT-2621-44"')`. {% /info %} {% multicode %} @@ -1621,7 +1633,7 @@ Query::orderDesc("column") Query.orderDesc("column") ``` ```server-go -query.OrderDesc("attribute") +query.OrderDesc("column") ``` ```server-rust Query::order_desc("column").to_string() @@ -1658,7 +1670,7 @@ Query::orderAsc("column") Query.orderAsc("column") ``` ```server-go -query.OrderAsc("attribute") +query.OrderAsc("column") ``` ```server-rust Query::order_asc("column").to_string() @@ -3072,4 +3084,3 @@ let rows = tables_db.list_rows( This example demonstrates how to combine `OR` and `AND` operations. The query uses `Query.or()` to match either condition: books under $20 OR magazines under $10. Each condition within the OR is composed of two AND conditions - one for the category and one for the price threshold. The database will return rows that match either of these combined conditions. - diff --git a/src/routes/docs/products/databases/quick-start/+page.markdoc b/src/routes/docs/products/databases/quick-start/+page.markdoc index 180e3b3cf1d..78738351f74 100644 --- a/src/routes/docs/products/databases/quick-start/+page.markdoc +++ b/src/routes/docs/products/databases/quick-start/+page.markdoc @@ -13,7 +13,7 @@ Optionally, add a custom database ID. {% section #create-table step=2 title="Create table" %} Create a table and name it `My books`. Optionally, add a custom table ID. -Navigate to **Columns** and create columns by clicking **Create column** and select **String**. +Navigate to **Columns** and create columns by clicking **Create column** and select **Text**. Columns define the structure of your table's rows. Enter **Column key** and **Size**. For example, `title` and `100`. Navigate to **Settings** > **Permissions** and add a new role **Any**. @@ -325,7 +325,7 @@ try { {% /multicode %} {% info title="Automatic type generation" %} -You can automatically generate type definitions for your tables using the [Appwrite CLI type generation](/docs/products/databases/type-generation) feature. Run `appwrite types collection` to generate models for your collections. +You can automatically generate type definitions for your tables using the [Appwrite CLI type generation](/docs/products/databases/type-generation) feature. Run `appwrite types` to generate models for your tables. {% /info %} ### Model methods diff --git a/src/routes/docs/products/databases/relationships/+page.markdoc b/src/routes/docs/products/databases/relationships/+page.markdoc index dd89e2a19b5..71d6f4adcec 100644 --- a/src/routes/docs/products/databases/relationships/+page.markdoc +++ b/src/routes/docs/products/databases/relationships/+page.markdoc @@ -184,16 +184,16 @@ const client = new Client() const tablesDB = new TablesDB(client); -tablesDB.createRelationshipColumn( - "marvel", // Database ID - "movies", // Table ID - "reviews", // Related table ID - "oneToMany", // Relationship type - true, // Is two-way - "reviews", // Column key - "movie", // Two-way column key - "cascade" // On delete action -); +tablesDB.createRelationshipColumn({ + databaseId: "marvel", + tableId: "movies", + relatedTableId: "reviews", + type: "oneToMany", + twoWay: true, + key: "reviews", + twoWayKey: "movie", + onDelete: "cascade" +}); ``` diff --git a/src/routes/docs/products/databases/rows/+page.markdoc b/src/routes/docs/products/databases/rows/+page.markdoc index 7b2c8541e3d..707b8b4d3a2 100644 --- a/src/routes/docs/products/databases/rows/+page.markdoc +++ b/src/routes/docs/products/databases/rows/+page.markdoc @@ -900,12 +900,12 @@ const client = new Client() const tablesDB = new TablesDB(client); -const promise = tablesDB.updateRow( - '', - '', - '', - { title: 'Updated Title' } -); +const promise = tablesDB.updateRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { title: 'Updated Title' } +}); promise.then(function (response) { console.log(response); @@ -1336,7 +1336,7 @@ if let jsonString = jsonString, {% /tabs %} {% info title="Generate types automatically" %} -You can automatically generate model definitions for your tables using the [Appwrite CLI](/docs/products/databases/type-generation). Run `appwrite types collection` to generate types based on your database schema. +You can automatically generate model definitions for your tables using the [Appwrite CLI](/docs/products/databases/type-generation). Run `appwrite types` to generate types based on your database schema. {% /info %} # Permissions {% #permissions %} @@ -1630,4 +1630,3 @@ Perform create, update, and delete operations on multiple rows simultaneously. Set custom creation and update timestamps when migrating data or backdating records. {% /cards_item %} {% /cards %} - diff --git a/src/routes/docs/products/databases/tables/+page.markdoc b/src/routes/docs/products/databases/tables/+page.markdoc index 1fcff68a5b3..05b212f1e62 100644 --- a/src/routes/docs/products/databases/tables/+page.markdoc +++ b/src/routes/docs/products/databases/tables/+page.markdoc @@ -1568,7 +1568,7 @@ You can choose between the following types. | Column | Description | |--------------|------------------------------------------------------------------| -| `varchar` | String column. Fully indexable if size < 768. Maximum 16,383 characters. | +| `varchar` | Short text column. Fully indexable if size < 768. Maximum 16,383 characters. | | `text` | Text column. Prefix indexing only. Maximum 16,383 characters. | | `mediumtext` | Text column. Prefix indexing only. Maximum 4,194,303 characters. | | `longtext` | Text column. Prefix indexing only. Maximum 1,073,741,823 characters. | diff --git a/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc b/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc index 5c32f39389f..22363521f26 100644 --- a/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc +++ b/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc @@ -1,24 +1,24 @@ --- 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). +To manually set `$createdAt` and `$updatedAt`, you must use a **server SDK** with an **API key**. These columns can be passed inside the `data` parameter on any of the create, update, or upsert routes (single or bulk). {% /info %} # 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 columns 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,39 +31,39 @@ 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 + // ...your columns } -); +}); ``` ```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: '', [ '$createdAt' => (new DateTime(''))->format(DATE_ATOM), '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), - // ...your attributes + // ...your columns ] ); ``` @@ -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,14 +85,14 @@ 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, - // ...your attributes + // ...your columns ] ) print("Created:", created) @@ -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,18 +111,18 @@ 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, - # ...your attributes + # ...your columns } ) ``` @@ -137,18 +137,18 @@ 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, - # ...your attributes + # ...your columns } ) ``` @@ -162,19 +162,19 @@ 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, ["$updatedAt"] = customDate, - // ...your attributes + // ...your columns } ); ``` @@ -186,24 +186,24 @@ 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, - // ...your attributes + // ...your columns }, ); ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use appwrite::id::ID; use serde_json::json; @@ -214,16 +214,16 @@ async fn main() -> Result<(), Box> { .set_project("") .set_key(""); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.create_document( + let result = tablesDB.create_row( "", - "", + "", &ID::unique(), json!({ "$createdAt": "2025-08-10T12:34:56.000Z", "$updatedAt": "2025-08-10T12:34:56.000Z" - // ...your attributes + // ...your columns }), None, None, @@ -237,41 +237,41 @@ async fn main() -> Result<(), Box> { ### 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 + // ...your columns } -); +}); ``` ```server-php -$databases->updateDocument( +$tablesDB->updateRow( databaseId: '', - collectionId: '', - documentId: '', + tableId: '', + rowId: '', [ '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), - // ...your attributes + // ...your columns ] ); ``` ```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 + # ...your columns } ) ``` @@ -284,20 +284,20 @@ 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 + // ...your columns ] ) print("Updated:", updated) @@ -315,17 +315,17 @@ 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 + # ...your columns } ) ``` @@ -339,18 +339,18 @@ 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, - // ...your attributes + // ...your columns } ); ``` @@ -362,23 +362,23 @@ 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 + // ...your columns }, ); ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use serde_json::json; #[tokio::main] @@ -388,15 +388,15 @@ async fn main() -> Result<(), Box> { .set_project("") .set_key(""); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.update_document( + let result = tablesDB.update_row( "", - "", - "", + "", + "", Some(json!({ "$updatedAt": "2025-08-10T12:34:56.000Z" - // ...your attributes + // ...your columns })), None, None, @@ -410,47 +410,47 @@ async fn main() -> Result<(), Box> { ## 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(), '$updatedAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), - // ...your attributes + // ...your columns }, { '$id': sdk.ID.unique(), '$createdAt': new Date('2024-02-01T00:00:00.000Z').toISOString(), '$updatedAt': new Date('2024-02-01T00:00:00.000Z').toISOString(), - // ...your attributes + // ...your columns } ] -); +}); ``` ```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(), '$updatedAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), - # ...your attributes + # ...your columns }, { '$id': ID.unique(), '$createdAt': datetime(2024, 2, 1, tzinfo=timezone.utc).isoformat(), '$updatedAt': datetime(2024, 2, 1, tzinfo=timezone.utc).isoformat(), - # ...your attributes + # ...your columns } ] ) @@ -458,30 +458,30 @@ 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), '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), - // ...your attributes + // ...your columns ], [ '$id' => ID::unique(), '$createdAt' => (new DateTime(''))->format(DATE_ATOM), '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), - // ...your attributes + // ...your columns ], ] ); @@ -495,7 +495,7 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -504,21 +504,21 @@ 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, "$updatedAt": first, - // ...your attributes + // ...your columns ], [ "$id": ID.unique(), "$createdAt": second, "$updatedAt": second, - // ...your attributes + // ...your columns ] ] ) @@ -537,26 +537,26 @@ 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, '$updatedAt' => first, - # ...your attributes + # ...your columns }, { '$id' => ID.unique(), '$createdAt' => second, '$updatedAt' => second, - # ...your attributes + # ...your columns } ] ) @@ -571,29 +571,29 @@ 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 { ["$id"] = ID.Unique(), ["$createdAt"] = first, ["$updatedAt"] = first, - // ...your attributes + // ...your columns }, new Dictionary { ["$id"] = ID.Unique(), ["$createdAt"] = second, ["$updatedAt"] = second, - // ...your attributes + // ...your columns } } ); @@ -606,33 +606,33 @@ 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, '\$updatedAt': first, - // ...your attributes + // ...your columns }, { '\$id': ID.unique(), '\$createdAt': second, '\$updatedAt': second, - // ...your attributes + // ...your columns } ], ); ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use appwrite::id::ID; use serde_json::json; @@ -643,23 +643,23 @@ async fn main() -> Result<(), Box> { .set_project("") .set_key(""); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.create_documents( + let result = tablesDB.create_rows( "", - "", + "", vec![ json!({ "$id": ID::unique(), "$createdAt": "2024-01-01T00:00:00.000Z", "$updatedAt": "2024-01-01T00:00:00.000Z" - // ...your attributes + // ...your columns }), json!({ "$id": ID::unique(), "$createdAt": "2024-02-01T00:00:00.000Z", "$updatedAt": "2024-02-01T00:00:00.000Z" - // ...your attributes + // ...your columns }), ], None, @@ -675,29 +675,29 @@ async fn main() -> Result<(), Box> { {% 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 + // ...your columns } ] -); +}); ``` ```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 + # ...your columns } ] ) @@ -705,24 +705,24 @@ 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 + // ...your columns ], ] ); @@ -736,7 +736,7 @@ let client = Client() .setProject("") .setKey("") -let databases = Databases(client) +let tablesDB = TablesDB(client) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -744,15 +744,15 @@ 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 + // ...your columns ] ] ) @@ -772,19 +772,19 @@ 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 + # ...your columns } ] ) @@ -799,22 +799,22 @@ 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 + // ...your columns } } ); @@ -827,27 +827,27 @@ 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 + // ...your columns } ], ); ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use serde_json::json; #[tokio::main] @@ -857,17 +857,17 @@ async fn main() -> Result<(), Box> { .set_project("") .set_key(""); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.upsert_documents( + let result = tablesDB.upsert_rows( "", - "", + "", vec![ json!({ - "$id": "", + "$id": "", "$createdAt": "2024-01-01T00:00:00.000Z", "$updatedAt": "2025-01-01T00:00:00.000Z" - // ...your attributes + // ...your columns }), ], None, @@ -889,23 +889,23 @@ 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(), [ '$createdAt' => '<ORIGINAL_CREATED_AT_ISO>', '$updatedAt' => '<LAST_MODIFIED_ISO>', @@ -915,10 +915,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>", @@ -928,10 +928,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>', @@ -941,10 +941,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>', @@ -954,10 +954,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>", @@ -968,10 +968,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>', @@ -982,7 +982,7 @@ await databases.createDocument( ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use appwrite::id::ID; use serde_json::json; @@ -993,9 +993,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { .set_project("<PROJECT_ID>") .set_key("<API_KEY>"); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.create_document( + let result = tablesDB.create_row( "<DATABASE_ID>", "blog_posts", &ID::unique(), @@ -1020,23 +1020,23 @@ 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(), [ '$createdAt' => '2023-12-31T23:59:59.000Z', '$updatedAt' => '2023-12-31T23:59:59.000Z', @@ -1046,10 +1046,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", @@ -1059,10 +1059,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', @@ -1072,10 +1072,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', @@ -1085,10 +1085,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", @@ -1099,10 +1099,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', @@ -1113,7 +1113,7 @@ await databases.createDocument( ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use appwrite::id::ID; use serde_json::json; @@ -1124,9 +1124,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { .set_project("<PROJECT_ID>") .set_key("<API_KEY>"); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.create_document( + let result = tablesDB.create_row( "<DATABASE_ID>", "transactions", &ID::unique(), @@ -1151,21 +1151,21 @@ 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>', [ '$updatedAt' => '<EXTERNAL_LAST_MODIFIED_ISO>', 'profile' => '<PROFILE_DATA>' @@ -1173,10 +1173,10 @@ $databases->upsertDocument( ); ``` ```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>" @@ -1184,10 +1184,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>' @@ -1195,10 +1195,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>' @@ -1206,10 +1206,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>", @@ -1218,10 +1218,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>' @@ -1230,7 +1230,7 @@ await databases.upsertDocument( ``` ```rust use appwrite::Client; -use appwrite::services::databases::Databases; +use appwrite::services::tables_db::TablesDB; use serde_json::json; #[tokio::main] @@ -1240,12 +1240,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { .set_project("<PROJECT_ID>") .set_key("<API_KEY>"); - let databases = Databases::new(&client); + let tablesDB = TablesDB::new(&client); - let result = databases.upsert_document( + let result = tablesDB.upsert_row( "<DATABASE_ID>", "users", - "<DOCUMENT_ID_OR_NEW_ID>", + "<ROW_ID_OR_NEW_ID>", Some(json!({ "$updatedAt": "<EXTERNAL_LAST_MODIFIED_ISO>", "profile": "<PROFILE_DATA>" @@ -1262,6 +1262,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { {% info title="Timestamp format and usage" %} - 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. +- You can set either or both columns as needed. If omitted, Appwrite sets them automatically. {% /info %} diff --git a/src/routes/docs/products/databases/transactions/+page.markdoc b/src/routes/docs/products/databases/transactions/+page.markdoc index ccba413fea5..2e18977a870 100644 --- a/src/routes/docs/products/databases/transactions/+page.markdoc +++ b/src/routes/docs/products/databases/transactions/+page.markdoc @@ -660,8 +660,8 @@ Use the `createOperations` method to stage multiple operations across databases { "action": "create|update|upsert|increment|decrement|delete|bulkCreate|bulkUpdate|bulkUpsert|bulkDelete", "databaseId": "<DATABASE_ID>", - "tableId|collectionId": "<TABLE_ID|COLLECTION_ID>", - "rowId|documentId": "<ROW_ID|DOCUMENT_ID>", + "tableId": "<TABLE_ID>", + "rowId": "<ROW_ID>", "data": {} } ] diff --git a/src/routes/docs/products/functions/develop/+page.markdoc b/src/routes/docs/products/functions/develop/+page.markdoc index 83427b0d894..a59c7dc62cc 100644 --- a/src/routes/docs/products/functions/develop/+page.markdoc +++ b/src/routes/docs/products/functions/develop/+page.markdoc @@ -1697,12 +1697,12 @@ export default function ({req, res, error}: any){ const tablesDB = new TablesDB(client); try { - tablesDB.createRow( - "<DATABASE_ID>", - "<TABLE_ID>", - ID.unique(), - {} - ); + tablesDB.createRow({ + databaseId: "<DATABASE_ID>", + tableId: "<TABLE_ID>", + rowId: ID.unique(), + data: {} + }); } catch (e) { error("Failed to create row: " + e.message); return res.text("Failed to create row"); @@ -2085,7 +2085,7 @@ def main(context) tablesDB = Appwrite::TablesDB.new(client) begin - tablesDB.create_row('<DATABASE_ID>', '<TABLE_ID>', Appwrite::ID.unique(), {}) + tablesDB.create_row(database_id: '<DATABASE_ID>', table_id: '<TABLE_ID>', row_id: Appwrite::ID.unique(), data: {}) rescue Appwrite::Exception => e context.error("Failed to create row: " + e.message) return context.response.text("Failed to create row") @@ -2110,12 +2110,12 @@ export default function ({req, res, error}: any){ const tablesDB = new TablesDB(client); try { - tablesDB.createRow( - "<DATABASE_ID>", - "<TABLE_ID>", - ID.unique(), - {} - ); + tablesDB.createRow({ + databaseId: "<DATABASE_ID>", + tableId: "<TABLE_ID>", + rowId: ID.unique(), + data: {} + }); } catch (e) { error("Failed to create row: " + e.message) return res.text("Failed to create row"); diff --git a/src/routes/docs/products/functions/examples/+page.markdoc b/src/routes/docs/products/functions/examples/+page.markdoc index ad0008d05fd..b9393b4bb49 100644 --- a/src/routes/docs/products/functions/examples/+page.markdoc +++ b/src/routes/docs/products/functions/examples/+page.markdoc @@ -397,7 +397,7 @@ def main(context): tablesDB = TablesDB(client) - existing_votes = tablesDB.list_rows('<VOTES_TABLE_ID>', [ + existing_votes = tablesDB.list_rows(database_id='<DATABASE_ID>', table_id='<VOTES_TABLE_ID>', queries=[ Query.equals('userId', vote['userId']), Query.equals('topicId', vote['topicId']) ]) @@ -540,10 +540,14 @@ def main(context) tablesDB = Appwrite::TablesDB.new(client) - existing_votes = tablesDB.list_rows('<DATABASE_ID>', '<VOTES_TABLE_ID>', [ - Appwrite::Query.equal('userId', vote['userId']), - Appwrite::Query.equal('topicId', vote['topicId']) - ]) + existing_votes = tablesDB.list_rows( + database_id: '<DATABASE_ID>', + table_id: '<VOTES_TABLE_ID>', + queries: [ + Appwrite::Query.equal('userId', vote['userId']), + Appwrite::Query.equal('topicId', vote['topicId']) + ] + ) if existing_votes['total'] > 0 return context.res.json({ @@ -788,7 +792,7 @@ def main(context): ) tablesDB = TablesDB(client) - row = tablesDB.create_row('<DATABASE_ID>', '<MESSAGES_TABLE_ID>', ID.unique(), message) + row = tablesDB.create_row(database_id='<DATABASE_ID>', table_id='<MESSAGES_TABLE_ID>', row_id=ID.unique(), data=message) return context.res.text("Message sent") @@ -891,7 +895,7 @@ def main(context) .set_key(context.req.headers['x-appwrite-key']) tablesDB = Appwrite::TablesDB.new(client) - row = tablesDB.create_row('<DATABASE_ID>', '<MESSAGES_TABLE_ID>', ID.unique(), message) + row = tablesDB.create_row(database_id: '<DATABASE_ID>', table_id: '<MESSAGES_TABLE_ID>', row_id: ID.unique(), data: message) return context.res.text("Message sent") end diff --git a/src/routes/docs/products/functions/execute/+page.markdoc b/src/routes/docs/products/functions/execute/+page.markdoc index 3a41e6adbb4..9d0c4817f97 100644 --- a/src/routes/docs/products/functions/execute/+page.markdoc +++ b/src/routes/docs/products/functions/execute/+page.markdoc @@ -756,13 +756,15 @@ client ; const promise = functions.createExecution( - '<FUNCTION_ID>', // functionId - '<BODY>', // body (optional) - true, // Scheduled executions need to be async - '<PATH>', // path (optional) - ExecutionMethod.GET, // method (optional) - {}, // headers (optional) - '2020-10-15T06:38:00.000+00:00' // Schedule execution (optional) + { + functionId: '<FUNCTION_ID>', + body: '<BODY>', + async: true, + xpath: '<PATH>', + method: ExecutionMethod.GET, + headers: {}, + scheduledAt: '2020-10-15T06:38:00.000+00:00' +} // Schedule execution (optional) ); promise.then(function (response) { @@ -784,13 +786,15 @@ client ; const promise = functions.createExecution( - '<FUNCTION_ID>', // functionId - '<BODY>', // body (optional) - true, // Scheduled executions need to be async - '<PATH>', // path (optional) - ExecutionMethod.GET, // method (optional) - {}, // headers (optional) - '2020-10-15T06:38:00.000+00:00' // Schedule execution (optional) + { + functionId: '<FUNCTION_ID>', + body: '<BODY>', + async: true, + xpath: '<PATH>', + method: ExecutionMethod.GET, + headers: {}, + scheduledAt: '2020-10-15T06:38:00.000+00:00' +} // Schedule execution (optional) ); promise.then(function (response) { diff --git a/src/routes/docs/products/messaging/apns/+page.markdoc b/src/routes/docs/products/messaging/apns/+page.markdoc index b542af02a1c..0fbdc51b05e 100644 --- a/src/routes/docs/products/messaging/apns/+page.markdoc +++ b/src/routes/docs/products/messaging/apns/+page.markdoc @@ -116,13 +116,15 @@ client ; const provider = await messaging.updateApnsProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<AUTH_KEY>', // authKey (optional) - '<AUTH_KEY_ID>', // authKeyId (optional) - '<TEAM_ID>', // teamId (optional) - '<BUNDLE_ID>' // bundleId (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + authKey: '<AUTH_KEY>', + authKeyId: '<AUTH_KEY_ID>', + teamId: '<TEAM_ID>', + bundleId: '<BUNDLE_ID>' +} // bundleId (optional) ); ``` ```deno @@ -140,13 +142,15 @@ client ; const provider = await messaging.updateApnsProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<AUTH_KEY>', // authKey (optional) - '<AUTH_KEY_ID>', // authKeyId (optional) - '<TEAM_ID>', // teamId (optional) - '<BUNDLE_ID>' // bundleId (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + authKey: '<AUTH_KEY>', + authKeyId: '<AUTH_KEY_ID>', + teamId: '<TEAM_ID>', + bundleId: '<BUNDLE_ID>' +} // bundleId (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/fcm/+page.markdoc b/src/routes/docs/products/messaging/fcm/+page.markdoc index b674fcdbb16..21c2f66f14f 100644 --- a/src/routes/docs/products/messaging/fcm/+page.markdoc +++ b/src/routes/docs/products/messaging/fcm/+page.markdoc @@ -139,10 +139,12 @@ client ; const provider = await messaging.updateFCMProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - {} // serviceAccountJSON (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + serviceAccountJSON: {} +} // serviceAccountJSON (optional) ); ``` ```deno @@ -160,10 +162,12 @@ client ; const provider = await messaging.updateFCMProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - {} // serviceAccountJSON (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + serviceAccountJSON: {} +} // serviceAccountJSON (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/mailgun/+page.markdoc b/src/routes/docs/products/messaging/mailgun/+page.markdoc index 2b63a570aed..a0756311b6a 100644 --- a/src/routes/docs/products/messaging/mailgun/+page.markdoc +++ b/src/routes/docs/products/messaging/mailgun/+page.markdoc @@ -156,7 +156,11 @@ client = Client() messaging = Messaging(client) -result = messaging.create_email('<MESSAGE_ID>', '<SUBJECT>', '<CONTENT>') +result = messaging.create_email( + message_id='<MESSAGE_ID>', + subject='<SUBJECT>', + content='<CONTENT>' +) ``` ```ruby require 'appwrite' @@ -349,25 +353,27 @@ client ; // update provider -messaging.updateSendgridProvider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', -).then(function (response) { +messaging.updateMailgunProvider({ + providerId: '<PROVIDER_ID>', + name: '<PROVIDER_NAME>', + apiKey: '<API_KEY>', + domain: '<DOMAIN>', + isEuRegion: false, + fromName: '<SENDER_NAME>', + fromEmail: '<SENDER_EMAIL>', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>', + enabled: true +}).then(function (response) { console.log(response); }, function (error) { console.log(error); }); // delete provider -messaging.deleteProvider('<PROVIDER_ID>') +messaging.deleteProvider({ + providerId: '<PROVIDER_ID>' +}) .then(function (response) { console.log(response); }, function (error) { @@ -383,25 +389,27 @@ let client = new sdk.Client(); let messaging = new sdk.Messaging(client); // update provider -messaging.updateSendgridProvider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', -).then(function (response) { +messaging.updateMailgunProvider({ + providerId: '<PROVIDER_ID>', + name: '<PROVIDER_NAME>', + apiKey: '<API_KEY>', + domain: '<DOMAIN>', + isEuRegion: false, + fromName: '<SENDER_NAME>', + fromEmail: '<SENDER_EMAIL>', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>', + enabled: true +}).then(function (response) { console.log(response); }, function (error) { console.log(error); }); // delete provider -messaging.deleteProvider('<PROVIDER_ID>') +messaging.deleteProvider({ + providerId: '<PROVIDER_ID>' +}) .then(function (response) { console.log(response); }, function (error) { @@ -450,17 +458,17 @@ client = Client() messaging = Messaging(client) -result = messaging.update_sendgrid_provider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', +result = messaging.update_mailgun_provider( + provider_id='<PROVIDER_ID>', + name='<PROVIDER_NAME>', + api_key='<API_KEY>', + domain='<DOMAIN>', + is_eu_region=False, + from_name='<SENDER_NAME>', + from_email='<SENDER_EMAIL>', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=True, ) ``` ```ruby diff --git a/src/routes/docs/products/messaging/messages/+page.markdoc b/src/routes/docs/products/messaging/messages/+page.markdoc index b1a7e31e605..74ee900a4ce 100644 --- a/src/routes/docs/products/messaging/messages/+page.markdoc +++ b/src/routes/docs/products/messaging/messages/+page.markdoc @@ -250,24 +250,26 @@ client ; const message = await messaging.createPush( - '<MESSAGE_ID>', // messageId - '<TITLE>', // title - '<BODY>', // body - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - {}, // data (optional) - '<ACTION>', // action (optional) - '<ICON>', // icon (optional) - '<SOUND>', // sound (optional) - '<COLOR>', // color (optional) - '<TAG>', // tag (optional) - 1, // badge (optional) - false, // contentAvailable (optional) - false, // critical (optional) - 'normal', // priority (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + title: '<TITLE>', + body: '<BODY>', + topics: [], + users: [], + targets: [], + data: {}, + action: '<ACTION>', + icon: '<ICON>', + sound: '<SOUND>', + color: '<COLOR>', + tag: '<TAG>', + badge: 1, + contentAvailable: false, + critical: false, + priority: 'normal', + draft: true, + scheduledAt: '' + } ); ``` ```deno @@ -285,24 +287,26 @@ client ; const message = await messaging.createPush( - '<MESSAGE_ID>', // messageId - '<TITLE>', // title - '<BODY>', // body - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - {}, // data (optional) - '<ACTION>', // action (optional) - '<ICON>', // icon (optional) - '<SOUND>', // sound (optional) - '<COLOR>', // color (optional) - '<TAG>', // tag (optional) - 1, // badge (optional) - false, // contentAvailable (optional) - false, // critical (optional) - 'normal', // priority (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + title: '<TITLE>', + body: '<BODY>', + topics: [], + users: [], + targets: [], + data: {}, + action: '<ACTION>', + icon: '<ICON>', + sound: '<SOUND>', + color: '<COLOR>', + tag: '<TAG>', + badge: 1, + contentAvailable: false, + critical: false, + priority: 'normal', + draft: true, + scheduledAt: '' + } ); ``` ```php @@ -660,19 +664,19 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const message - await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - true, // draft (optional) - false, // html (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createEmail({ + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + draft: true, + html: false, + scheduledAt: '' +}); ``` ```deno import * as sdk from "npm:node-appwrite"; @@ -688,19 +692,19 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const message - await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - true, // draft (optional) - false, // html (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createEmail({ + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + draft: true, + html: false, + scheduledAt: '' +}); ``` ```php <?php @@ -998,13 +1002,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -1022,13 +1028,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/msg91/+page.markdoc b/src/routes/docs/products/messaging/msg91/+page.markdoc index 0ea24698aec..b9fdcf70d9e 100644 --- a/src/routes/docs/products/messaging/msg91/+page.markdoc +++ b/src/routes/docs/products/messaging/msg91/+page.markdoc @@ -81,13 +81,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -105,13 +107,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php @@ -384,12 +388,14 @@ client ; const message = await messaging.updateMsg91Provider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<SENDER_ID>', // senderId (optional) - '<AUTH_KEY>', // authKey (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + templateId: '<SENDER_ID>', + senderId: '<AUTH_KEY>', + authKey: '<FROM>' +} // from (optional) ); ``` ```deno @@ -407,12 +413,14 @@ client ; const message = await messaging.updateMsg91Provider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<SENDER_ID>', // senderId (optional) - '<AUTH_KEY>', // authKey (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + templateId: '<SENDER_ID>', + senderId: '<AUTH_KEY>', + authKey: '<FROM>' +} // from (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/send-email-messages/+page.markdoc b/src/routes/docs/products/messaging/send-email-messages/+page.markdoc index 42a5efc0261..2b3b5ac363d 100644 --- a/src/routes/docs/products/messaging/send-email-messages/+page.markdoc +++ b/src/routes/docs/products/messaging/send-email-messages/+page.markdoc @@ -52,12 +52,14 @@ const client = new sdk.Client() const users = new sdk.Users(client); const target = await users.createTarget( - '<USER_ID>', // userId - '<TARGET_ID>', // targetId - sdk.MessagingProviderType.Email, // providerType - '<IDENTIFIER>', // identifier - '<PROVIDER_ID>', // providerId (optional) - '<NAME>' // name (optional) + { + userId: '<USER_ID>', + targetId: '<TARGET_ID>', + providerType: sdk.MessagingProviderType.Email, + identifier: '<IDENTIFIER>', + providerId: '<PROVIDER_ID>', + name: '<NAME>' +} // name (optional) ); ``` ```deno @@ -71,12 +73,14 @@ const client = new sdk.Client() const users = new sdk.Users(client); const target = await users.createTarget( - '<USER_ID>', // userId - '<TARGET_ID>', // targetId - sdk.MessagingProviderType.Email, // providerType - '<IDENTIFIER>', // identifier - '<PROVIDER_ID>', // providerId (optional) - '<NAME>' // name (optional) + { + userId: '<USER_ID>', + targetId: '<TARGET_ID>', + providerType: sdk.MessagingProviderType.Email, + identifier: '<IDENTIFIER>', + providerId: '<PROVIDER_ID>', + name: '<NAME>' +} // name (optional) ); ``` ```php @@ -320,8 +324,10 @@ client ; const topic = await messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>' // name + { + topicId: '<TOPIC_ID>', + name: '<NAME>' +} // name ); ``` ```deno @@ -339,8 +345,10 @@ client ; const topic = await messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>' // name + { + topicId: '<TOPIC_ID>', + name: '<NAME>' +} // name ); ``` ```php @@ -555,18 +563,20 @@ client ; const message = await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - [], // attachments (optional) - false, // draft (optional) - false, // html (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + attachments: [], + draft: false, + html: false, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -584,18 +594,20 @@ client ; const message = await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - [], // attachments (optional) - false, // draft (optional) - false, // html (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + attachments: [], + draft: false, + html: false, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php @@ -891,17 +903,19 @@ client ; const message = await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - false, // draft (optional) - false, // html (optional) - '2025-02-13T22:01:00+0000' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + draft: false, + html: false, + scheduledAt: '2025-02-13T22:01:00+0000' + } ); ``` ```deno @@ -918,19 +932,19 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const message - await messaging.createEmail( - '<MESSAGE_ID>', // messageId - '<SUBJECT>', // subject - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - [], // cc (optional) - [], // bcc (optional) - false, // draft (optional) - false, // html (optional) - '2025-02-13T22:01:00+0000' // scheduledAt (optional) - ); +const message = await messaging.createEmail({ + messageId: '<MESSAGE_ID>', + subject: '<SUBJECT>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + cc: [], + bcc: [], + draft: false, + html: false, + scheduledAt: '2025-02-13T22:01:00+0000' +}); ``` ```php <?php diff --git a/src/routes/docs/products/messaging/send-sms-messages/+page.markdoc b/src/routes/docs/products/messaging/send-sms-messages/+page.markdoc index b5b10245f81..d9175c1553a 100644 --- a/src/routes/docs/products/messaging/send-sms-messages/+page.markdoc +++ b/src/routes/docs/products/messaging/send-sms-messages/+page.markdoc @@ -61,12 +61,14 @@ const client = new sdk.Client() const users = new sdk.Users(client); const target = await users.createTarget( - '<USER_ID>', // userId - '<TARGET_ID>', // targetId - sdk.MessagingProviderType.Phone, // providerType - '<IDENTIFIER>', // identifier - '<PROVIDER_ID>', // providerId (optional) - '<NAME>' // name (optional) + { + userId: '<USER_ID>', + targetId: '<TARGET_ID>', + providerType: sdk.MessagingProviderType.Phone, + identifier: '<IDENTIFIER>', + providerId: '<PROVIDER_ID>', + name: '<NAME>' +} // name (optional) ); ``` ```deno @@ -80,12 +82,14 @@ const client = new sdk.Client() const users = new sdk.Users(client); const target = await users.createTarget( - '<USER_ID>', // userId - '<TARGET_ID>', // targetId - sdk.MessagingProviderType.Phone, // providerType - '<IDENTIFIER>', // identifier - '<PROVIDER_ID>', // providerId (optional) - '<NAME>' // name (optional) + { + userId: '<USER_ID>', + targetId: '<TARGET_ID>', + providerType: sdk.MessagingProviderType.Phone, + identifier: '<IDENTIFIER>', + providerId: '<PROVIDER_ID>', + name: '<NAME>' +} // name (optional) ); ``` ```php @@ -326,8 +330,10 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const topic = await messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>' // name + { + topicId: '<TOPIC_ID>', + name: '<NAME>' +} // name ); ``` ```deno @@ -341,8 +347,10 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const topic = await messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>' // name + { + topicId: '<TOPIC_ID>', + name: '<NAME>' +} // name ); ``` ```php @@ -528,13 +536,15 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - false, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: false, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -548,13 +558,15 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - false, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: false, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php @@ -784,13 +796,15 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - false, // draft (optional) - '2025-02-13T22:01:00+0000' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: false, + scheduledAt: '2025-02-13T22:01:00+0000' +} // scheduledAt (optional) ); ``` ```deno @@ -804,13 +818,15 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - false, // draft (optional) - '2025-02-13T22:01:00+0000' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: false, + scheduledAt: '2025-02-13T22:01:00+0000' +} // scheduledAt (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/sendgrid/+page.markdoc b/src/routes/docs/products/messaging/sendgrid/+page.markdoc index 37ade828b70..bce6da06224 100644 --- a/src/routes/docs/products/messaging/sendgrid/+page.markdoc +++ b/src/routes/docs/products/messaging/sendgrid/+page.markdoc @@ -149,7 +149,11 @@ client = Client() messaging = Messaging(client) -result = messaging.create_email('<MESSAGE_ID>', '<SUBJECT>', '<CONTENT>') +result = messaging.create_email( + message_id='<MESSAGE_ID>', + subject='<SUBJECT>', + content='<CONTENT>' +) ``` ```ruby require 'appwrite' @@ -341,16 +345,16 @@ client .setKey('<YOUR_API_KEY>') // Your secret API key ; -const provider = await messaging.updateSendgridProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - '<API_KEY>', // apiKey (optional) - false, // enabled (optional) - '<FROM_NAME>', // fromName (optional) - 'email@example.com', // fromEmail (optional) - '<REPLY_TO_NAME>', // replyToName (optional) - '<REPLY_TO_EMAIL>' // replyToEmail (optional) - ); +const provider = await messaging.updateSendgridProvider({ + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + apiKey: '<API_KEY>', + fromName: '<FROM_NAME>', + fromEmail: 'email@example.com', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>' +}); ``` ```deno import * as sdk from "npm:node-appwrite"; @@ -366,16 +370,16 @@ client .setKey('<YOUR_API_KEY>') // Your secret API key ; -const provider = await messaging.updateSendgridProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - '<API_KEY>', // apiKey (optional) - false, // enabled (optional) - '<FROM_NAME>', // fromName (optional) - 'email@example.com', // fromEmail (optional) - '<REPLY_TO_NAME>', // replyToName (optional) - '<REPLY_TO_EMAIL>' // replyToEmail (optional) - ); +const provider = await messaging.updateSendgridProvider({ + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + apiKey: '<API_KEY>', + fromName: '<FROM_NAME>', + fromEmail: 'email@example.com', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>' +}); ``` ```php <?php diff --git a/src/routes/docs/products/messaging/smtp/+page.markdoc b/src/routes/docs/products/messaging/smtp/+page.markdoc index 52e7fd580ab..b1c3068126b 100644 --- a/src/routes/docs/products/messaging/smtp/+page.markdoc +++ b/src/routes/docs/products/messaging/smtp/+page.markdoc @@ -158,7 +158,11 @@ client = Client() messaging = Messaging(client) -result = messaging.create_email('<MESSAGE_ID>', '<SUBJECT>', '<CONTENT>') +result = messaging.create_email( + message_id='<MESSAGE_ID>', + subject='<SUBJECT>', + content='<CONTENT>' +) ``` ```ruby require 'appwrite' @@ -351,25 +355,31 @@ client ; // update provider -messaging.updateSendgridProvider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', -).then(function (response) { +messaging.updateSmtpProvider({ + providerId: '<PROVIDER_ID>', + name: '<PROVIDER_NAME>', + host: '<HOST>', + port: 587, + username: '<USERNAME>', + password: '<PASSWORD>', + encryption: sdk.SmtpEncryption.Tls, + autoTLS: true, + mailer: '<MAILER>', + fromName: '<SENDER_NAME>', + fromEmail: '<SENDER_EMAIL>', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>', + enabled: true +}).then(function (response) { console.log(response); }, function (error) { console.log(error); }); // delete provider -messaging.deleteProvider('<PROVIDER_ID>') +messaging.deleteProvider({ + providerId: '<PROVIDER_ID>' +}) .then(function (response) { console.log(response); }, function (error) { @@ -385,25 +395,31 @@ let client = new sdk.Client(); let messaging = new sdk.Messaging(client); // update provider -messaging.updateSendgridProvider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', -).then(function (response) { +messaging.updateSmtpProvider({ + providerId: '<PROVIDER_ID>', + name: '<PROVIDER_NAME>', + host: '<HOST>', + port: 587, + username: '<USERNAME>', + password: '<PASSWORD>', + encryption: sdk.SmtpEncryption.Tls, + autoTLS: true, + mailer: '<MAILER>', + fromName: '<SENDER_NAME>', + fromEmail: '<SENDER_EMAIL>', + replyToName: '<REPLY_TO_NAME>', + replyToEmail: '<REPLY_TO_EMAIL>', + enabled: true +}).then(function (response) { console.log(response); }, function (error) { console.log(error); }); // delete provider -messaging.deleteProvider('<PROVIDER_ID>') +messaging.deleteProvider({ + providerId: '<PROVIDER_ID>' +}) .then(function (response) { console.log(response); }, function (error) { @@ -452,17 +468,21 @@ client = Client() messaging = Messaging(client) -result = messaging.update_sendgrid_provider( - '<PROVIDER_ID>', - '<PROVIDER_NAME>', - '<API_KEY>', - '<DOMAIN>', - '<IS_EU_REGION?>', - '<SENDER_NAME>', - '<SENDER_EMAIL>', - '<REPLY_TO_NAME>', - '<REPLY_TO_EMAIL>', - '<ENABLED?>', +result = messaging.update_smtp_provider( + provider_id='<PROVIDER_ID>', + name='<PROVIDER_NAME>', + host='<HOST>', + port=587, + username='<USERNAME>', + password='<PASSWORD>', + encryption='tls', + auto_tls=True, + mailer='<MAILER>', + from_name='<SENDER_NAME>', + from_email='<SENDER_EMAIL>', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=True, ) ``` ```ruby diff --git a/src/routes/docs/products/messaging/telesign/+page.markdoc b/src/routes/docs/products/messaging/telesign/+page.markdoc index 3b8292fd328..fc11c398f25 100644 --- a/src/routes/docs/products/messaging/telesign/+page.markdoc +++ b/src/routes/docs/products/messaging/telesign/+page.markdoc @@ -79,15 +79,15 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const messaging = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createSms({ + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +}); ``` ```deno import * as sdk from "npm:node-appwrite"; @@ -103,15 +103,15 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const messaging = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createSms({ + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +}); ``` ```php <?php @@ -382,12 +382,14 @@ client ; const provider = await messaging.updateTelesignProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<USERNAME>', // username (optional) - '<PASSWORD>', // password (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + customerId: '<USERNAME>', + apiKey: '<PASSWORD>', + from: '<FROM>' +} // from (optional) ); ``` ```deno @@ -405,12 +407,14 @@ client ; const provider = await messaging.updateTelesignProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<USERNAME>', // username (optional) - '<PASSWORD>', // password (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + customerId: '<USERNAME>', + apiKey: '<PASSWORD>', + from: '<FROM>' +} // from (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/textmagic/+page.markdoc b/src/routes/docs/products/messaging/textmagic/+page.markdoc index 76d09c143e0..179da6240fb 100644 --- a/src/routes/docs/products/messaging/textmagic/+page.markdoc +++ b/src/routes/docs/products/messaging/textmagic/+page.markdoc @@ -79,15 +79,15 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const messaging = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createSms({ + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +}); ``` ```deno import * as sdk from "npm:node-appwrite"; @@ -103,15 +103,15 @@ client .setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key ; -const messaging = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) - ); +const message = await messaging.createSms({ + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +}); ``` ```php <?php @@ -382,12 +382,14 @@ client ; const provider = await messaging.updateTextmagicProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<USERNAME>', // username (optional) - '<API_KEY>', // apiKey (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + username: '<USERNAME>', + apiKey: '<API_KEY>', + from: '<FROM>' +} // from (optional) ); ``` ```deno @@ -405,12 +407,14 @@ client ; const provider = await messaging.updateTextmagicProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<USERNAME>', // username (optional) - '<API_KEY>', // apiKey (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + username: '<USERNAME>', + apiKey: '<API_KEY>', + from: '<FROM>' +} // from (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/topics/+page.markdoc b/src/routes/docs/products/messaging/topics/+page.markdoc index bb31eee9fd8..ce665b89567 100644 --- a/src/routes/docs/products/messaging/topics/+page.markdoc +++ b/src/routes/docs/products/messaging/topics/+page.markdoc @@ -59,9 +59,11 @@ client ; const topic = messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>', // name - '<ROLES>' // permission roles for who can subscribe + { + topicId: '<TOPIC_ID>', + name: '<NAME>', + subscribe: ['<ROLE>'] + } ); ``` @@ -80,15 +82,10 @@ client ; const topic = messaging.createTopic({ - topicId: '<TOPIC_ID>', - name: '<NAME>', - subscribe: '<ROLES>' // permission roles for who can subscribe - }); -const topic = messaging.createTopic( - '<TOPIC_ID>', // topicId - '<NAME>', // name - '<ROLES>' // permission roles for who can subscribe - ); + topicId: '<TOPIC_ID>', + name: '<NAME>', + subscribe: ['<ROLE>'] +}); ``` ```php <?php @@ -354,9 +351,11 @@ const client = new sdk.Client() const messaging = new sdk.Messaging(client); const subscriber = await messaging.createSubscriber( - '<TOPIC_ID>', // topicId - '<SUBSCRIBER_ID>', // subscriberId - '<TARGET_ID>' // targetId + { + topicId: '<TOPIC_ID>', + subscriberId: '<SUBSCRIBER_ID>', + targetId: '<TARGET_ID>' +} // targetId ); ``` ```deno diff --git a/src/routes/docs/products/messaging/twilio/+page.markdoc b/src/routes/docs/products/messaging/twilio/+page.markdoc index f97e0b451e8..d9bea4e3db4 100644 --- a/src/routes/docs/products/messaging/twilio/+page.markdoc +++ b/src/routes/docs/products/messaging/twilio/+page.markdoc @@ -82,13 +82,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -106,13 +108,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php @@ -382,12 +386,14 @@ client ; const provider = await messaging.updateTwilioProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<ACCOUNT_SID>', // accountSid (optional) - '<AUTH_TOKEN>', // authToken (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + accountSid: '<ACCOUNT_SID>', + authToken: '<AUTH_TOKEN>', + from: '<FROM>' +} // from (optional) ); ``` ```deno @@ -405,12 +411,14 @@ client ; const provider = await messaging.updateTwilioProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<ACCOUNT_SID>', // accountSid (optional) - '<AUTH_TOKEN>', // authToken (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + accountSid: '<ACCOUNT_SID>', + authToken: '<AUTH_TOKEN>', + from: '<FROM>' +} // from (optional) ); ``` ```php diff --git a/src/routes/docs/products/messaging/vonage/+page.markdoc b/src/routes/docs/products/messaging/vonage/+page.markdoc index 0d57869a326..a7a25fe8647 100644 --- a/src/routes/docs/products/messaging/vonage/+page.markdoc +++ b/src/routes/docs/products/messaging/vonage/+page.markdoc @@ -82,13 +82,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```deno @@ -106,13 +108,15 @@ client ; const message = await messaging.createSms( - '<MESSAGE_ID>', // messageId - '<CONTENT>', // content - [], // topics (optional) - [], // users (optional) - [], // targets (optional) - true, // draft (optional) - '' // scheduledAt (optional) + { + messageId: '<MESSAGE_ID>', + content: '<CONTENT>', + topics: [], + users: [], + targets: [], + draft: true, + scheduledAt: '' +} // scheduledAt (optional) ); ``` ```php @@ -384,12 +388,14 @@ client ; const provider = await messaging.updateVonageProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<API_KEY>', // apiKey (optional) - '<API_SECRET>', // apiSecret (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + apiKey: '<API_KEY>', + apiSecret: '<API_SECRET>', + from: '<FROM>' +} // from (optional) ); ``` ```deno @@ -407,12 +413,14 @@ client ; const provider = await messaging.updateVonageProvider( - '<PROVIDER_ID>', // providerId - '<NAME>', // name (optional) - false, // enabled (optional) - '<API_KEY>', // apiKey (optional) - '<API_SECRET>', // apiSecret (optional) - '<FROM>' // from (optional) + { + providerId: '<PROVIDER_ID>', + name: '<NAME>', + enabled: false, + apiKey: '<API_KEY>', + apiSecret: '<API_SECRET>', + from: '<FROM>' +} // from (optional) ); ``` ```php diff --git a/src/routes/docs/products/network/caching/+page.markdoc b/src/routes/docs/products/network/caching/+page.markdoc index 8b055fc04b1..81b6154d772 100644 --- a/src/routes/docs/products/network/caching/+page.markdoc +++ b/src/routes/docs/products/network/caching/+page.markdoc @@ -8,29 +8,29 @@ Appwrite employs a multi-layered caching approach to enhance the performance of # Region-level {% #region-level %} -At the region level, Appwrite provides smart in-memory caching for various resources: +At the region level, Appwrite provides smart in-memory caching for various resources: -- **Documents**: Frequently accessed rows are cached in memory and automatically purged when updated, ensuring data consistency without manual intervention. -- **Storage files**: Frequently accessed files are cached in memory to reduce disk reads and improve performance. -- **Image transformations**: Processed images (e.g., resized or converted) are cached in memory for faster repeated requests, reducing processing overhead. +- **Rows**: Frequently accessed rows are cached in memory and automatically purged when updated, ensuring data consistency without manual intervention. +- **Storage files**: Frequently accessed files are cached in memory to reduce disk reads and improve performance. +- **Image transformations**: Processed images (e.g., resized or converted) are cached in memory for faster repeated requests, reducing processing overhead. Region-level caching is tightly integrated with Appwrite's APIs, optimizing performance while preserving data integrity. # Edge-level {% #edge-level %} -At the edge, Appwrite employs smart caching for specific use cases: +At the edge, Appwrite employs smart caching for specific use cases: -- **Compute builds**: Caches build artifacts for faster deployments and reduced latency during function executions. -- **Cold starts**: Pre-loads frequently accessed resources, reducing latency for new requests and improving application responsiveness. +- **Compute builds**: Caches build artifacts for faster deployments and reduced latency during function executions. +- **Cold starts**: Pre-loads frequently accessed resources, reducing latency for new requests and improving application responsiveness. Edge-level caching complements region-level caching, ensuring optimal performance for globally distributed applications. # Private caching {% #cdn-caching %} Appwrite's CDN layer includes **private caching**, a caching strategy designed to handle the dynamic and permission-sensitive nature of Appwrite's APIs and resources securely. -**What is private caching?** +**What is private caching?** -In the HTTP context, private caching allows responses to be cached but ensures they are only served to the specific user or client that requested them. This is achieved using HTTP headers that control caching behavior. For example: +In the HTTP context, private caching allows responses to be cached but ensures they are only served to the specific user or client that requested them. This is achieved using HTTP headers that control caching behavior. For example: - `Cache-Control: private, max-age=3600` Indicates that the response can be cached, but only in a private cache (e.g., the user's browser). @@ -58,4 +58,4 @@ Enterprise customers can collaborate with their Appwrite success manager to defi - Defining exclusion rules for sensitive or frequently changing data. - Optimizing cache invalidation strategies for complex workflows. -For more information on upgrading to the enterprise plan, [contact sales](https://appwrite.io/contact-us/enterprise). \ No newline at end of file +For more information on upgrading to the enterprise plan, [contact sales](https://appwrite.io/contact-us/enterprise). diff --git a/src/routes/docs/products/network/compression/+page.markdoc b/src/routes/docs/products/network/compression/+page.markdoc index f588bda1d2e..a85d0cdc0fd 100644 --- a/src/routes/docs/products/network/compression/+page.markdoc +++ b/src/routes/docs/products/network/compression/+page.markdoc @@ -81,19 +81,21 @@ client ; const result = storage.getFilePreview( - 'photos', // bucket ID - 'sunset.png', // file ID - 1800, // width, will be resized using this value. - 0, // height, ignored when 0 - 'center', // crop center - '90', // slight compression - 5, // border width - 'CDCA30', // border color - 15, // border radius - 1, // full opacity - 0, // no rotation - 'FFFFFF', // background color - 'webp' // output jpg format + { + bucketId: 'photos', + fileId: 'sunset.png', + width: 1800, + height: 0, + gravity: 'center', + quality: '90', + borderWidth: 5, + borderColor: 'CDCA30', + borderRadius: 15, + opacity: 1, + rotation: 0, + background: 'FFFFFF', + output: 'webp' +} // output jpg format ); console.log(result.href); diff --git a/src/routes/docs/products/storage/buckets/+page.markdoc b/src/routes/docs/products/storage/buckets/+page.markdoc index 2609ca305b3..3cde6f1b620 100644 --- a/src/routes/docs/products/storage/buckets/+page.markdoc +++ b/src/routes/docs/products/storage/buckets/+page.markdoc @@ -110,7 +110,7 @@ client = Client() storage = Storage(client) -result = storage.create_bucket('<BUCKET_ID>', '<NAME>') +result = storage.create_bucket(bucket_id='<BUCKET_ID>', name='<NAME>') ``` ```ruby require 'Appwrite' diff --git a/src/routes/docs/quick-starts/apple/+page.markdoc b/src/routes/docs/quick-starts/apple/+page.markdoc index 56c4d665fd9..b542412cac5 100644 --- a/src/routes/docs/quick-starts/apple/+page.markdoc +++ b/src/routes/docs/quick-starts/apple/+page.markdoc @@ -232,7 +232,7 @@ do { ``` {% info title="Generate types automatically" %} -Use the [Appwrite CLI](/docs/products/databases/type-generation) to generate model structs automatically: `appwrite types collection` +Use the [Appwrite CLI](/docs/products/databases/type-generation) to generate model structs automatically: `appwrite types ./models` {% /info %} {% /section %} diff --git a/src/routes/docs/quick-starts/go/+page.markdoc b/src/routes/docs/quick-starts/go/+page.markdoc index ccd5cf4dd12..9618e85d567 100644 --- a/src/routes/docs/quick-starts/go/+page.markdoc +++ b/src/routes/docs/quick-starts/go/+page.markdoc @@ -202,8 +202,8 @@ type Todo struct { } type TodoList struct { - *models.DocumentList - Documents []Todo `json:"rows"` + *models.RowList + Rows []Todo `json:"rows"` } func getTodos() { @@ -217,7 +217,7 @@ func getTodos() { todoResponse.Decode(&todos) fmt.Println("Todos:") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } @@ -238,7 +238,7 @@ func getCompletedTodos() { todoResponse.Decode(&todos) fmt.Println("Completed todos (limited to 5):") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } @@ -258,7 +258,7 @@ func getIncompleteTodos() { todoResponse.Decode(&todos) fmt.Println("Incomplete todos (ordered by title):") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } @@ -307,4 +307,4 @@ func main() { Run your project with `go run .` and view the response in your console. -{% /section %} \ No newline at end of file +{% /section %} diff --git a/src/routes/docs/quick-starts/go/prompt.md b/src/routes/docs/quick-starts/go/prompt.md index 5036401ad4f..d6c83746455 100644 --- a/src/routes/docs/quick-starts/go/prompt.md +++ b/src/routes/docs/quick-starts/go/prompt.md @@ -168,8 +168,8 @@ type Todo struct { } type TodoList struct { - *models.DocumentList - Documents []Todo `json:"rows"` + *models.RowList + Rows []Todo `json:"rows"` } ``` @@ -186,7 +186,7 @@ func getTodos() { todoResponse.Decode(&todos) fmt.Println("Todos:") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } @@ -210,7 +210,7 @@ func getCompletedTodos() { todoResponse.Decode(&todos) fmt.Println("Completed todos (limited to 5):") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } @@ -233,7 +233,7 @@ func getIncompleteTodos() { todoResponse.Decode(&todos) fmt.Println("Incomplete todos (ordered by title):") - for _, todo := range todos.Documents { + for _, todo := range todos.Rows { fmt.Printf("Title: %s\nDescription: %s\nIs Todo Complete: %t\n\n", todo.Title, todo.Description, todo.IsComplete) } } diff --git a/src/routes/docs/tooling/ai/agents/antigravity/+page.markdoc b/src/routes/docs/tooling/ai/agents/antigravity/+page.markdoc index 3c32208631a..a12dc4d0dd5 100644 --- a/src/routes/docs/tooling/ai/agents/antigravity/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/antigravity/+page.markdoc @@ -112,8 +112,8 @@ Open **Agent Manager** in Antigravity to test your MCP integrations. You can try - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/agents/claude-code/+page.markdoc b/src/routes/docs/tooling/ai/agents/claude-code/+page.markdoc index 53b69e92132..22a07570ec7 100644 --- a/src/routes/docs/tooling/ai/agents/claude-code/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/claude-code/+page.markdoc @@ -128,8 +128,8 @@ Try out the following example prompts based on the MCP server you have configure - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/agents/codex/+page.markdoc b/src/routes/docs/tooling/ai/agents/codex/+page.markdoc index d3899251ef1..5545d67a582 100644 --- a/src/routes/docs/tooling/ai/agents/codex/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/codex/+page.markdoc @@ -114,8 +114,8 @@ Try out the following example prompts based on the MCP server you have configure - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/agents/opencode/+page.markdoc b/src/routes/docs/tooling/ai/agents/opencode/+page.markdoc index ab5cf9bfddf..2b77c4e64d8 100644 --- a/src/routes/docs/tooling/ai/agents/opencode/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/opencode/+page.markdoc @@ -108,8 +108,8 @@ Try out the following example prompts based on the MCP server you have configure - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/agents/vscode/+page.markdoc b/src/routes/docs/tooling/ai/agents/vscode/+page.markdoc index 551f7939f7c..8d36c435d79 100644 --- a/src/routes/docs/tooling/ai/agents/vscode/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/vscode/+page.markdoc @@ -111,8 +111,8 @@ Open **Copilot Chat** in VS Code and switch to **Agent Mode** to test your MCP i - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/agents/windsurf/+page.markdoc b/src/routes/docs/tooling/ai/agents/windsurf/+page.markdoc index 8d22ff02593..1779be52ca6 100644 --- a/src/routes/docs/tooling/ai/agents/windsurf/+page.markdoc +++ b/src/routes/docs/tooling/ai/agents/windsurf/+page.markdoc @@ -112,8 +112,8 @@ Open Cascade chat in the Windsurf Editor and test your MCP integrations. You can - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/vibe-coding/claude-desktop/+page.markdoc b/src/routes/docs/tooling/ai/vibe-coding/claude-desktop/+page.markdoc index 6f300d9618c..6c43913419f 100644 --- a/src/routes/docs/tooling/ai/vibe-coding/claude-desktop/+page.markdoc +++ b/src/routes/docs/tooling/ai/vibe-coding/claude-desktop/+page.markdoc @@ -132,8 +132,8 @@ Try out the following example prompts based on the MCP server you have configure - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/ai/vibe-coding/emergent/+page.markdoc b/src/routes/docs/tooling/ai/vibe-coding/emergent/+page.markdoc index 54a749c8615..e4b66feda80 100644 --- a/src/routes/docs/tooling/ai/vibe-coding/emergent/+page.markdoc +++ b/src/routes/docs/tooling/ai/vibe-coding/emergent/+page.markdoc @@ -77,8 +77,8 @@ Once connected, you can use natural language to interact with Appwrite. Try prom - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} @@ -96,4 +96,4 @@ Once connected, you can use natural language to interact with Appwrite. Try prom {% /tabsitem %} {% /tabs %} -{% /section %} \ No newline at end of file +{% /section %} diff --git a/src/routes/docs/tooling/ai/vibe-coding/zenflow/+page.markdoc b/src/routes/docs/tooling/ai/vibe-coding/zenflow/+page.markdoc index 228da1ef771..014e4824e70 100644 --- a/src/routes/docs/tooling/ai/vibe-coding/zenflow/+page.markdoc +++ b/src/routes/docs/tooling/ai/vibe-coding/zenflow/+page.markdoc @@ -111,8 +111,8 @@ You can try out the following example prompts based on the MCP server you have c **Example prompts:** - `Create a new user in my Appwrite project` - `List all databases in my project` -- `Show me the collections in my database` -- `Create a new document in my collection` +- `Show me the tables in my database` +- `Create a new row in my table` - `Delete a specific user by ID` {% /tabsitem %} diff --git a/src/routes/docs/tooling/command-line/commands/+page.markdoc b/src/routes/docs/tooling/command-line/commands/+page.markdoc index 23dd2960870..cb3ca88b467 100644 --- a/src/routes/docs/tooling/command-line/commands/+page.markdoc +++ b/src/routes/docs/tooling/command-line/commands/+page.markdoc @@ -228,7 +228,7 @@ List commands across services accept a set of dedicated flags for the most commo * Return results before this cursor ID. Use for backward cursor pagination. --- * `--select <attribute>` -* Limit returned attributes on row and document list commands such as `tables-db list-rows`. Repeat the flag to include multiple attributes. +* Limit returned attributes on list commands such as `tables-db list-rows`. Repeat the flag to include multiple attributes. --- {% /table %} diff --git a/src/routes/docs/tooling/command-line/installation/+page.markdoc b/src/routes/docs/tooling/command-line/installation/+page.markdoc index 525b3984c87..3cb8ff3bfba 100644 --- a/src/routes/docs/tooling/command-line/installation/+page.markdoc +++ b/src/routes/docs/tooling/command-line/installation/+page.markdoc @@ -206,7 +206,7 @@ The CLI resolves resource paths relative to the file that defines the resource. The single-file format continues to work. You can split any supported resource arrays over time and keep other arrays in the root config. -Supported include keys are `functions`, `sites`, `databases`, `collections`, `tablesDB`, `tables`, `topics`, `teams`, `buckets`, `webhooks`, and `messages`. +Supported include keys are `functions`, `sites`, `databases`, `tablesDB`, `tables`, `topics`, `teams`, `buckets`, `webhooks`, and `messages`. Include paths must be local JSON files inside your project. They must be relative paths, end in `.json`, and cannot use parent-directory segments, absolute paths, URLs, URL-like schemes, null bytes, or JSON pointer fragments. diff --git a/src/routes/docs/tooling/command-line/tables/+page.markdoc b/src/routes/docs/tooling/command-line/tables/+page.markdoc index 9f3e9311cab..6519aecdc52 100644 --- a/src/routes/docs/tooling/command-line/tables/+page.markdoc +++ b/src/routes/docs/tooling/command-line/tables/+page.markdoc @@ -221,11 +221,11 @@ appwrite tables-db [COMMAND] [OPTIONS] * `create-relationship-column [options]` * Create relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). --- -* `create-string-column [options]` -* Create a string column. +* `create-text-column [options]` +* Create a text column. --- -* `update-string-column [options]` -* Update a string column. Changing the 'default' value will not update already existing rows. +* `update-text-column [options]` +* Update a text column. Changing the 'default' value will not update already existing rows. --- * `create-url-column [options]` * Create a URL column. diff --git a/src/routes/docs/tooling/terraform/resources/webhooks/+page.markdoc b/src/routes/docs/tooling/terraform/resources/webhooks/+page.markdoc index d993301adb0..b35d234df53 100644 --- a/src/routes/docs/tooling/terraform/resources/webhooks/+page.markdoc +++ b/src/routes/docs/tooling/terraform/resources/webhooks/+page.markdoc @@ -33,7 +33,7 @@ resource "appwrite_webhook" "user_events" { resource "appwrite_webhook" "authenticated" { name = "authenticated webhook" url = "https://api.example.com/webhooks/secure" - events = ["databases.*.collections.*.documents.*.create"] + events = ["tablesdb.*.tables.*.rows.*.create"] auth_username = "webhook" auth_password = var.webhook_password tls = true diff --git a/src/routes/docs/tutorials/nextjs/step-4/+page.markdoc b/src/routes/docs/tutorials/nextjs/step-4/+page.markdoc index 202b4ead8c4..db01c4e78ea 100644 --- a/src/routes/docs/tutorials/nextjs/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/nextjs/step-4/+page.markdoc @@ -55,7 +55,9 @@ export function useAuth() { }; const logout = async (): Promise<void> => { - await account.deleteSession('current'); + await account.deleteSession({ + sessionId: 'current' + }); setCurrent(null); router.push('/'); }; diff --git a/src/routes/docs/tutorials/nextjs/step-6/+page.markdoc b/src/routes/docs/tutorials/nextjs/step-6/+page.markdoc index db8cb33278c..83b947792c6 100644 --- a/src/routes/docs/tutorials/nextjs/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/nextjs/step-6/+page.markdoc @@ -78,11 +78,11 @@ export function useIdeas() { // Fetch the 10 most recent ideas from the database const fetch = async (): Promise<void> => { try { - const response = await tablesDB.listRows( + const response = await tablesDB.listRows({ databaseId, tableId, - [Query.orderDesc('$createdAt'), Query.limit(queryLimit)] - ); + queries: [Query.orderDesc('$createdAt'), Query.limit(queryLimit)] + }); setCurrent(response.rows as Idea[]); } catch (error) { console.error('Error fetching ideas:', error); @@ -94,17 +94,17 @@ export function useIdeas() { // Add new idea to the database const add = async (idea: Omit<Idea, '$id' | '$createdAt' | '$updatedAt' | '$permissions'>): Promise<void> => { try { - const response = await tablesDB.createRow( + const response = await tablesDB.createRow({ databaseId, tableId, - ID.unique(), - idea, - [ + rowId: ID.unique(), + data: idea, + permissions: [ Permission.read('any'), Permission.update(`user:${idea.userId}`), Permission.delete(`user:${idea.userId}`) ] - ); + }); setCurrent(prev => [response as Idea, ...prev].slice(0, queryLimit)); } catch (error) { console.error('Error adding idea:', error); @@ -113,7 +113,11 @@ export function useIdeas() { const remove = async (id: string): Promise<void> => { try { - await tablesDB.deleteRow(databaseId, tableId, id); + await tablesDB.deleteRow({ + databaseId, + tableId, + rowId: id + }); await fetch(); // Refetch ideas to ensure we have 10 items } catch (error) { console.error('Error removing idea:', error); @@ -134,4 +138,4 @@ export function useIdeas() { } ``` -Now we can call the `useIdeas()` hook from the home page. \ No newline at end of file +Now we can call the `useIdeas()` hook from the home page. diff --git a/src/routes/integrations/ai-hugging-face-image-classification/+page.markdoc b/src/routes/integrations/ai-hugging-face-image-classification/+page.markdoc index 66db050125a..74a0fc658c9 100644 --- a/src/routes/integrations/ai-hugging-face-image-classification/+page.markdoc +++ b/src/routes/integrations/ai-hugging-face-image-classification/+page.markdoc @@ -26,7 +26,7 @@ Image classification is a key application of machine learning that involves cate # How does the integration work? -You can utilize a pre-built Appwrite function template to add image classification with Hugging Face to your app. This will allow you to upload an image to an Appwrite storage bucket and store labels from the image in an Appwrite collection. +You can utilize a pre-built Appwrite function template to add image classification with Hugging Face to your app. This will allow you to upload an image to an Appwrite storage bucket and store labels from the image in an Appwrite table. # How to implement @@ -54,13 +54,13 @@ Then, create a new repository with the default branch and root directory setting ## Step 3: Test the function -Once all the steps are complete, it is time to test the function! Use the Appwrite console or one of Appwrite’s SDKs to [upload an image](https://appwrite.io/docs/references/cloud/client-web/storage#createFile) to the `image_classification` storage bucket. If successful, you will find a response saved in the `image_classification` collection in the `ai` database in the following format: +Once all the steps are complete, it is time to test the function! Use the Appwrite console or one of Appwrite’s SDKs to [upload an image](https://appwrite.io/docs/references/cloud/client-web/storage#createFile) to the `image_classification` storage bucket. If successful, you will find a response saved in the `image_classification` table in the `ai` database in the following format: | image | labels | | --- | --- | | `66a13bf100318c752f1b` | `[{"label":"llama","score":0.9664694666862488},{"label":"comic book","score":0.019199883565306664},{"label":"suit, suit of clothes","score":0.005637330934405327},{"label":"television, television system","score":0.00037627643905580044},{"label":"muzzle","score":0.00019786457414738834}]` | -The `image` attribute contains the ID of the image file uploaded to the `image_classification` storage bucket, and the `labels` attribute contains the response from Hugging Face, including all the labels and their confidence score. +The `image` column contains the ID of the image file uploaded to the `image_classification` storage bucket, and the `labels` column contains the response from Hugging Face, including all the labels and their confidence score. # Read more about Hugging Face and Appwrite Functions @@ -72,4 +72,4 @@ If you would like to learn more about Hugging Face and Appwrite Functions, we ha - [Building with Appwrite AI Function templates](https://appwrite.io/blog/post/building-with-ai-function-templates) - [Introducing the Python machine learning runtime](https://appwrite.io/blog/post/introducing-python-machine-learning-runtime) - [Find more function templates](https://appwrite.io/docs/products/functions/templates) -- [Appwrite Functions API reference](https://appwrite.io/docs/references) \ No newline at end of file +- [Appwrite Functions API reference](https://appwrite.io/docs/references) diff --git a/src/routes/integrations/ai-hugging-face-speech-recognition/+page.markdoc b/src/routes/integrations/ai-hugging-face-speech-recognition/+page.markdoc index 05a17f58ff6..090c8672ccd 100644 --- a/src/routes/integrations/ai-hugging-face-speech-recognition/+page.markdoc +++ b/src/routes/integrations/ai-hugging-face-speech-recognition/+page.markdoc @@ -26,7 +26,7 @@ Speech recognition is a transformative technology that converts spoken language # How does the integration work? -You can utilize a pre-built Appwrite function template to add speech recognition with Hugging Face to your app. This will allow you to upload an audio file to an Appwrite storage bucket and store the recognized speech in an Appwrite collection as text. +You can utilize a pre-built Appwrite function template to add speech recognition with Hugging Face to your app. This will allow you to upload an audio file to an Appwrite storage bucket and store the recognized speech in an Appwrite table as text. # How to implement @@ -54,13 +54,13 @@ Then, create a new repository with the default branch and root directory setting ## Step 3: Test the Function -Once all the steps are complete, it is time to test the function! Use the Appwrite console or one of Appwrite’s SDKs to [upload an audio file](https://appwrite.io/docs/references/cloud/client-web/storage#createFile) to the `speech_recognition` storage bucket. If successful, you will find a response saved in the `speech_recognition` collection in the `ai` database in the following format: +Once all the steps are complete, it is time to test the function! Use the Appwrite console or one of Appwrite’s SDKs to [upload an audio file](https://appwrite.io/docs/references/cloud/client-web/storage#createFile) to the `speech_recognition` storage bucket. If successful, you will find a response saved in the `speech_recognition` table in the `ai` database in the following format: | audio | speech | | --- | --- | | 66a7b386000a1042305c | my thought i have nobody by a beauty and will as you've poured mr rochester is sub and that so don't find simpus and devoted about to at might in a | -The `audio` attribute contains the ID of the audio file uploaded to the `speech_recognition` storage bucket, and the `speech` attribute contains the response from Hugging Face. +The `audio` column contains the ID of the audio file uploaded to the `speech_recognition` storage bucket, and the `speech` column contains the response from Hugging Face. # Read more about Hugging Face and Appwrite Functions @@ -70,4 +70,4 @@ If you would like to learn more about Hugging Face and Appwrite Functions, we ha - [Sign up for Hugging Face](https://huggingface.co/login) - [Set up the Hugging Face provider in Appwrite](https://appwrite.io/docs/products/ai/tutorials/object-detection) - [Find more function templates](https://appwrite.io/docs/products/functions/templates) -- [Appwrite Functions API reference](https://appwrite.io/docs/references) \ No newline at end of file +- [Appwrite Functions API reference](https://appwrite.io/docs/references) diff --git a/src/routes/integrations/lemon-squeezy-payments/+page.markdoc b/src/routes/integrations/lemon-squeezy-payments/+page.markdoc index 50aac51ab84..9244c0d50c2 100644 --- a/src/routes/integrations/lemon-squeezy-payments/+page.markdoc +++ b/src/routes/integrations/lemon-squeezy-payments/+page.markdoc @@ -80,7 +80,7 @@ Then, open it in your browser to test the function and access the pre-built inte ![Demo](/images/integrations/lemon-squeezy-payments/demo.avif) -You can visit the **Databases** page in your Appwrite project, enter the `orders` database and the `orders` collection within that, and find your orders. +You can visit the **Databases** page in your Appwrite project, enter the `orders` database and the `orders` table within that, and find your orders. ![Paid orders in database](/images/integrations/lemon-squeezy-payments/database.avif) diff --git a/src/routes/integrations/replication-rxdb/+page.markdoc b/src/routes/integrations/replication-rxdb/+page.markdoc index dc6552c87bf..268bdceaaf2 100644 --- a/src/routes/integrations/replication-rxdb/+page.markdoc +++ b/src/routes/integrations/replication-rxdb/+page.markdoc @@ -31,26 +31,26 @@ To implement the RxDB integration, there are several steps you must complete: ## Step 1: Configure Appwrite project -For this step, you must [create an account on Appwrite Cloud](https://cloud.appwrite.io/register) or [self-host Appwrite](https://appwrite.io/docs/advanced/self-hosting) if you haven’t already. Head over to the Appwrite console, 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 `mydb`, and then a collection with the ID `humans` (save both IDs for further usage). +For this step, you must [create an account on Appwrite Cloud](https://cloud.appwrite.io/register) or [self-host Appwrite](https://appwrite.io/docs/advanced/self-hosting) if you haven’t already. Head over to the Appwrite console, 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 `mydb`, and then a table with the ID `humans` (save both IDs for further usage). -Click on the **Attributes** tab and add the following attributes (schema used for demo purposes): +Click on the **Columns** tab and add the following columns (schema used for demo purposes): | Key | Type | Size | Required | | --- | --- | --- | --- | -| `name` | String | 100 | Yes | +| `name` | Text | 100 | Yes | | `age` | Integer | | Yes | -| `homeAddress` | String | 2000 | Yes | +| `homeAddress` | Text | 2000 | Yes | | `deleted` | Boolean | | Yes | -> **Note:** The `deleted` attribute is necessary to add because RxDB only soft deletes to prevent data loss in offline scenarios (no hard deletion of data occurs). +> **Note:** The `deleted` column is necessary to add because RxDB only soft deletes to prevent data loss in offline scenarios (no hard deletion of data occurs). -Then, head to the **Settings** tab of your collection, scroll down to the **Permissions** section, and the following: +Then, head to the **Settings** tab of your table, scroll down to the **Permissions** section, and the following: | Role | Create | Read | Update | Delete | | --- | --- | --- | --- | --- | | Any | Yes | Yes | Yes | Yes | -> **Note:** While RxDB does not allow you to manually configure permissions for each document, if your app uses Appwrite Auth and document-level permissions are enabled for your collection, the Appwrite SDK will automatically assign read and write permissions to the logged-in user for each document created. +> **Note:** While RxDB does not allow you to manually configure permissions for each row, if your app uses Appwrite Auth and row-level permissions are enabled for your table, the Appwrite SDK will automatically assign read and write permissions to the logged-in user for each row created. ## Step 2: Install Appwrite Web SDK and RxDB library @@ -66,7 +66,7 @@ In your app’s `.env` file, add the following: APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 APPWRITE_PROJECT_ID=your-project-id APPWRITE_DATABASE_ID=your-database-id -APPWRITE_COLLECTION_ID=your-collection-id +APPWRITE_TABLE_ID=your-table-id ``` Then, import all necessary libraries in your code: @@ -134,7 +134,7 @@ await db.addCollections({ const collection = db.humans; ``` -> **Note:** Please ensure that the names of your RxDB database and collection match the IDs of your Appwrite database and collection. +> **Note:** Please ensure that the names of your RxDB database and collection match the IDs of your Appwrite database and table. > ## Step 5: Start replication @@ -146,7 +146,7 @@ const replicationState = replicateAppwrite({ replicationIdentifier: 'my-appwrite-replication', client, databaseId: process.env.APPWRITE_DATABASE_ID, - collectionId: process.env.APPWRITE_COLLECTION_ID, + collectionId: process.env.APPWRITE_TABLE_ID, deletedField: 'deleted', // Field that represents deletion in Appwrite collection, pull: { @@ -173,4 +173,4 @@ If you would like to learn more about RxDB and Appwrite Databases, we have some - [RxDB docs for Appwrite](https://rxdb.info/replication-appwrite.html) - [Appwrite offline sync docs](/docs/products/databases/offline) - [Build an offline-first journal app with RxDB and Appwrite](/blog/post/offline-first-journal) -- [Offline-first journal demo app on GitHub](https://github.com/appwrite-community/offline-journal) \ No newline at end of file +- [Offline-first journal demo app on GitHub](https://github.com/appwrite-community/offline-journal) diff --git a/src/routes/integrations/search-algolia/+page.markdoc b/src/routes/integrations/search-algolia/+page.markdoc index 3d21bffd470..1d077acbad5 100644 --- a/src/routes/integrations/search-algolia/+page.markdoc +++ b/src/routes/integrations/search-algolia/+page.markdoc @@ -8,14 +8,14 @@ isPartner: true isNew: false cover: /images/integrations/search-algolia/cover.avif category: search -product: +product: avatar: '/images/integrations/avatars/algolia.avif' vendor: Algolia description: 'Algolia is a search platform that helps you add fast and relevant search capabilities to your app.' -platform: +platform: - 'Self-hosted' - 'Cloud' -images: +images: - /images/integrations/search-algolia/cover.avif - /images/integrations/search-algolia/overview.avif - /images/integrations/search-algolia/index.avif @@ -46,13 +46,13 @@ Then, click on the **Search** page from the left sidebar and **create a new Inde ## Step 2: Create the Appwrite Function -For this step, you must [create an account on Appwrite Cloud](https://cloud.appwrite.io/register) or [self-host Appwrite](https://appwrite.io/docs/advanced/self-hosting) if you haven’t already. In case you decide to self-host Appwrite, there are [additional setup steps](https://appwrite.io/docs/advanced/self-hosting/functions) to use Appwrite Function templates. Additionally, for this template, you must have an Appwrite Database and Collection filled with data in advance. +For this step, you must [create an account on Appwrite Cloud](https://cloud.appwrite.io/register) or [self-host Appwrite](https://appwrite.io/docs/advanced/self-hosting) if you haven’t already. In case you decide to self-host Appwrite, there are [additional setup steps](https://appwrite.io/docs/advanced/self-hosting/functions) to use Appwrite Function templates. Additionally, for this template, you must have an Appwrite database and table filled with data in advance. Head over to the Appwrite console, navigate to the **Functions** page, click the **Templates** tab, and search for the **Algolia** function template. ![Function template](/images/integrations/search-algolia/template.avif) -During the setup process, click on the checkbox to create an Appwrite API key on completion and add the Appwrite Database ID and Collection ID for the data you want to index and search as well as the **Algolia application ID**, **search API key**, **write API key** (under Admin API Key), and **index ID** in the **Variables** step. If you are self-hosting Appwrite, click on the **optional variables** dropdown and update the Appwrite endpoint to your instance’s publicly accessible endpoint. +During the setup process, click on the checkbox to create an Appwrite API key on completion and add the Appwrite database ID and table ID for the data you want to index and search as well as the **Algolia application ID**, **search API key**, **write API key** (under Admin API Key), and **index ID** in the **Variables** step. If you are self-hosting Appwrite, click on the **optional variables** dropdown and update the Appwrite endpoint to your instance’s publicly accessible endpoint. ![Env variables](/images/integrations/search-algolia/variables.avif) @@ -62,16 +62,16 @@ Then, create a new repository with the default branch and root directory setting Once all the steps are complete, it is time to test the function! Before we go further, visit the **Domains** tab on the **Functions** page and copy the domain URL to test the function. -Send a POST (HTTP) Request to the function URL to index the Appwrite Collection in Algolia. +Send a POST (HTTP) Request to the function URL to index the Appwrite table in Algolia. ```bash curl -XPOST https://DEPLOYED_FUNCTION_DOMAIN ``` -In case you have a large collection of data and your function executions timeout, you can head to the **Settings** tab on the **Functions** page and increase the function timeout limit from **15 seconds** to **900 seconds** (maximum). - +In case you have a large table of data and your function executions timeout, you can head to the **Settings** tab on the **Functions** page and increase the function timeout limit from **15 seconds** to **900 seconds** (maximum). + Then, open your function URL in the browser to try the interactive search UI. - + ![Demo](/images/integrations/search-algolia/demo.avif) @@ -81,4 +81,4 @@ If you would like to learn more about Algolia, we have some resources that you s - [Sign up for Algolia](https://dashboard.algolia.com/users/sign_up) - [Learn more about Functions templates in Appwrite docs](https://appwrite.io/docs/products/functions/templates) -- [Connect with other developers and the Appwrite team on Discord.](https://discord.com/invite/appwrite) \ No newline at end of file +- [Connect with other developers and the Appwrite team on Discord.](https://discord.com/invite/appwrite) diff --git a/src/routes/integrations/stripe-payments/+page.markdoc b/src/routes/integrations/stripe-payments/+page.markdoc index 2347b295f05..c4f16263db2 100644 --- a/src/routes/integrations/stripe-payments/+page.markdoc +++ b/src/routes/integrations/stripe-payments/+page.markdoc @@ -88,7 +88,7 @@ Then, open it in your browser to test the function and access the pre-built inte ![Demo](/images/integrations/stripe-payments/demo.avif) -You can visit the **Databases** page in your Appwrite project, enter the `orders` database and the `orders` collection within that, and find your orders. +You can visit the **Databases** page in your Appwrite project, enter the `orders` database and the `orders` table within that, and find your orders. ![Appwrite database](/images/integrations/stripe-payments/database.avif) @@ -97,4 +97,4 @@ You can visit the **Databases** page in your Appwrite project, enter the `orders If you would like to learn more about Stripe Payments and Appwrite Functions, we have some resources that you should visit: - [Sign up on Stripe](https://stripe.com) -- [Learn more about Functions templates in Appwrite docs](https://appwrite.io/docs/products/functions/templates) \ No newline at end of file +- [Learn more about Functions templates in Appwrite docs](https://appwrite.io/docs/products/functions/templates)