diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml
index 9a6ba173d2..48e1fb1c28 100644
--- a/.github/workflows/staging.yml
+++ b/.github/workflows/staging.yml
@@ -2,9 +2,6 @@ name: Staging deployment
on:
workflow_dispatch:
- push:
- branches:
- - main
concurrency:
group: ${{ github.workflow }}
diff --git a/.optimize-cache.json b/.optimize-cache.json
index ef218fa5e1..ae72c4c101 100644
--- a/.optimize-cache.json
+++ b/.optimize-cache.json
@@ -256,6 +256,7 @@
"static/images/blog/anthropic-just-launched-claude-fable-5-and-claude-mythos-5/cover.png": "45721aba1b005e1b02d2b585c69d3e48227bea22b8c7253aaeda2ce67bb79b1d",
"static/images/blog/anthropic-just-launched-claude-for-small-business/cover.png": "9b89a91dab0f56e4d49777ca4a370c558edca339e573c155d6c1838f04a1dbd6",
"static/images/blog/anthropic-just-launched-claude-opus-48-with-fast-mode-and-dynamic-workflows/cover.png": "0fba6f3f51a4fc24f02ac6c14b1f098277bd8a8d3e555c024b3dca0b29d20167",
+ "static/images/blog/anthropic-launches-free-claude-access-for-us-k-12-teachers/cover.png": "5af971e028a2acca04c08342ab035a422b5eea67367d0a71c33eeedac50f280c",
"static/images/blog/apply-appwrite-how/cover.png": "d23f45ced245b42c8712c021f5d2068c17aebd94fd049cb90222cb9647a41a4a",
"static/images/blog/appwrite-1-8-0-self-hosted-release/cover.png": "c15a9d88ccd16c2dc8333dc74e715e1f4a6c7818d3b4a05f4d68342eacdc0523",
"static/images/blog/appwrite-1-8-1-self-hosted-release/cover.png": "82f0a396c56b6b299b24133079acc6a317c66b2bf02fd91f4862bd3be0f8f373",
@@ -633,6 +634,7 @@
"static/images/blog/february-and-march-product-update-realtime-queries-appwrite-skills-and-new-database-features/Introducing_Realtime_queries.png": "0b632e9ceac3a763f5ba2ed50ab54fffe389fde34fcdfb7ad99599ec7ae83b9e",
"static/images/blog/february-and-march-product-update-realtime-queries-appwrite-skills-and-new-database-features/Relationship_queries.png": "2d9772691f05b1be3ec6cb954d77babc10d5666c9994fe3975d2a078facc87b8",
"static/images/blog/file-tokens.png": "23d2fa4a88db2d9548f43f95df15b5ca60d512481570ed2d1b3d66ce1b1f504f",
+ "static/images/blog/file-vs-object-vs-block-storage-whats-the-difference/cover.png": "441cc26959c276c917e3da173c31aec6893b1209a609954f1f981836efa3800b",
"static/images/blog/firebase-vs-open-source-tradeoffs/cover.png": "1be4185b4fa90c5f37e96003a74b8b55c31956d0258c70000fdec0990f2496eb",
"static/images/blog/first-pr.png": "f369419a756ccb2c784dac916d79a1cc33317fa4c43f37c7f41ae62bf0a603dd",
"static/images/blog/first-pr1.png": "256144fd88d0564c239cb73c16882113e4fe2cb23156fcbc4f15f6f70437faaf",
@@ -699,6 +701,9 @@
"static/images/blog/google-oauth-expo/login-successful.png": "441ef69e3ba8716c4ff4c20dc8c31f8f3f418010a11ff1e74585645bad414f74",
"static/images/blog/gpt-5-5-launch/card-problem.png": "9cfd1fa44c5f8d76a54904d1e656595f1580721d498952a96105dd6f9ddc82fd",
"static/images/blog/gpt-5-5-launch/cover.png": "768138556cc8a3c1645ff1c602ea1ac5ba2eb373f8f642d2c023e6380950ec08",
+ "static/images/blog/gpt-56-is-here-openais-efficient-frontier-model/cover.png": "2156c5047fa2630e148ea0fd5189c4d08579b5c96e92530c4ae45b642acb6589",
+ "static/images/blog/grok-45-coding-model/cover.png": "b40f87952b940767f393a93a7628f8277119862a3a3ac7b7d40d7ee5679213ff",
+ "static/images/blog/grok-build-open-source/cover.png": "b6550e801f952ba0446227c7f9295d3e5beb12e5d7f9e62547c5a3ffa78e674f",
"static/images/blog/hacktoberfest-2023.png": "e0521ba26e59dacf91072391f7eb0df0e724457961fec4cb04a4914b31dd0024",
"static/images/blog/hacktoberfest2023-swag.png": "7a0af270f7fe24c53e2497ca59bead84255d2d2e882dd38473b1bc6bb0a22d1f",
"static/images/blog/handle-cors-in-serverless-functions/cover.png": "9db00c07cedfbc97f7c4a1a79d1ce1f0d31adab3ea246935ac9bdc9f47fc1e4c",
@@ -825,6 +830,11 @@
"static/images/blog/introducing-python-machine-learning-runtime/cover.png": "2976f7c650ab992ade016df1767c8b783da8173f8a3bbe10549fe4d3e4767536",
"static/images/blog/introducing-python-machine-learning-runtime/create-function.png": "0728b8ed66cd81e826d2d0a5b70e9f572d415f995065cf44feed2e22ce50e492",
"static/images/blog/introducing-python-machine-learning-runtime/settings-timeout.png": "84214c72048b9911dfb2b9b40b62dd715f856c01badae58b148066be137fdfb5",
+ "static/images/blog/june-product-update-react-library-password-strength-baa-and-more/Appwrite_plugin_for_Claude_Code_is_now_in_the_official_Claude_marketplace.jpg": "f8679e1c03a01ccc2a3a1d3709d609ee8dbd9af63e9548cdcc336059457dae3c",
+ "static/images/blog/june-product-update-react-library-password-strength-baa-and-more/cover.png": "b636e349b4ecc0d569967d449ddb665e3ae1a6ed554224b1a643e4efab18d251",
+ "static/images/blog/june-product-update-react-library-password-strength-baa-and-more/introducing_self-serve_baa.png": "4d4ed038ee7e94249857342f6d317b31dbb5b44410941fa14f6280e0bc8d938b",
+ "static/images/blog/june-product-update-react-library-password-strength-baa-and-more/Introducing_the_Appwrite_React_library.png": "f69d9adae872ab44368fd32159ece9daf621cdf002ab78461f7f1d07b66dba73",
+ "static/images/blog/june-product-update-react-library-password-strength-baa-and-more/Password_Strength.png": "bc6a351aa59b845d6a9513ec4a4993c393b3059c9a7d324c999bea52142d8648",
"static/images/blog/kcollect.png": "d044d69e18de7dd4af75213b7f4cf131529f43875352d7db86fe80c274466242",
"static/images/blog/local-development-guide/localdev-cover.png": "07cce5e779991c560d2e8c58b280803ea8ebcfc8775bcc75e959d76b01409fa7",
"static/images/blog/logo.png": "6df582f74767156f37ced895ebeb6b5a656858301dd09539cbc84157a6a54ed3",
@@ -1258,15 +1268,20 @@
"static/images/blog/vibe-coding-vs-traditional-development/cover.png": "ed973e32ed844c5bb24dff7946cb531d7cfc74e31fee5cd7208391f4feb6fc5d",
"static/images/blog/webp-support-for-safari/cover.png": "ea4e965ffe21500f3552073bb7ca325d453020cf095d67164329edbda3f1c799",
"static/images/blog/what-developers-actually-want-from-a-backend-platform/cover.png": "0c540d48b12cd7031e3cadaf4223086ded946b42dc283c641cfa024311b2ec36",
+ "static/images/blog/what-is-a-storage-bucket/cover.png": "3dc114305196a7061b9bf55035c3a72470091df8b60096ea4fbe5606705a349b",
"static/images/blog/what-is-an-ai-backend/cover.png": "cb36f49035cbdcd97a70ac658783741f275d3a220b7cfd16b39d4fb86a929edd",
"static/images/blog/what-is-cdn/cover.png": "ef77860288e150c6c22f3950a5eae4c88aefefb6db204f10c2a0544e51548703",
"static/images/blog/what-is-ciam/cover.png": "45a5261ae1bb8a38777f60a21ea60426c0832e3d58bf3164100548400d388ce1",
"static/images/blog/what-is-cicd-a-complete-guide-for-developers/cover.png": "7abddce55b1467188faab83abd58189173bf9aba84de3d9f28fff0be8c6e9276",
+ "static/images/blog/what-is-cloud-storage-an-expert-guide-for-developers/cover.png": "b7f545dbe9334d60f214e748ddfcea47484530a97f30ecf579d064a053c2821d",
"static/images/blog/what-is-docker-a-simple-guide-for-developers/cover.png": "acd9c50ad749fcf676dd58b38cc6bbffba913bf5d817c6b725bd2c305088689e",
+ "static/images/blog/what-is-kubernetes-an-expert-guide-for-developers/cover.png": "a7601f375841b143d62511fe3bbfb4bacb6989ddc56613f772b7dcd3d5e90688",
"static/images/blog/what-is-mcp/claude-mcp-chat.png": "26842cfebca3ec2cec89448e1c0d7ddb3f5421cc57acdb8780d48d30a54cad82",
"static/images/blog/what-is-mcp/claude-mcp-tools.png": "3a5ae700867b8671b5c9e3af61b094aeb64611168463db66ff440e0d427ac6bc",
"static/images/blog/what-is-mcp/cover.png": "dc4537990c91d6f1768c5ab8775e5c52239eb901b15e2e74fce8b5a018855c32",
+ "static/images/blog/what-is-object-storage-a-beginners-guide/cover.png": "77f961972c0249f5cc77634e68b4c0b6b285887cc62a25114a5af4b5906a6a3c",
"static/images/blog/what-is-redis-a-complete-guide-for-developers/cover.png": "b7b87a372bbb99421c2ab6df37430da960728b5cf339f1803c432644154c764f",
+ "static/images/blog/what-is-serverless-an-expert-guide-for-developers/cover.png": "fd88e32613ca877625cd69e0e38ba76c3c2cc73da05b789739928712bdd7b454",
"static/images/blog/when-custom-backend-stops-being-worth-it/cover.png": "d03b13c4e8f3294823a7883cdae89ca18a4030b170c51f597bd139c9ca274793",
"static/images/blog/why-ai-generated-apps-need-backend/cover.png": "8761878c13c51dd8a720a625606b89b93d9c56651aed636fa1b2bd346bd4fd82",
"static/images/blog/why-developers-choose-appwrite-auth/cover.png": "f56c37ebfc25191e113b928ff3cf144563be740159e46d75a427bdafdd11214b",
diff --git a/src/partials/auth-security.md b/src/partials/auth-security.md
index bd1eb8c5a0..0763f3bd89 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/security/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 strength {% #password-strength %}
diff --git a/src/routes/(marketing)/(components)/hero-banner.svelte b/src/routes/(marketing)/(components)/hero-banner.svelte
index 960d2aa359..2559951291 100644
--- a/src/routes/(marketing)/(components)/hero-banner.svelte
+++ b/src/routes/(marketing)/(components)/hero-banner.svelte
@@ -12,7 +12,12 @@
const { title, href, icon = 'sparkle' }: Props = $props();
-
+
{#if icon === 'mongo'}
diff --git a/src/routes/(marketing)/(components)/hero.svelte b/src/routes/(marketing)/(components)/hero.svelte
index 6f1984b881..3c16d4c5d9 100644
--- a/src/routes/(marketing)/(components)/hero.svelte
+++ b/src/routes/(marketing)/(components)/hero.svelte
@@ -152,16 +152,16 @@
>
{#if layoutAside}
{:else}
{/if}
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 acbef39aa7..2d38c962b6 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
@@ -3,6 +3,7 @@ layout: post
title: "25 startup ideas you can build with vibe coding"
description: "25 concrete startup ideas a solo founder can build with vibe coding tools like Cursor, Claude Code, and Lovable, plus the backend primitives each one needs."
date: 2026-05-11
+lastUpdated: 2026-06-29
cover: /images/blog/25-startup-ideas-you-can-build-with-vibe-coding/cover.avif
timeToRead: 12
author: aditya-oberai
@@ -150,7 +151,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 488dcb0193..6a4d18ffb1 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
@@ -3,6 +3,7 @@ layout: post
title: "3 things you can build with the Go runtime"
description: Explore a few use cases of how to leverage Go's speed in your app.
date: 2024-08-22
+lastUpdated: 2026-06-29
cover: /images/blog/building-with-go-cover.avif
timeToRead: 8
author: aditya-oberai
@@ -16,7 +17,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 +39,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 +65,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 +152,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 +192,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 +200,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 +290,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 +308,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 +340,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 +348,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 +359,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 982a613f03..b2b3585eb5 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
@@ -3,6 +3,7 @@ layout: post
title: 7 prompting mistakes you need to stop making right now
description: Avoid these 7 common prompting mistakes that lead to poor AI outputs. Learn how to write better prompts and get more accurate and reliable results.
date: 2026-04-27
+lastUpdated: 2026-06-29
cover: /images/blog/7-prompting-mistakes-you-need-to-stop-making-right-now/cover.avif
timeToRead: 5
author: aishwari
@@ -104,7 +105,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 cbe6fe825e..2de5006282 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
@@ -3,6 +3,7 @@ layout: post
title: "7 practical steps to achieve GDPR compliance for your startup"
description: All you need to know about GDPR compliance for startups, the do's, don'ts and best practices.
date: 2024-09-25
+lastUpdated: 2026-06-29
cover: /images/blog/7-steps-gdpr-startups/cover.avif
timeToRead: 4
author: aditya-oberai
@@ -16,7 +17,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 294ae13e7a..c9b24a6f6d 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
@@ -3,6 +3,7 @@ layout: post
title: 7 things Claude can do that will blow your mind
description: Discover 7 things Claude can do that most people don't use. From better prompts to smarter workflows, learn how to get more out of your AI.
date: 2026-04-27
+lastUpdated: 2026-06-29
cover: /images/blog/7-things-claude-can-do-that-will-blow-your-mind/cover.avif
timeToRead: 5
author: aishwari
@@ -17,7 +18,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 +65,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 4093fa2388..60745436b9 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
@@ -5,22 +5,23 @@ description: Learn how to add a search function to your application.
cover: /images/blog/add-a-search-function-to-your-app/cover.avif
timeToRead: 5
date: 2023-11-17
+lastUpdated: 2026-06-29
author: haimantika-mitra
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 `tablesdb..tables..rows.*.create`, `tablesdb..tables..rows.*.update`, and `tablesdb..tables..rows.*.delete` 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 +30,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 +64,12 @@ Here’s the keys you need:

-- `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 +77,7 @@ If you want to see the source code, you can find it on our [templates GitHub re

-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 +98,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 `tablesdb..tables..rows.*.create`, `tablesdb..tables..rows.*.update`, and `tablesdb..tables..rows.*.delete` events to automatically index new rows
- Using weights and other meilisearch features to optimise search such as excluding certain fields from indexing
Some examples are:
@@ -109,6 +110,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](/docs/products/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.
+3. **[Appwrite Discord](/discord)**: 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 4adfdecbf2..1e9d8b70c9 100644
--- a/src/routes/blog/post/adding-url-shortener-function/+page.markdoc
+++ b/src/routes/blog/post/adding-url-shortener-function/+page.markdoc
@@ -5,6 +5,7 @@ description: Learning how to add the URL shortener function template to your app
cover: /images/blog/adding-url-shortener/cover.avif
timeToRead: 5
date: 2023-11-08
+lastUpdated: 2026-06-29
author: haimantika-mitra
category: tutorial
featured: false
@@ -14,13 +15,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 +69,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](/docs/products/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.
+3. **[Appwrite Discord](/discord)**: 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 e0069082e6..3e89425a36 100644
--- a/src/routes/blog/post/agency-backend-standardization/+page.markdoc
+++ b/src/routes/blog/post/agency-backend-standardization/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "How agencies standardize backend stacks across clients"
description: Why development agencies benefit from standardizing their backend stack and how to choose a platform flexible enough to serve diverse client needs.
date: 2026-03-16
+lastUpdated: 2026-06-29
cover: /images/blog/agency-backend-standardization/cover.avif
timeToRead: 6
author: aditya-oberai
@@ -59,7 +60,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 d785fae047..867bfc9eae 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
@@ -5,6 +5,7 @@ description: Explore Agentic AI vs Generative AI with clear examples and key dif
cover: /images/blog/agentic-ai-vs-generative-ai/cover.avif
timeToRead: 10
date: 2025-10-23
+lastUpdated: 2026-06-29
author: veeresh-mulge
callToAction: true
unlisted: true
@@ -17,7 +18,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 69377ac672..20737486a9 100644
--- a/src/routes/blog/post/ai-crystal-ball/+page.markdoc
+++ b/src/routes/blog/post/ai-crystal-ball/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Predicting your developer destiny: how I built the AI Crystal Ball"
description: How I used Appwrite, OpenAI, GitHub, and SvelteKit to develop an AI prank app.
date: 2024-05-10
+lastUpdated: 2026-06-29
cover: /images/blog/ai-crystal-ball/cover.avif
timeToRead: 9
author: aditya-oberai
@@ -17,7 +18,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 +34,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 +73,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 | - | - | Yes |
| followers | Integer | - | Yes | - |
| following | Integer | - | Yes | - |
-| username | String | 255 | Yes | - |
+| username | Varchar | 255 | Yes | - |
-Visit the collection settings, enable **Document security,** and set the following (collection-level) **Permissions**:
+Visit the table settings, enable **Row security,** and set the following (table-level) **Permissions**:
| Role | Create | Read | Update | Delete |
| --- | --- | --- | --- | --- |
| Users | Yes | - | - | - |
-#### The `destiny` collection
+#### The `destiny` table
-Create the collection and add the following attributes:
+Create the table and add the following columns:
| Key | Type | Size | Required |
| --- | --- | --- | --- |
-| destiny | String | 25000 | Yes |
-| username | String | 255 | Yes |
+| destiny | MediumText | - | Yes |
+| username | Varchar | 255 | Yes |
-Visit the collection settings, enable **Document security,** and set the following (collection-level) **Permissions**:
+Visit the table settings, enable **Row security,** and set the following (table-level) **Permissions**:
| Role | Create | Read | Update | Delete |
| --- | --- | --- | --- | --- |
@@ -136,15 +137,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 +153,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 +282,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/databases.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 +306,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,
@@ -511,47 +512,41 @@ Since our UI has already been prepared in the previous step, we need not make an
## Storing and sharing the destiny
-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:
+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 db = {
+ // Existing getUserData and addUserData functions
+
+ 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 +619,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 4e84d95308..1bf29d71f4 100644
--- a/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc
+++ b/src/routes/blog/post/ai-vibe-coding-insights/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Exploring AI and vibe coding: Insights from the Appwrite developer community"
description: Survey insights on how developers are using AI, vibe coding tools, and the Appwrite MCP server.
date: 2025-08-25
+lastUpdated: 2026-06-29
cover: /images/blog/ai-vibe-coding-insights/cover.avif
timeToRead: 10
author: laura-du-ry
@@ -13,7 +14,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 +98,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 473f97220b..42ee460cac 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
@@ -3,6 +3,7 @@ layout: post
title: "Announcing an improved Appwrite Databases experience. A completely new look and feel"
description: Appwrite Databases just got the most significant update to date. We proudly present new terminology, a new TablesDB UI, and a supporting TablesDB API, all built to make working with databases simpler and faster.
date: 2025-08-26
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-appwrite-databases-new-ui/cover.avif
timeToRead: 5
author: darshan-pandya
@@ -60,7 +61,7 @@ Alongside these terminology updates, the **TablesDB API** introduces new method
| `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.
+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.
Here’s an example of how you can use the new SDK methods:
@@ -86,11 +87,12 @@ async function main() {
name: ''
});
- const nameColumn = await tablesDB.createStringColumn({
+ const nameColumn = await tablesDB.createVarcharColumn({
databaseId: database.$id,
tableId: table.$id,
key: 'name',
- size: 255
+ size: 255,
+ required: true
});
const row = await tablesDB.createRow({
@@ -106,7 +108,7 @@ 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.
+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.
@@ -129,7 +131,7 @@ Watch this [product tour](https://www.youtube.com/watch?v=b80BXqj5CJE) to learn
**For the new terminology and TablesDB API:**
-The existing `Collections` API will continue to function as expected. The new `TablesDB` API introduces a parallel, relational model experience.
+The existing `Collections` API will continue to function as expected. The new `TablesDB` API introduces a parallel, 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 ba23c4cf70..f1276fdd9c 100644
--- a/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc
+++ b/src/routes/blog/post/announcing-appwrite-integration-catalog/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Appwrite's new Integrations Catalog"
description: We are beyond excited to announce the launch of our brand-new Appwrite Integrations Catalog.
date: 2024-07-29
+lastUpdated: 2026-06-29
cover: /images/blog/integrations-catalog.avif
timeToRead: 12
author: aditya-oberai
@@ -18,7 +19,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 b375b0dbd7..b5073cfb6d 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
@@ -3,6 +3,7 @@ layout: post
title: Appwrite is now HIPAA compliant
description: This is another step towards building a platform for developers from all industries.
date: 2024-04-11
+lastUpdated: 2026-06-29
cover: /images/blog/hipaa.avif
timeToRead: 4
author: may-ender
@@ -13,7 +14,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 91ad258820..8563efc1d4 100644
--- a/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc
+++ b/src/routes/blog/post/announcing-appwrite-messaging/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Messaging: Push, Email and SMS"
description: You can now send emails, SMS and push notifications with Appwrite’s easy-to-use APIs.
date: 2024-02-26
+lastUpdated: 2026-06-29
cover: /images/blog/messaging-announcement.avif
timeToRead: 6
author: eldad-fux
@@ -16,7 +17,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 c7c77d39b6..ac905bcc1f 100644
--- a/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc
+++ b/src/routes/blog/post/announcing-atomic-numeric-operations/+page.markdoc
@@ -1,8 +1,9 @@
---
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
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-atomic-numeric-operations/cover.avif
timeToRead: 5
author: jake-barnby
@@ -10,33 +11,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 +55,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 f86f655e1b..d621814244 100644
--- a/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc
+++ b/src/routes/blog/post/announcing-auto-increment-support/+page.markdoc
@@ -1,37 +1,38 @@
---
layout: post
-title: "Announcing Auto-increment support: Built-in numeric sequencing for your documents"
-description: Get reliable, sequential ordering across your collections with fast, indexed auto-increment IDs.
+title: "Announcing Auto-increment support: Built-in numeric sequencing for your rows"
+description: Get reliable, sequential ordering across your tables with fast, indexed auto-increment IDs.
date: 2025-07-15
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-auto-increment-support/cover.avif
timeToRead: 5
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.
+Previously, maintaining consistent insertion order and generating numeric identifiers required either manual increments, complex logic, or dependence on timestamps, which weren't always accurate or reliable. Appwrite’s new Auto-increment support feature solves these issues seamlessly, providing a built-in numeric identifier that increases predictably with every new row added.
Whether you're creating paginated data sets, managing invoice numbers, logging activities, or building real-time feeds, Appwrite's Auto-increment support feature offers effortless numeric sequencing. This means less manual work, fewer bugs, and significantly improved reliability.
@@ -50,21 +51,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 +78,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 90933b2658..d021f61450 100644
--- a/src/routes/blog/post/announcing-bigint-columns/+page.markdoc
+++ b/src/routes/blog/post/announcing-bigint-columns/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing BigInt columns: Store 64-bit integers for counters, IDs, and timestamps"
description: A new column type for values that overflow the 32-bit integer range, with the same constraints, defaults, and atomic operations you already use.
date: 2026-05-12
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-bigint-columns/cover.avif
timeToRead: 5
author: arnab-chatterjee
@@ -182,7 +183,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 307096690c..bc45c8b052 100644
--- a/src/routes/blog/post/announcing-bulk-api/+page.markdoc
+++ b/src/routes/blog/post/announcing-bulk-api/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Bulk API: Handle heavy data workloads with ease"
description: Perform multiple database operations in a single API call. Enjoy faster writes and better performance for heavy server-side workloads.
date: 2025-07-03
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-bulk-api/cover.avif
timeToRead: 5
author: jake-barnby
@@ -10,14 +11,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 (one row at a time), 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 +28,9 @@ We're excited to introduce another Appwrite Databases feature, **Bulk API**. Exp
# Faster development with bulk actions
-Previously, writing or modifying large amounts of data in Appwrite Databases required sending one request per document. This method was inefficient, slow, and resource-intensive, especially when dealing with thousands of records.
+Previously, writing or modifying large amounts of data in Appwrite Databases required sending one request per row. This method was inefficient, slow, and resource-intensive, especially when dealing with thousands of records.
-With the new Bulk API, you can create, update, or delete multiple documents in one go, vastly speeding up your workflows and reducing network overhead.
+With the new Bulk API, you can create, update, or delete multiple rows in one go, vastly speeding up your workflows and reducing network overhead.
# Optimized for server-side workloads
@@ -49,12 +50,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 +66,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 077ddd6efc..ddc0e4c9e5 100644
--- a/src/routes/blog/post/announcing-csv-export/+page.markdoc
+++ b/src/routes/blog/post/announcing-csv-export/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing CSV exports: Effortless data extraction, right from your Console"
description: Export your data to a CSV file directly from the Appwrite Console. Filter, customize, and download your data in just a few clicks.
date: 2026-02-04
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-csv-export/cover.avif
timeToRead: 5
author: jake-barnby
@@ -14,7 +15,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 +59,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 54974be0c3..6ead788ec1 100644
--- a/src/routes/blog/post/announcing-csv-imports/+page.markdoc
+++ b/src/routes/blog/post/announcing-csv-imports/+page.markdoc
@@ -1,8 +1,9 @@
---
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
+lastUpdated: 2026-06-29
# update this cover later, once available!
cover: /images/blog/announcing-csv-imports/cover.avif
timeToRead: 5
@@ -10,42 +11,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 %}
-
+
-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 +61,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 487699609f..a9c9a4f6bb 100644
--- a/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc
+++ b/src/routes/blog/post/announcing-database-ai-suggestions/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Database AI suggestions: from table name to schema in one click"
description: We’re introducing new AI features into Appwrite Databases that generate complete schemas and indexes from just a table name.
date: 2025-11-28
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-database-ai-suggestions/cover.avif
timeToRead: 5
author: darshan-pandya
@@ -18,11 +19,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 +52,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 11a63dabba..e7a856ff69 100644
--- a/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc
+++ b/src/routes/blog/post/announcing-database-reads-and-writes-pricing/+page.markdoc
@@ -3,7 +3,7 @@ layout: post
title: Announcing Database Reads and Writes pricing
description: To ensure Appwrite Cloud's sustainability, we are introducing pricing for database read and write operations, effective April 10th, 2025.
date: 2025-03-13
-lastUpdated: 2026-05-22
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-database-reads-and-writes-pricing/cover.avif
timeToRead: 6
author: eldad-fux
@@ -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 e5c52237fc..60bfc40534 100644
--- a/src/routes/blog/post/announcing-database-upsert/+page.markdoc
+++ b/src/routes/blog/post/announcing-database-upsert/+page.markdoc
@@ -1,8 +1,9 @@
---
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
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-database-upsert/cover.avif
timeToRead: 5
author: jake-barnby
@@ -10,26 +11,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 +50,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 +78,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 97e9c7fa77..1ef96a1a42 100644
--- a/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc
+++ b/src/routes/blog/post/announcing-encrypted-string-attributes/+page.markdoc
@@ -1,9 +1,9 @@
---
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
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-encrypted-string-attributes/cover.avif
timeToRead: 5
author: jake-barnby
@@ -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 024c6367a6..05eebcb90f 100644
--- a/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc
+++ b/src/routes/blog/post/announcing-full-schema-creation/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Full Schema Creation: Provision complete tables in one atomic call"
description: Create a table, all its columns, and indexes synchronously, ready to use instantly, with no background jobs.
date: 2025-12-30
+lastUpdated: 2026-06-29
cover: /images/blog/full-schema.avif
timeToRead: 5
author: aditya-oberai
@@ -28,7 +29,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.
@@ -58,7 +59,7 @@ const table = await tablesDB.createTable({
},
{
key: 'name',
- type: 'string',
+ type: 'varchar',
size: 255,
required: true
},
diff --git a/src/routes/blog/post/announcing-go-support/+page.markdoc b/src/routes/blog/post/announcing-go-support/+page.markdoc
index cdf3da51d9..c6269c384d 100644
--- a/src/routes/blog/post/announcing-go-support/+page.markdoc
+++ b/src/routes/blog/post/announcing-go-support/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Introducing Go, high-performance Functions runtime and SDK"
description: We’re excited to announce that Go runtime is now supported in Appwrite Functions and we have added a Go SDK.
date: 2024-08-22
+lastUpdated: 2026-06-29
cover: /images/blog/go-cover.avif
timeToRead: 7
author: matej-baco
@@ -229,7 +230,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 +239,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 +262,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 +277,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 d03780bded..20128ff82f 100644
--- a/src/routes/blog/post/announcing-inversion-queries/+page.markdoc
+++ b/src/routes/blog/post/announcing-inversion-queries/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing inversion queries: Exclusion rules made simple"
description: Adding five new NOT operators to let you exclude what you don’t need while keeping queries efficient.
date: 2025-09-04
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-inversion-queries/cover.avif
timeToRead: 5
author: jake-barnby
@@ -22,7 +23,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.
@@ -38,11 +39,11 @@ With inversion queries, you can express these cases directly in your database ca
They include:
-- `notContains`: Find rows where the attribute does not contain the given substring
-- `notSearch`: Find rows where the attribute does not match the search query
-- `notBetween`: Find rows where the attribute is not between two given values
-- `notStartsWith`: Find rows where the attribute does not start with the given value
-- `notEndsWith`: Find rows where the attribute does not end with the given value
+- `notContains`: Find rows where the column does not contain the given substring
+- `notSearch`: Find rows where the column does not match the search query
+- `notBetween`: Find rows where the column is not between two given values
+- `notStartsWith`: Find rows where the column does not start with the given value
+- `notEndsWith`: Find rows where the column does not end with the given value
# Why does this matter?
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 0e720a9747..6c08459b85 100644
--- a/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc
+++ b/src/routes/blog/post/announcing-realtime-channel-helpers/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Realtime Channel helpers: Type-safe subscriptions made simple"
description: Build realtime subscriptions faster with a fluent, chainable API that reduces errors and improves code clarity.
date: 2026-02-13
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-realtime-channel-helpers/cover.avif
timeToRead: 5
author: jake-barnby
@@ -10,7 +11,7 @@ category: announcement
featured: false
faqs:
- question: "What are Realtime channel helpers in Appwrite?"
- answer: "Channel helpers are a fluent, type-safe API for building Appwrite Realtime channel strings without manual concatenation. Instead of writing strings like `databases..tables..rows.`, you chain methods on the `Channel` class and the SDK builds the correct string with IDE autocomplete and compile-time checks."
+ answer: "Channel helpers are a fluent, type-safe API for building Appwrite Realtime channel strings without manual concatenation. Instead of writing strings like `tablesdb..tables..rows.`, you chain methods on the `Channel` class and the SDK builds the correct string with IDE autocomplete and compile-time checks."
- question: "How does Appwrite Realtime work?"
answer: "[Appwrite Realtime](/docs/apis/realtime) opens a single WebSocket connection per client and streams events for every channel that client subscribes to. When data changes in your project, Appwrite emits an event and pushes it to every subscribed client over that connection, so you do not need to poll."
- question: "Which channels does Appwrite Realtime support?"
@@ -23,7 +24,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.
@@ -57,7 +58,7 @@ const subscription = await realtime.subscribe(
);
```
-Instead of writing `databases..tables..rows.`, the helper builds the correct string for you while providing autocomplete and validation every step of the way.
+Instead of writing `tablesdb..tables..rows.`, the helper builds the correct string for you while providing autocomplete and validation every step of the way.
# Flexible and composable
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 f9405ce931..435098be9b 100644
--- a/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc
+++ b/src/routes/blog/post/announcing-time-helper-queries/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing time helper queries: Cleaner, more expressive time-based queries"
description: Handle time-based filtering directly in your queries using new helpers for created and updated timestamps.
date: 2025-09-02
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-time-helper-queries/cover.avif
timeToRead: 5
author: jake-barnby
@@ -11,7 +12,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 +27,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 +67,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 0f3645afe1..9033d9a4c9 100644
--- a/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc
+++ b/src/routes/blog/post/announcing-timestamp-overrides/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Timestamp Overrides: Accurate timelines for production workloads"
description: Move historical data into Appwrite without losing context or disrupting chronological accuracy.
date: 2025-08-13
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-timestamp-overrides/cover.avif
timeToRead: 5
author: jake-barnby
@@ -10,11 +11,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 +27,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 +53,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 +71,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 a8b37e52c3..95c4d7793a 100644
--- a/src/routes/blog/post/announcing-transactions-api/+page.markdoc
+++ b/src/routes/blog/post/announcing-transactions-api/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Announcing Transactions API: Reliable multi-record writes across tables"
description: Appwrite introduces a new Databases feature called Transactions API. This new feature allows developers to group multiple operations across tables and commit them in a single atomic action, ensuring data consistency and eliminating partial writes.
date: 2025-10-09
+lastUpdated: 2026-06-29
cover: /images/blog/announcing-transactions-api/cover.avif
timeToRead: 5
author: jake-barnby
@@ -56,7 +57,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 741b217a7d..3f1d5201b0 100644
--- a/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc
+++ b/src/routes/blog/post/announcing-type-generation-feature/+page.markdoc
@@ -1,28 +1,29 @@
---
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
+lastUpdated: 2026-06-29
cover: /images/blog/type-generation-feature/cover.avif
timeToRead: 4
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 +37,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/anthropic-launches-free-claude-access-for-us-k-12-teachers/+page.markdoc b/src/routes/blog/post/anthropic-launches-free-claude-access-for-us-k-12-teachers/+page.markdoc
new file mode 100644
index 0000000000..f57ecc4c76
--- /dev/null
+++ b/src/routes/blog/post/anthropic-launches-free-claude-access-for-us-k-12-teachers/+page.markdoc
@@ -0,0 +1,115 @@
+---
+layout: post
+title: Anthropic launches free Claude access for US K-12 teachers
+description: Anthropic just introduced Claude for Teachers, giving verified US K-12 educators free Claude, teaching skills, and aligned curricula.
+date: 2026-07-15
+cover: /images/blog/anthropic-launches-free-claude-access-for-us-k-12-teachers/cover.avif
+timeToRead: 5
+author: aishwari
+category: ai
+featured: false
+faqs:
+ - question: Is Claude for Teachers free?
+ answer: Yes. Verified US K-12 educators get it entirely free, with a full year of access if they sign up by June 30, 2027.
+ - question: Who can use Claude for Teachers?
+ answer: "Verified K-12 educators in the US. It follows Claude's 18-and-over policy, so it's designed for teachers, not students."
+ - question: Is student data used to train Claude?
+ answer: No. Data from Claude for Teachers is not used for model training, and student information is protected under a K-12 Data Processing Addendum written to comply with FERPA.
+ - question: Can schools and districts get it?
+ answer: A dedicated offering for schools and districts is coming soon. For now, Claude for Teachers is for individual educators, and districts can use Claude for Nonprofits.
+ - question: What resources are available for education developers?
+ answer: Anthropic released new connectors, an open-source teaching-skills repository, and a technical write-up explaining how the skills were evaluated. Education developers can use these resources when creating classroom tools and connectors, then pair their application with backend services such as authentication, databases, storage, and server-side functions.
+---
+On July 14, 2026, Anthropic launched [Claude for Teachers](https://www.anthropic.com/news/claude-for-teachers), giving verified K-12 educators in the US free access to premium Claude capabilities, a library of teaching skills, and evidence-based curricula mapped to academic standards in all 50 states.
+
+If you teach, or you build the tools that teachers use, this launch matters. It ships a classroom-ready version of Claude, but it also opens up new connectors, an open-source skills repository, and a technical write-up for anyone building in education. Here's what shipped, how it works, and where developers fit in.
+
+# What is Claude for Teachers?
+
+**Claude for Teachers is a free offering for verified US K-12 educators that pairs premium Claude capabilities with a curated set of teaching skills and access to evidence-based, standards-aligned curricula.** It's built for the day-to-day work of teaching, including planning lessons, differentiating materials, and making sense of class data, without the planning spilling into every evening.
+
+Once an educator is verified, they get access to Claude with the Learning Commons connector and a set of tailored teaching skills grounded in learning science. The offering also includes Claude Code and Claude Cowork, so Claude can carry longer tasks forward on its own rather than stopping at a single reply.
+
+# Why Anthropic built Claude for Teachers
+
+Decades of research show that practices like differentiation, mastery-based learning, and small group instruction reliably improve student achievement. The problem is rarely knowing what works. It's finding the time and resources to do it. Budgets are tight, classes are often too large to meet every student individually, and planning eats into evenings. That strain falls hardest on under-resourced schools.
+
+Anthropic frames Claude for Teachers as a way to close the gap between educational best practice and what a teacher's week actually allows. The framing is backed by an important nuance in the early evidence: while the impact of AI tools *for students* is mixed and depends heavily on implementation, AI tools *for teachers* can strengthen instructional practice and improve student outcomes. Claude for Teachers is aimed squarely at the teacher's craft, so educators can protect what they value most, which is time with their students.
+
+# What Claude for Teachers connects to
+
+Anthropic connects Claude to academic standards and established curricular resources to help teachers create more relevant, standards-aligned materials.
+
+At the center is **Learning Commons**, which gives Claude access to academic standards across all 50 states, and beneath each standard, the smaller learning competencies it's built from and the order students typically learn them. So when Claude drafts a lesson, the plan is scaffolded and aligned to teaching standards rather than generic. On top of that, Claude for Teachers brings in trusted curricular resources like [OpenSciEd](https://www.openscied.org/) and IM v.360 from [Illustrative Mathematics](https://www.illustrativemathematics.org/).
+
+Educators can also connect Claude across an entire ecosystem of K-12 tools:
+
+* **ASSISTments** generates auto-scored, standards-aligned math problems for practice and assessment.
+* **Brisk Teaching** turns ideas into interactive student activities and standards-aligned lessons in seconds.
+* **Canva Education** turns lesson materials into classroom-ready designs and interactive experiences.
+* **Coteach** creates high-quality math diagrams grounded in K-12 curriculum.
+* **Diffit** creates and adapts instructional materials for every student.
+* **Eedi** generates diagnostic questions that reveal student thinking, in English and Spanish.
+* **MagicSchool** makes instructional content classroom-ready.
+* **Snorkl** surfaces insights about classes, assignments, and student progress to inform instruction.
+* **TeachFX** gives personalized instructional feedback grounded in real classroom talk.
+
+# How does Claude for Teachers work?
+
+**After verification, a K-12 educator gets Claude with the Learning Commons connector and a set of teaching skills co-developed with Learning Commons and grounded in learning science.** Those skills were designed around the tasks teachers said mattered most, evaluated for rigor and classroom usability, and refined with feedback from real classrooms, including schools like Prospect Schools in Brooklyn.
+
+Two skills anchor the experience.
+
+**Plan a lesson from high-quality instructional materials.** Ask for a lesson and Claude draws on widely used curricula mapped to your state's standards, plus the fine-grained learning components and progressions beneath them. It drafts a plan and student-facing materials you can revise and take straight into class.
+
+**Differentiate for every learner in your room.** Ask Claude to adapt materials for students at different readiness levels, and it builds a differentiation plan plus personalized student-facing materials for each proficiency level. Scaffolds make the material accessible, while students who are ready get further challenge.
+
+Because Claude for Teachers includes Claude Code and Cowork, it can also run longer, multi-step work:
+
+* **Analyze class data to plan instruction.** Hand Claude a folder of data, such as a roster, diagnostics, attendance, and your own notes, and it builds a clear picture of where every student is, so you can tailor instruction. You control what's shared, and nothing you share is used in model training.
+* **Schedule repeated tasks.** Hand off a task once, like reviewing each day's exit tickets and adapting tomorrow's plan to match, and it runs every school day at 4pm. Claude works while you drive home.
+
+# How does Claude for Teachers handle student data?
+
+Anthropic says Claude for Teachers is educator-only, includes terms designed for K-12 privacy, and protects student information through a K-12 Data Processing Addendum written to comply with FERPA. Anthropic also says data submitted through Claude for Teachers is not used for model training.
+
+The offering follows Claude's 18-and-over policy, so it's for teachers rather than students. Anthropic is also working with the [American Federation of Teachers](https://www.aft.org/) to align its terms and privacy practices with a developing "Gold Standard" for the sector.
+
+> "We've been working with Anthropic on a Gold Standard that sets out industry best practices for safety and privacy in K-12 education," said Randi Weingarten, President of the American Federation of Teachers. "It's important that Anthropic is committing to these principles in their new Claude for Teachers, a tool designed by and for educators to assist them instructionally and hopefully give them more time for the human relationships at the heart of learning."
+
+# AI fluency training for teachers
+
+Alongside the product, Anthropic released **AI Fluency for K-12 Teachers**, a course co-created with [Teach for America](https://www.teachforamerica.org/), plus a train-the-trainer module co-created with the American Federation of Teachers. The guidance is model-agnostic, Creative Commons-licensed, and practical, covering which classroom tasks AI is suited for and how to use it responsibly with students. Because it's model-agnostic and openly licensed, it's useful even to educators and schools not using Claude.
+
+# Public goods for the education community, and where builders come in
+
+This is the part that should interest developers. As part of Anthropic's Beneficial Deployments mission, Claude for Teachers ships with a set of open resources for anyone building in education:
+
+* **New connectors** published in Anthropic's directory.
+* **An open-source repository** of the teaching skills.
+* **A technical write-up** of how the skills were evaluated, so other builders can apply the same approach.
+
+Anthropic is also piloting an evaluation of Claude for Teachers in the **Detroit Public Schools Community District** to study impact on educator wellbeing and practice, and the work ties into its partnership with the [Gates Foundation](https://www.gatesfoundation.org/) to co-develop tools that improve K-12 outcomes. On top of that, **Playlab** will support a national network of lab schools, helping educators become *builders* of the AI tools used in their classrooms.
+
+That last point is particularly relevant for developers. It points toward a growing ecosystem of connectors, skills, and purpose-built tools created by educators and EdTech teams. Many of those tools will need authentication, data storage, file handling, and server-side workflows.
+
+# How to get started with Claude for Teachers
+
+**Once verified, US K-12 educators get Claude for Teachers entirely free.** Sign up by **June 30, 2027** for a full year of access. Claude for Teachers is for individual educators today, and a dedicated offering for schools and districts is coming soon. In the meantime, districts interested in Claude can use Claude for Nonprofits.
+
+# What this means if you build education tools on Appwrite
+
+If you're building an education connector, a classroom app, or a Claude-powered agent, the model is only half the stack. Agents and apps still need authentication, stored state, file persistence, and server-side logic between steps. Wiring that up by hand is exactly what slows teams down on the path from prototype to product. Education tools may handle sensitive information, so teams should avoid assuming that the use of any particular backend or AI service automatically satisfies institutional or regulatory requirements. Teams should independently assess their institution's privacy, security, data residency, retention, and regulatory requirements before deploying any backend.
+
+That's where [Appwrite](/) fits. Appwrite gives your app a production backend out of the box: [Auth](/auth) for teacher and admin accounts, [Databases](/databases) for rosters and progress data, [Storage](/storage) for lesson materials and student uploads, and [Functions](/functions) for the server-side logic your agent triggers between steps.
+
+If you want a Claude-powered agent to stand up that backend without manually assembling infrastructure, the **Appwrite plugin for Claude Code** bundles the Appwrite API MCP server, the Appwrite Docs MCP server, and SDK-specific agent skills into a single install. With the appropriate project access and permissions, Claude Code can interact with Appwrite APIs and documentation while helping developers implement services such as Auth, Databases, Storage, Sites, and Functions. It's the same pattern we covered when [Claude Fable 5 returned with stronger jailbreak safeguards](/blog/post/claude-fable-5-returns-with-stronger-jailbreak-safeguards).
+
+So if you're building for teachers rather than teaching yourself, you can [start for free on Appwrite Cloud](https://cloud.appwrite.io/), explore the [integrations directory](/integrations), or join the conversation on the [Appwrite Discord](https://appwrite.io/discord). Claude can help you build the classroom experience, while Appwrite provides the backend services needed to move it toward production.
+
+# Resources
+
+* [Start building on Appwrite Cloud](https://cloud.appwrite.io/)
+* [Appwrite integrations directory](/integrations)
+* [Appwrite docs](/docs)
+* [Join the Appwrite Discord](https://appwrite.io/discord)
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 5c88fd005d..32a3b10105 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
@@ -4,6 +4,7 @@ title: "Appwrite 1.8.0: The most powerful self-hosted release yet"
description: Appwrite 1.8.0 brings powerful database features, new runtimes, and key performance updates to make your self-hosted setup faster, safer, and more scalable.
cover: /images/blog/appwrite-1-8-0-self-hosted-release/cover.avif
date: 2025-10-31
+lastUpdated: 2026-06-29
timeToRead: 5
author: steven
category: announcement
@@ -11,7 +12,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 introduces tables, columns, and rows alongside the existing collections and documents model, 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,7 +36,7 @@ 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.
@@ -60,7 +61,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 20fac797fc..19d059cf8d 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
@@ -3,6 +3,7 @@ layout: post
title: Appwrite 1.5 is now available on Cloud
description: This release was highly anticipated by the community, and we're very happy to finally share the great news.
date: 2024-04-29
+lastUpdated: 2026-06-29
cover: /images/blog/appwrite-1.5-now-available-on-cloud/cloud15.avif
timeToRead: 8
author: eldad-fux
@@ -217,9 +218,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 +230,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 873f2e6dde..6b1f138dcb 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
@@ -3,6 +3,7 @@ layout: post
title: "Appwrite for hackathons: Build fast, ship faster"
description: Learn how Appwrite helps hackathon teams go from idea to working prototype in hours, not days.
date: 2026-03-30
+lastUpdated: 2026-06-29
cover: /images/blog/appwrite-for-hackathons-build-fast-ship-faster/cover.avif
timeToRead: 5
author: aditya-oberai
@@ -85,7 +86,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 1a27f49e61..8f6faab77d 100644
--- a/src/routes/blog/post/appwrite-functions-guide/+page.markdoc
+++ b/src/routes/blog/post/appwrite-functions-guide/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Appwrite Functions: everything you need to know"
description: A complete guide to Appwrite Functions covering triggers, execution modes, deployment options, permissions, and practical use cases for server-side logic.
date: 2026-03-24
+lastUpdated: 2026-06-29
cover: /images/blog/appwrite-functions-guide/cover.avif
timeToRead: 5
author: aditya-oberai
@@ -137,7 +138,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 cf32465c96..b10153595d 100644
--- a/src/routes/blog/post/appwrite-indexes/+page.markdoc
+++ b/src/routes/blog/post/appwrite-indexes/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Appwrite Indexes: how to speed up your database queries"
description: Learn what indexes are in Appwrite Databases, which types to use, when to create them, and when adding more indexes actually hurts performance.
date: 2026-03-26
+lastUpdated: 2026-06-29
cover: /images/blog/appwrite-indexes/cover.avif
timeToRead: 5
author: aditya-oberai
@@ -13,13 +14,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 +56,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 +68,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 +87,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 +101,9 @@ await tablesDB.createIndex({
tableId: "",
key: "email_unique",
type: "unique",
- attributes: ["email"],
+ columns: ["email"],
orders: ["ASC"],
+ lengths: [255],
});
```
@@ -134,8 +137,9 @@ await tablesDB.createIndex({
tableId: "",
key: "status_author_index",
type: "key",
- attributes: ["status", "authorId"],
+ columns: ["status", "authorId"],
orders: ["ASC", "ASC"],
+ lengths: [32, 36],
});
```
@@ -165,7 +169,7 @@ Indexes and the Query API work as a pair. The Query API expresses what data you
Before putting a table into production, review every query pattern you will run on it and verify the needed indexes are in place. Adding indexes retroactively on large tables takes time and temporarily affects performance during the build phase.
-The [Appwrite Databases queries reference](/docs/products/databases/queries) rows every `Query` method and makes it clear which ones require or benefit from specific index types. Keep it open when designing your schema.
+The [Appwrite Databases queries reference](/docs/products/databases/queries) lists every `Query` method and makes it clear which ones require or benefit from specific index types. Keep it open when designing your schema.
# Build faster queries with the right indexes
diff --git a/src/routes/blog/post/appwrite-query-api/+page.markdoc b/src/routes/blog/post/appwrite-query-api/+page.markdoc
index be23431ca0..e517fd6da9 100644
--- a/src/routes/blog/post/appwrite-query-api/+page.markdoc
+++ b/src/routes/blog/post/appwrite-query-api/+page.markdoc
@@ -3,6 +3,7 @@ layout: post
title: "Appwrite's Query API: filtering, sorting, and pagination"
description: Learn how to use Appwrite's Query API to filter, sort, and paginate database results efficiently, including offset and cursor pagination.
date: 2026-03-25
+lastUpdated: 2026-06-29
cover: /images/blog/appwrite-query-api/cover.avif
timeToRead: 5
author: aditya-oberai
@@ -180,7 +181,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 d3eb6939d1..cb7bfc381a 100644
--- a/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc
+++ b/src/routes/blog/post/appwrite-realtime-for-flutter/+page.markdoc
@@ -7,16 +7,16 @@ timeToRead: 6
date: 2024-09-02
author: damodar-lohani
category: tutorial
-lastUpdated: 2025-10-14
+lastUpdated: 2026-06-29
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,23 @@ 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.
-
+
-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
-- Size: 25
+- Type: Text
+- Column Key: name
- Default: null
- Required: true
- Array: false
- 
+ 
-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.

@@ -96,7 +95,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 +183,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 +205,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