diff --git a/.optimize-cache.json b/.optimize-cache.json index 4b6b96d03be..94a2b5f5399 100644 --- a/.optimize-cache.json +++ b/.optimize-cache.json @@ -199,6 +199,8 @@ "static/images/blog/announcing-appwrite-rust-sdk/cover.png": "b5b69e19f4fc1ac5eb985016c1b058bb9ed308680197f315f3b9b861f3377310", "static/images/blog/announcing-atomic-numeric-operations/cover.png": "9fbe339856b8040eb49fb01ca4353b5600609962ab8044dcfc34ffbe0e8a9738", "static/images/blog/announcing-auto-increment-support/cover.png": "83a7b1dd0e31ae86e49fa873cde0b6c0319a552b27dc447b4a214efd7f3fd534", + "static/images/blog/announcing-bigint-columns/console-create-bigint.png": "21e815c2f7d297ef6f544c71f17680367391e35e36e809b3c71029397c6706d7", + "static/images/blog/announcing-bigint-columns/cover.png": "de0ec57085646427a552f35f0b1ab9a77f9e38b9b20801329ac19bc4bdb069f7", "static/images/blog/announcing-bulk-api/cover.png": "78a0fdf3b54ce8412bf6be1880eb4a42bcac2cb04420045a60bce888c8999c6f", "static/images/blog/announcing-bun-and-dart/bun-and-dart.png": "ea94af00a5c035cefb6474a9061441803db0160b7cc194fb5a71fbe05abf0d12", "static/images/blog/announcing-cname-flattening/cover.png": "3d79ccb4a7f1bac9e32e0456e31fe09f4475e87368e582591799445a7d4a6f3a", diff --git a/src/routes/blog/author/arnab-chatterjee/+page.markdoc b/src/routes/blog/author/arnab-chatterjee/+page.markdoc index 53806130991..bb63078323b 100644 --- a/src/routes/blog/author/arnab-chatterjee/+page.markdoc +++ b/src/routes/blog/author/arnab-chatterjee/+page.markdoc @@ -2,7 +2,7 @@ layout: author name: Arnab Chatterjee slug: arnab-chatterjee -role: Engineering Intern +role: Platform Engineer bio: Breaking, fixing, and building two tools at a time. avatar: /images/avatars/arnab-chatterjee.avif twitter: https://x.com/arnabch20k diff --git a/src/routes/blog/post/announcing-bigint-columns/+page.markdoc b/src/routes/blog/post/announcing-bigint-columns/+page.markdoc new file mode 100644 index 00000000000..90933b26583 --- /dev/null +++ b/src/routes/blog/post/announcing-bigint-columns/+page.markdoc @@ -0,0 +1,486 @@ +--- +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 +cover: /images/blog/announcing-bigint-columns/cover.avif +timeToRead: 5 +author: arnab-chatterjee +category: announcement +featured: false +callToAction: true +faqs: + - question: "What is a BigInt column in Appwrite Databases?" + answer: "BigInt is a column type that stores 64-bit signed integers natively. It accepts any value from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, which is roughly 4 billion times the range of the 32-bit integer column. Use it whenever a numeric value can exceed the ±2.1 billion limit of a regular integer." + - question: "When should I use BigInt instead of integer?" + answer: "Stick with the integer type if your values will always fit in the 32-bit signed range. Reach for BigInt when you expect values larger than about 2.1 billion in either direction, when you import IDs from systems that use 64-bit keys, when a counter or accumulator grows unbounded over time, or when you store high-resolution timestamps. If the upper bound is uncertain, BigInt is the safer default." + - question: "Does BigInt work with Appwrite's Operators?" + answer: "Yes. BigInt columns work with every numeric operator, including Operator.increment, Operator.decrement, Operator.multiply, and Operator.divide. The change is applied atomically on the server under concurrency control, respects any min or max bounds you set on the column, and returns the updated value without needing to fetch the row first." + - question: "Can I migrate an existing integer column to BigInt?" + answer: "There is no automatic migration between integer and BigInt today. The recommended approach is to add a new BigInt column, backfill data from the existing integer column, and then drop the old column once the cutover is complete. Picking BigInt up front when the upper bound is unclear saves a future schema change." + - question: "Which Appwrite SDKs support BigInt columns?" + answer: "BigInt is available in every Server SDK that ships with Appwrite, including Node.js, Python, PHP, Ruby, Dart, Kotlin, Java, Swift, .NET, Go, and Rust. Each SDK exposes a createBigIntColumn (or language-equivalent) method on the TablesDB service, with optional min, max, and default parameters." + - question: "Where can I create a BigInt column from the Appwrite Console?" + answer: "Open the table you want to extend, head to the Columns tab, and click Create column. Pick BigInt from the type dropdown, give the column a key, and optionally set min, max, and default values. The column is ready to read and write the moment the request returns." +--- + +A 32-bit signed integer caps out at roughly **-2.1 billion** on the low end and **+2.1 billion** on the high end. The moment your column needs to hold a value outside that range, in either direction, the existing integer type stops being enough. + +To close this gap, we’re introducing **BigInt columns** in Appwrite Databases. + +BigInt is a new column type that stores 64-bit signed integers natively, with the same `min`, `max`, and `default` parameters you already use with regular integer columns. Previously, the only way to store 64-bit values was to set `size: 8` on an integer column, which was easy to miss and felt out of place on a type called integer. BigInt makes the intent explicit: if your data fits in a 32-bit integer, you don’t need to change anything; if it doesn’t, BigInt is the column type you reach for. + +# What BigInt gives you + +A BigInt column accepts any value in the 64-bit signed integer range, from **-9,223,372,036,854,775,808** to **9,223,372,036,854,775,807**. That’s roughly 4 billion times the range of a 32-bit integer column, and enough to cover the data types where 32-bit was always the wrong size: + +- **Large counters and accumulators**: lifetime views, total bytes processed, cumulative API calls, retry counters that keep climbing across years of operation. +- **External IDs**: account IDs, message IDs, or transaction IDs imported from systems that use 64-bit keys. +- **High-resolution timestamps**: nanosecond or microsecond epochs, monotonic clocks, sequence numbers from event logs. +- **Financial values in minor units**: balances, totals, or ledger entries stored as integers in the smallest currency unit, where you want to avoid floating-point rounding entirely. + +# Works with Operators + +BigInt columns plug straight into [Operators](/docs/products/databases/operators), so you can update a 64-bit value directly on the server without fetching the row first. `Operator.increment`, `Operator.decrement`, and the rest of the numeric operators treat BigInt the same way they treat integer and float columns: the change is applied atomically under concurrency control, respects any `min` or `max` bounds you set, and returns the new value. + +This means a counter like `total_views` can sit at 12 billion, get hammered with concurrent `+1` requests across regions, and stay consistent without any client-side coordination. + +{% multicode %} + +```server-nodejs +const result = await tablesDB.updateRow({ + databaseId: '', + tableId: '', + rowId: '', + data: { + total_views: sdk.Operator.increment(1) + } +}); +``` + +```server-python +result = tables_db.update_row( + database_id = '', + table_id = '', + row_id = '', + data = { 'total_views': Operator.increment(1) } +) +``` + +```server-php +$result = $tablesDB->updateRow( + databaseId: '', + tableId: '', + rowId: '', + data: [ 'total_views' => Operator::increment(1) ] +); +``` + +```server-ruby +result = tables_db.update_row( + database_id: '', + table_id: '', + row_id: '', + data: { 'total_views' => Operator.increment(1) } +) +``` + +```server-dart +await tablesDB.updateRow( + databaseId: '', + tableId: '', + rowId: '', + data: { + 'total_views': Operator.increment(1) + }, +); +``` + +```server-kotlin +val response = tablesDB.updateRow( + databaseId = "", + tableId = "", + rowId = "", + data = mapOf("total_views" to Operator.increment(1)) +) +``` + +```server-java +tablesDB.updateRow( + "", + "", + "", + Map.of("total_views", Operator.increment(1)), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` + +```server-swift +_ = try await tablesDB.updateRow( + databaseId: "", + tableId: "", + rowId: "", + data: [ + "total_views": Operator.increment(1) + ] +) +``` + +```server-dotnet +await tablesDB.UpdateRow( + databaseId: "", + tableId: "", + rowId: "", + data: new Dictionary + { + { "total_views", Operator.Increment(1) } + } +); +``` + +```server-go +_, err := tablesDB.UpdateRow( + "", + "", + "", + tablesDB.WithUpdateRowData(map[string]any{ + "total_views": operator.Increment(1), + }), +) +``` + +```server-rust +tables_db.update_row( + "", + "", + "", + Some(json!({ + "total_views": operator::increment_by(1) + })), + None, + None, +).await?; +``` + +{% /multicode %} + +# Creating a BigInt column + +You can create a BigInt column from the Appwrite Console or programmatically using any of the [Server SDKs](/docs/sdks#server). + +## From the Console + +In the Appwrite Console, open your table, head to the **Columns** tab, and click **Create column**. Pick **BigInt** from the type dropdown, give it a key, and optionally set `min`, `max`, and `default`. The column is ready to read and write the moment the request returns. + +![Creating a BigInt column in the Appwrite Console](/images/blog/announcing-bigint-columns/console-create-bigint.avif) + +## 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). + +{% multicode %} + +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const tablesDB = new sdk.TablesDB(client); + +const result = await tablesDB.createBigIntColumn({ + databaseId: '', + tableId: '', + key: 'total_views', + required: false, + min: 0, + max: 9223372036854775807n, + xdefault: 0 +}); +``` + +```server-python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +tables_db = TablesDB(client) + +result = tables_db.create_big_int_column( + database_id = '', + table_id = '', + key = 'total_views', + required = False, + min = 0, + max = 9223372036854775807, + default = 0 +) +``` + +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$tablesDB = new TablesDB($client); + +$result = $tablesDB->createBigIntColumn( + databaseId: '', + tableId: '', + key: 'total_views', + required: false, + min: 0, + max: 9223372036854775807, + default: 0 +); +``` + +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +tables_db = TablesDB.new(client) + +result = tables_db.create_big_int_column( + database_id: '', + table_id: '', + key: 'total_views', + required: false, + min: 0, + max: 9223372036854775807, + default: 0 +) +``` + +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +TablesDB tablesDB = TablesDB(client); + +ColumnBigint result = await tablesDB.createBigIntColumn( + databaseId: '', + tableId: '', + key: 'total_views', + xrequired: false, + min: 0, + max: 9223372036854775807, + xdefault: 0, +); +``` + +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.TablesDB + +val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val tablesDB = TablesDB(client) + +val response = tablesDB.createBigIntColumn( + databaseId = "", + tableId = "", + key = "total_views", + required = false, + min = 0, + max = 9223372036854775807, + default = 0 +) +``` + +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.TablesDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +tablesDB.createBigIntColumn( + "", // databaseId + "", // tableId + "total_views", // key + false, // required + 0L, // min + 9223372036854775807L, // max + 0L, // default + false, // array + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); + }) +); +``` + +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let tablesDB = TablesDB(client) + +let columnBigint = try await tablesDB.createBigIntColumn( + databaseId: "", + tableId: "", + key: "total_views", + required: false, + min: 0, + max: 9223372036854775807, + default: 0 +) +``` + +```server-dotnet +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +TablesDB tablesDB = new TablesDB(client); + +ColumnBigint result = await tablesDB.CreateBigIntColumn( + databaseId: "", + tableId: "", + key: "total_views", + required: false, + min: 0, + max: 9223372036854775807, + default: 0 +); +``` + +```server-go +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/tablesdb" +) + +func main() { + cli := client.New( + client.WithEndpoint("https://.cloud.appwrite.io/v1"), + client.WithProject(""), + client.WithKey(""), + ) + + service := tablesdb.New(cli) + + response, err := service.CreateBigIntColumn( + "", + "", + "total_views", + false, + service.WithCreateBigIntColumnMin(0), + service.WithCreateBigIntColumnMax(9223372036854775807), + service.WithCreateBigIntColumnDefault(0), + ) + if err != nil { + panic(err) + } + fmt.Println(response) +} +``` + +```server-rust +use appwrite::Client; +use appwrite::services::TablesDB; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .set_project("") // Your project ID + .set_key(""); // Your secret API key + + let tables_db = TablesDB::new(&client); + + let result = tables_db.create_big_int_column( + "", + "", + "total_views", + false, + Some(0), + Some(9_223_372_036_854_775_807i64), + Some(0), + Some(false), + ).await?; + + println!("{:?}", result); + + Ok(()) +} +``` + +{% /multicode %} + +# When to use BigInt over integer + +The simple rule: if the values your column holds will always fit in 32-bit signed range (-2,147,483,648 to 2,147,483,647), stick with integer. It’s a smaller type, slightly cheaper to store, and matches the most common case. + +Reach for BigInt when any of the following is true: + +- You expect values larger than ~2.1 billion at any point in the column’s lifetime. +- You’re importing IDs from a system that already uses 64-bit keys. +- The column stores cumulative metrics that grow unbounded over time. +- You want headroom for a counter that might run for years across many users. + +There is no automatic migration between integer and BigInt, so picking the right type up front saves you a schema change later. If you’re unsure, BigInt is a safe default for any counter, ID, or timestamp where the upper bound isn’t obvious. + +# Get started + +BigInt columns are available on **Appwrite Cloud** today. + +You can create your first BigInt column directly from the Console, or roll it out programmatically through any of the Server SDKs above. The new column type works with every TablesDB feature you already use, including Operators, indexes, and full schema creation. + +# More resources + +- [Read the Databases documentation](/docs/products/databases/tables#columns) +- [Announcing Database Operators](/blog/post/announcing-db-operators) +- [Announcing Full Schema Creation](/blog/post/announcing-full-schema-creation) diff --git a/src/routes/changelog/(entries)/2026-05-12.markdoc b/src/routes/changelog/(entries)/2026-05-12.markdoc new file mode 100644 index 00000000000..3d86cb45f08 --- /dev/null +++ b/src/routes/changelog/(entries)/2026-05-12.markdoc @@ -0,0 +1,14 @@ +--- +layout: changelog +title: "Store 64-bit integers with BigInt columns" +date: 2026-05-12 +cover: /images/blog/announcing-bigint-columns/cover.avif +--- + +Appwrite Databases now supports **BigInt** columns, giving you a 64-bit signed integer type alongside the existing 32-bit `integer`. Use BigInt when your values may exceed the ±2.1 billion range of a regular integer, for example large counters, high-resolution timestamps, or external IDs from systems that use 64-bit keys. + +BigInt columns accept optional `min`, `max`, and `default` parameters and work with [Operators](/docs/products/databases/operators) for atomic server-side updates, just like regular integers. + +{% arrow_link href="/blog/post/announcing-bigint-columns" %} +Read the announcement +{% /arrow_link %} diff --git a/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc b/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc index 7ff801c48eb..9de1237ed7e 100644 --- a/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc +++ b/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc @@ -6,6 +6,8 @@ description: Safely increment and decrement numeric fields without race conditio Atomic numeric operations allow you to safely increase or decrease numeric fields without fetching the full row. This eliminates race conditions and reduces bandwidth usage when updating any numeric values that need to be modified atomically, such as counters, scores, balances, and other fast-moving numeric data. +These operations work with `integer`, `bigint`, and `float` columns. Use `bigint` columns when your counters or accumulators may exceed the 32-bit integer range. + # How atomic operations work {% #how-atomic-operations-work %} Instead of the traditional read-modify-write pattern, atomic numeric operations use dedicated methods to modify values directly on the server. The server applies the change atomically under concurrency control and returns the new value. diff --git a/src/routes/docs/products/databases/tables/+page.markdoc b/src/routes/docs/products/databases/tables/+page.markdoc index 9083db88987..1fcff68a5b3 100644 --- a/src/routes/docs/products/databases/tables/+page.markdoc +++ b/src/routes/docs/products/databases/tables/+page.markdoc @@ -77,6 +77,11 @@ const promise = tablesDB.createTable({ type: 'float', required: false }, + { + key: 'total_views', + type: 'bigint', + required: false + }, { key: 'is_active', type: 'boolean', @@ -209,6 +214,11 @@ let promise = tablesDB.createTable({ type: 'float', required: false }, + { + key: 'total_views', + type: 'bigint', + required: false + }, { key: 'is_active', type: 'boolean', @@ -343,6 +353,11 @@ $result = $tablesDB->createTable( 'type' => 'float', 'required' => false ], + [ + 'key' => 'total_views', + 'type' => 'bigint', + 'required' => false + ], [ 'key' => 'is_active', 'type' => 'boolean', @@ -469,6 +484,11 @@ result = tablesDB.create_table( 'type': 'float', 'required': False }, + { + 'key': 'total_views', + 'type': 'bigint', + 'required': False + }, { 'key': 'is_active', 'type': 'boolean', @@ -593,6 +613,11 @@ response = tablesDB.create_table( type: 'float', required: false }, + { + key: 'total_views', + type: 'bigint', + required: false + }, { key: 'is_active', type: 'boolean', @@ -728,6 +753,12 @@ Table result = await tablesDB.CreateTable( { "required", false } }, new Dictionary + { + { "key", "total_views" }, + { "type", "bigint" }, + { "required", false } + }, + new Dictionary { { "key", "is_active" }, { "type", "boolean" }, @@ -864,6 +895,11 @@ void main() { // Init SDK 'type': 'float', 'required': false }, + { + 'key': 'total_views', + 'type': 'bigint', + 'required': false + }, { 'key': 'is_active', 'type': 'boolean', @@ -995,6 +1031,11 @@ val response = tablesDB.createTable( "type" to "float", "required" to false ), + mapOf( + "key" to "total_views", + "type" to "bigint", + "required" to false + ), mapOf( "key" to "is_active", "type" to "boolean", @@ -1116,6 +1157,11 @@ List> columns = Arrays.asList( put("type", "float"); put("required", false); }}, + new HashMap() {{ + put("key", "total_views"); + put("type", "bigint"); + put("required", false); + }}, new HashMap() {{ put("key", "is_active"); put("type", "boolean"); @@ -1254,6 +1300,11 @@ let table = try await tablesDB.createTable( "type": "float", "required": false ], + [ + "key": "total_views", + "type": "bigint", + "required": false + ], [ "key": "is_active", "type": "boolean", @@ -1383,6 +1434,11 @@ async fn main() -> Result<(), Box> { "type": "float", "required": false }), + json!({ + "key": "total_views", + "type": "bigint", + "required": false + }), json!({ "key": "is_active", "type": "boolean", @@ -1516,7 +1572,8 @@ You can choose between the following types. | `text` | Text column. Prefix indexing only. Maximum 16,383 characters. | | `mediumtext` | Text column. Prefix indexing only. Maximum 4,194,303 characters. | | `longtext` | Text column. Prefix indexing only. Maximum 1,073,741,823 characters. | -| `integer` | Integer column. | +| `integer` | Integer column. 32-bit signed, range -2,147,483,648 to 2,147,483,647. | +| `bigint` | Big integer column. 64-bit signed, range -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Ideal for timestamps, large counters, or IDs that exceed 32-bit limits. | | `float` | Float column. | | `boolean` | Boolean column. | | `datetime` | Datetime column formatted as an ISO 8601 string. | diff --git a/static/images/blog/announcing-bigint-columns/console-create-bigint.avif b/static/images/blog/announcing-bigint-columns/console-create-bigint.avif new file mode 100644 index 00000000000..fab0189d819 Binary files /dev/null and b/static/images/blog/announcing-bigint-columns/console-create-bigint.avif differ diff --git a/static/images/blog/announcing-bigint-columns/cover.avif b/static/images/blog/announcing-bigint-columns/cover.avif new file mode 100644 index 00000000000..e39e3d85c03 Binary files /dev/null and b/static/images/blog/announcing-bigint-columns/cover.avif differ