appwrite-dart
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAppwrite Dart SDK
Appwrite Dart SDK
Installation
安装
bash
undefinedbash
undefinedFlutter (client-side)
Flutter (客户端)
flutter pub add appwrite
flutter pub add appwrite
Dart (server-side)
Dart (后端)
dart pub add dart_appwrite
undefineddart pub add dart_appwrite
undefinedSetting Up the Client
客户端配置
Client-side (Flutter)
客户端(Flutter)
dart
import 'package:appwrite/appwrite.dart';
final client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]');dart
import 'package:appwrite/appwrite.dart';
final client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]');Server-side (Dart)
后端(Dart)
dart
import 'package:dart_appwrite/dart_appwrite.dart';
final client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject(Platform.environment['APPWRITE_PROJECT_ID']!)
.setKey(Platform.environment['APPWRITE_API_KEY']!);dart
import 'package:dart_appwrite/dart_appwrite.dart';
final client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject(Platform.environment['APPWRITE_PROJECT_ID']!)
.setKey(Platform.environment['APPWRITE_API_KEY']!);Code Examples
代码示例
Authentication (client-side)
客户端认证
dart
final account = Account(client);
// Signup
await account.create(userId: ID.unique(), email: 'user@example.com', password: 'password123', name: 'User Name');
// Login
final session = await account.createEmailPasswordSession(email: 'user@example.com', password: 'password123');
// OAuth login
await account.createOAuth2Session(provider: OAuthProvider.google);
// Get current user
final user = await account.get();
// Logout
await account.deleteSession(sessionId: 'current');dart
final account = Account(client);
// 注册
await account.create(userId: ID.unique(), email: 'user@example.com', password: 'password123', name: 'User Name');
// 登录
final session = await account.createEmailPasswordSession(email: 'user@example.com', password: 'password123');
// OAuth登录
await account.createOAuth2Session(provider: OAuthProvider.google);
// 获取当前用户
final user = await account.get();
// 登出
await account.deleteSession(sessionId: 'current');User Management (server-side)
后端用户管理
dart
final users = Users(client);
// Create user
final user = await users.create(userId: ID.unique(), email: 'user@example.com', password: 'password123', name: 'User Name');
// List users
final list = await users.list(queries: [Query.limit(25)]);
// Get user
final fetched = await users.get(userId: '[USER_ID]');
// Delete user
await users.delete(userId: '[USER_ID]');dart
final users = Users(client);
// 创建用户
final user = await users.create(userId: ID.unique(), email: 'user@example.com', password: 'password123', name: 'User Name');
// 列出用户
final list = await users.list(queries: [Query.limit(25)]);
// 获取用户信息
final fetched = await users.get(userId: '[USER_ID]');
// 删除用户
await users.delete(userId: '[USER_ID]');Database Operations
数据库操作
Note: Use(not the deprecatedTablesDBclass) for all new code. Only useDatabasesif the existing codebase already relies on it or the user explicitly requests it.DatabasesTip: Prefer named parameters (e.g.,) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.databaseId: '...'
dart
final tablesDB = TablesDB(client);
// Create database (server-side only)
final db = await tablesDB.create(databaseId: ID.unique(), name: 'My Database');
// Create table (server-side only)
final col = await tablesDB.createTable(databaseId: '[DATABASE_ID]', tableId: ID.unique(), name: 'My Table');
// Create row
final doc = await tablesDB.createRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: ID.unique(),
data: {'title': 'Hello', 'done': false},
);
// Query rows
final results = await tablesDB.listRows(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
queries: [Query.equal('done', false), Query.limit(10)],
);
// Get row
final row = await tablesDB.getRow(databaseId: '[DATABASE_ID]', tableId: '[TABLE_ID]', rowId: '[ROW_ID]');
// Update row
await tablesDB.updateRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: '[ROW_ID]',
data: {'done': true},
);
// Delete row
await tablesDB.deleteRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: '[ROW_ID]',
);注意: 所有新代码请使用(而非已弃用的TablesDB类)。仅当现有代码库已依赖Databases或用户明确要求时,才使用该类。Databases提示: 所有SDK方法调用优先使用命名参数(例如:)。仅当现有代码库已使用位置参数或用户明确要求时,才使用位置参数。databaseId: '...'
dart
final tablesDB = TablesDB(client);
// 创建数据库(仅后端可用)
final db = await tablesDB.create(databaseId: ID.unique(), name: 'My Database');
// 创建表(仅后端可用)
final col = await tablesDB.createTable(databaseId: '[DATABASE_ID]', tableId: ID.unique(), name: 'My Table');
// 创建行
final doc = await tablesDB.createRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: ID.unique(),
data: {'title': 'Hello', 'done': false},
);
// 查询行
final results = await tablesDB.listRows(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
queries: [Query.equal('done', false), Query.limit(10)],
);
// 获取行
final row = await tablesDB.getRow(databaseId: '[DATABASE_ID]', tableId: '[TABLE_ID]', rowId: '[ROW_ID]');
// 更新行
await tablesDB.updateRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: '[ROW_ID]',
data: {'done': true},
);
// 删除行
await tablesDB.deleteRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: '[ROW_ID]',
);String Column Types
字符串列类型
Note: The legacytype is deprecated. Use explicit column types for all new columns.string
| Type | Max characters | Indexing | Storage |
|---|---|---|---|
| 16,383 | Full index (if size ≤ 768) | Inline in row |
| 16,383 | Prefix only | Off-page |
| 4,194,303 | Prefix only | Off-page |
| 1,073,741,823 | Prefix only | Off-page |
- is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
varchar - ,
text, andmediumtextare stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.longtextis not required for these types.size
dart
// Create table with explicit string column types
await tablesDB.createTable(
databaseId: '[DATABASE_ID]',
tableId: ID.unique(),
name: 'articles',
columns: [
{'key': 'title', 'type': 'varchar', 'size': 255, 'required': true}, // inline, fully indexable
{'key': 'summary', 'type': 'text', 'required': false}, // off-page, prefix index only
{'key': 'body', 'type': 'mediumtext', 'required': false}, // up to ~4 M chars
{'key': 'raw_data', 'type': 'longtext', 'required': false}, // up to ~1 B chars
],
);注意: 旧版类型已弃用。所有新列请使用明确的列类型。string
| 类型 | 最大字符数 | 索引支持 | 存储方式 |
|---|---|---|---|
| 16,383 | 全索引(若长度≤768) | 行内存储 |
| 16,383 | 仅前缀索引 | 页外存储 |
| 4,194,303 | 仅前缀索引 | 页外存储 |
| 1,073,741,823 | 仅前缀索引 | 页外存储 |
- 以内联方式存储,占用64KB行大小限制的配额。适合用于名称、短链接(slug)或标识符等短且需要索引的字段。
varchar - 、
text和mediumtext以页外方式存储(行中仅保留一个20字节的指针),因此不会占用行大小配额。这些类型无需指定longtext参数。size
dart
// 创建包含明确字符串列类型的表
await tablesDB.createTable(
databaseId: '[DATABASE_ID]',
tableId: ID.unique(),
name: 'articles',
columns: [
{'key': 'title', 'type': 'varchar', 'size': 255, 'required': true}, // 行内存储,支持全索引
{'key': 'summary', 'type': 'text', 'required': false}, // 页外存储,仅支持前缀索引
{'key': 'body', 'type': 'mediumtext', 'required': false}, // 最多支持约400万字符
{'key': 'raw_data', 'type': 'longtext', 'required': false}, // 最多支持约10亿字符
],
);Query Methods
查询方法
dart
// Filtering
Query.equal('field', 'value') // == (or pass list for IN)
Query.notEqual('field', 'value') // !=
Query.lessThan('field', 100) // <
Query.lessThanEqual('field', 100) // <=
Query.greaterThan('field', 100) // >
Query.greaterThanEqual('field', 100) // >=
Query.between('field', 1, 100) // 1 <= field <= 100
Query.isNull('field') // is null
Query.isNotNull('field') // is not null
Query.startsWith('field', 'prefix') // starts with
Query.endsWith('field', 'suffix') // ends with
Query.contains('field', 'sub') // contains
Query.search('field', 'keywords') // full-text search (requires index)
// Sorting
Query.orderAsc('field')
Query.orderDesc('field')
// Pagination
Query.limit(25) // max rows (default 25, max 100)
Query.offset(0) // skip N rows
Query.cursorAfter('[ROW_ID]') // cursor pagination (preferred)
Query.cursorBefore('[ROW_ID]')
// Selection & Logic
Query.select(['field1', 'field2']) // return only specified fields
Query.or([Query.equal('a', 1), Query.equal('b', 2)]) // OR
Query.and([Query.greaterThan('age', 18), Query.lessThan('age', 65)]) // AND (default)dart
// 过滤
Query.equal('field', 'value') // ==(传入列表则为IN查询)
Query.notEqual('field', 'value') // !=
Query.lessThan('field', 100) // <
Query.lessThanEqual('field', 100) // <=
Query.greaterThan('field', 100) // >
Query.greaterThanEqual('field', 100) // >=
Query.between('field', 1, 100) // 1 <= field <= 100
Query.isNull('field') // 为空
Query.isNotNull('field') // 不为空
Query.startsWith('field', 'prefix') // 以指定前缀开头
Query.endsWith('field', 'suffix') // 以指定后缀结尾
Query.contains('field', 'sub') // 包含指定子串
Query.search('field', 'keywords') // 全文搜索(需提前创建索引)
// 排序
Query.orderAsc('field')
Query.orderDesc('field')
// 分页
Query.limit(25) // 最大行数(默认25,最大100)
Query.offset(0) // 跳过N行
Query.cursorAfter('[ROW_ID]') // 游标分页(推荐使用)
Query.cursorBefore('[ROW_ID]')
// 字段选择与逻辑
Query.select(['field1', 'field2']) // 仅返回指定字段
Query.or([Query.equal('a', 1), Query.equal('b', 2)]) // 或逻辑
Query.and([Query.greaterThan('age', 18), Query.lessThan('age', 65)]) // 与逻辑(默认)File Storage
文件存储
dart
final storage = Storage(client);
// Upload file
final file = await storage.createFile(
bucketId: '[BUCKET_ID]',
fileId: ID.unique(),
file: InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png'),
);
// Get file preview
final preview = storage.getFilePreview(bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]', width: 300, height: 300);
// List files
final files = await storage.listFiles(bucketId: '[BUCKET_ID]');
// Delete file
await storage.deleteFile(bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]');dart
final storage = Storage(client);
// 上传文件
final file = await storage.createFile(
bucketId: '[BUCKET_ID]',
fileId: ID.unique(),
file: InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png'),
);
// 获取文件预览
final preview = storage.getFilePreview(bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]', width: 300, height: 300);
// 列出文件
final files = await storage.listFiles(bucketId: '[BUCKET_ID]');
// 删除文件
await storage.deleteFile(bucketId: '[BUCKET_ID]', fileId: '[FILE_ID]');InputFile Factory Methods
InputFile 工厂方法
dart
// Client-side (Flutter)
InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png') // from path
InputFile.fromBytes(bytes: uint8List, filename: 'file.png') // from Uint8List
// Server-side (Dart)
InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png')
InputFile.fromBytes(bytes: uint8List, filename: 'file.png')dart
// 客户端(Flutter)
InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png') // 从路径创建
InputFile.fromBytes(bytes: uint8List, filename: 'file.png') // 从Uint8List创建
// 后端(Dart)
InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png')
InputFile.fromBytes(bytes: uint8List, filename: 'file.png')Teams
团队管理
dart
final teams = Teams(client);
// Create team
final team = await teams.create(teamId: ID.unique(), name: 'Engineering');
// List teams
final list = await teams.list();
// Create membership (invite user by email)
final membership = await teams.createMembership(
teamId: '[TEAM_ID]',
roles: ['editor'],
email: 'user@example.com',
);
// List memberships
final members = await teams.listMemberships(teamId: '[TEAM_ID]');
// Update membership roles
await teams.updateMembership(teamId: '[TEAM_ID]', membershipId: '[MEMBERSHIP_ID]', roles: ['admin']);
// Delete team
await teams.delete(teamId: '[TEAM_ID]');Role-based access: Usefor all team members orRole.team('[TEAM_ID]')for a specific team role when setting permissions.Role.team('[TEAM_ID]', 'editor')
dart
final teams = Teams(client);
// 创建团队
final team = await teams.create(teamId: ID.unique(), name: 'Engineering');
// 列出团队
final list = await teams.list();
// 创建团队成员(通过邮箱邀请用户)
final membership = await teams.createMembership(
teamId: '[TEAM_ID]',
roles: ['editor'],
email: 'user@example.com',
);
// 列出团队成员
final members = await teams.listMemberships(teamId: '[TEAM_ID]');
// 更新团队成员角色
await teams.updateMembership(teamId: '[TEAM_ID]', membershipId: '[MEMBERSHIP_ID]', roles: ['admin']);
// 删除团队
await teams.delete(teamId: '[TEAM_ID]');基于角色的权限控制: 设置权限时,使用表示所有团队成员,使用Role.team('[TEAM_ID]')表示团队中特定角色的成员。Role.team('[TEAM_ID]', 'editor')
Real-time Subscriptions (client-side)
客户端实时订阅
dart
final realtime = Realtime(client);
final subscription = realtime.subscribe(['databases.[DATABASE_ID].tables.[TABLE_ID].rows']);
subscription.stream.listen((response) {
print(response.events); // e.g. ['databases.*.tables.*.rows.*.create']
print(response.payload); // the affected resource
});
// Subscribe to multiple channels
final multi = realtime.subscribe([
'databases.[DATABASE_ID].tables.[TABLE_ID].rows',
'buckets.[BUCKET_ID].files',
]);
// Cleanup
subscription.close();Available channels:
| Channel | Description |
|---|---|
| Changes to the authenticated user's account |
| All rows in a table |
| A specific row |
| All files in a bucket |
| A specific file |
| Changes to teams the user belongs to |
| A specific team |
| The user's team memberships |
| A specific membership |
| Function execution updates |
Response fields: (array), (resource), (matched), (ISO 8601).
eventspayloadchannelstimestampdart
final realtime = Realtime(client);
final subscription = realtime.subscribe(['databases.[DATABASE_ID].tables.[TABLE_ID].rows']);
subscription.stream.listen((response) {
print(response.events); // 示例:['databases.*.tables.*.rows.*.create']
print(response.payload); // 受影响的资源
});
// 订阅多个频道
final multi = realtime.subscribe([
'databases.[DATABASE_ID].tables.[TABLE_ID].rows',
'buckets.[BUCKET_ID].files',
]);
// 清理资源
subscription.close();可用频道:
| 频道 | 描述 |
|---|---|
| 已认证用户的账户变更 |
| 表中所有行的变更 |
| 指定行的变更 |
| 存储桶中所有文件的变更 |
| 指定文件的变更 |
| 用户所属团队的变更 |
| 指定团队的变更 |
| 用户的团队成员身份变更 |
| 指定团队成员身份的变更 |
| 函数执行状态更新 |
响应字段:(数组)、(资源内容)、(匹配的频道)、(ISO 8601格式时间戳)。
eventspayloadchannelstimestampServerless Functions (server-side)
后端无服务器函数
dart
final functions = Functions(client);
// Execute function
final execution = await functions.createExecution(functionId: '[FUNCTION_ID]', body: '{"key": "value"}');
// List executions
final executions = await functions.listExecutions(functionId: '[FUNCTION_ID]');dart
final functions = Functions(client);
// 执行函数
final execution = await functions.createExecution(functionId: '[FUNCTION_ID]', body: '{"key": "value"}');
// 列出执行记录
final executions = await functions.listExecutions(functionId: '[FUNCTION_ID]');Writing a Function Handler (Dart runtime)
编写函数处理逻辑(Dart运行时)
dart
// lib/main.dart — Appwrite Function entry point
Future<dynamic> main(final context) async {
// context.req.body — raw body (String)
// context.req.bodyJson — parsed JSON (Map or null)
// context.req.headers — headers (Map)
// context.req.method — HTTP method
// context.req.path — URL path
// context.req.query — query params (Map)
context.log('Processing: ${context.req.method} ${context.req.path}');
if (context.req.method == 'GET') {
return context.res.json({'message': 'Hello from Appwrite Function!'});
}
return context.res.json({'success': true}); // JSON
// return context.res.text('Hello'); // plain text
// return context.res.empty(); // 204
// return context.res.redirect('https://...'); // 302
}dart
// lib/main.dart — Appwrite Function 入口文件
Future<dynamic> main(final context) async {
// context.req.body — 原始请求体(字符串)
// context.req.bodyJson — 解析后的JSON(Map或null)
// context.req.headers — 请求头(Map)
// context.req.method — HTTP方法
// context.req.path — URL路径
// context.req.query — 查询参数(Map)
context.log('Processing: ${context.req.method} ${context.req.path}');
if (context.req.method == 'GET') {
return context.res.json({'message': 'Hello from Appwrite Function!'});
}
return context.res.json({'success': true}); // 返回JSON
// return context.res.text('Hello'); // 返回纯文本
// return context.res.empty(); // 返回204无内容
// return context.res.redirect('https://...'); // 重定向
}Server-Side Rendering (SSR) Authentication
服务端渲染(SSR)认证
SSR apps using server-side Dart (Dart Frog, Shelf, etc.) use the server SDK () to handle auth. You need two clients:
dart_appwrite- Admin client — uses an API key, creates sessions, bypasses rate limits (reusable singleton)
- Session client — uses a session cookie, acts on behalf of a user (create per-request, never share)
dart
import 'package:dart_appwrite/dart_appwrite.dart';
// Admin client (reusable)
final adminClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]')
.setKey(Platform.environment['APPWRITE_API_KEY']!);
// Session client (create per-request)
final sessionClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]');
final session = request.cookies['a_session_[PROJECT_ID]'];
if (session != null) {
sessionClient.setSession(session);
}使用Dart后端(Dart Frog、Shelf等)的SSR应用需使用服务端SDK()处理认证,需要两个客户端:
dart_appwrite- 管理员客户端 — 使用API密钥,可创建会话、绕过速率限制(可复用单例)
- 会话客户端 — 使用会话Cookie,代表用户执行操作(每个请求创建一个,不可共享)
dart
import 'package:dart_appwrite/dart_appwrite.dart';
// 管理员客户端(可复用)
final adminClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]')
.setKey(Platform.environment['APPWRITE_API_KEY']!);
// 会话客户端(每个请求创建)
final sessionClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]');
final session = request.cookies['a_session_[PROJECT_ID]'];
if (session != null) {
sessionClient.setSession(session);
}Email/Password Login
邮箱/密码登录
dart
final account = Account(adminClient);
final session = await account.createEmailPasswordSession(
email: body['email'],
password: body['password'],
);
// Cookie name must be a_session_<PROJECT_ID>
response.headers.add('Set-Cookie',
'a_session_[PROJECT_ID]=${session.secret}; '
'HttpOnly; Secure; SameSite=Strict; '
'Expires=${HttpDate.format(DateTime.parse(session.expire))}; Path=/');dart
final account = Account(adminClient);
final session = await account.createEmailPasswordSession(
email: body['email'],
password: body['password'],
);
// Cookie名称必须为a_session_<PROJECT_ID>
response.headers.add('Set-Cookie',
'a_session_[PROJECT_ID]=${session.secret}; '
'HttpOnly; Secure; SameSite=Strict; '
'Expires=${HttpDate.format(DateTime.parse(session.expire))}; Path=/');Authenticated Requests
认证请求处理
dart
final session = request.cookies['a_session_[PROJECT_ID]'];
if (session == null) {
return Response(statusCode: 401, body: 'Unauthorized');
}
final sessionClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]')
.setSession(session);
final account = Account(sessionClient);
final user = await account.get();dart
final session = request.cookies['a_session_[PROJECT_ID]'];
if (session == null) {
return Response(statusCode: 401, body: 'Unauthorized');
}
final sessionClient = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
.setProject('[PROJECT_ID]')
.setSession(session);
final account = Account(sessionClient);
final user = await account.get();OAuth2 SSR Flow
OAuth2 SSR 流程
dart
// Step 1: Redirect to OAuth provider
final account = Account(adminClient);
final redirectUrl = await account.createOAuth2Token(
provider: OAuthProvider.github,
success: 'https://example.com/oauth/success',
failure: 'https://example.com/oauth/failure',
);
return Response(statusCode: 302, headers: {'Location': redirectUrl});
// Step 2: Handle callback — exchange token for session
final account = Account(adminClient);
final session = await account.createSession(
userId: request.uri.queryParameters['userId']!,
secret: request.uri.queryParameters['secret']!,
);
// Set session cookie as aboveCookie security: Always use,HttpOnly, andSecureto prevent XSS. The cookie name must beSameSite=Strict.a_session_<PROJECT_ID>
Forwarding user agent: Callto record the end-user's browser info for debugging and security.sessionClient.setForwardedUserAgent(request.headers['user-agent'])
dart
// 步骤1:重定向到OAuth提供商
final account = Account(adminClient);
final redirectUrl = await account.createOAuth2Token(
provider: OAuthProvider.github,
success: 'https://example.com/oauth/success',
failure: 'https://example.com/oauth/failure',
);
return Response(statusCode: 302, headers: {'Location': redirectUrl});
// 步骤2:处理回调 — 交换令牌获取会话
final account = Account(adminClient);
final session = await account.createSession(
userId: request.uri.queryParameters['userId']!,
secret: request.uri.queryParameters['secret']!,
);
// 如上设置会话CookieCookie安全: 始终使用、HttpOnly和Secure属性以防止XSS攻击。Cookie名称必须为SameSite=Strict。a_session_<PROJECT_ID>
转发用户代理: 调用以记录终端用户的浏览器信息,用于调试和安全分析。sessionClient.setForwardedUserAgent(request.headers['user-agent'])
Error Handling
错误处理
dart
import 'package:appwrite/appwrite.dart';
// AppwriteException is included in the main import
try {
final row = await tablesDB.getRow(databaseId: '[DATABASE_ID]', tableId: '[TABLE_ID]', rowId: '[ROW_ID]');
} on AppwriteException catch (e) {
print(e.message); // human-readable message
print(e.code); // HTTP status code (int)
print(e.type); // error type (e.g. 'document_not_found')
print(e.response); // full response body (Map)
}Common error codes:
| Code | Meaning |
|---|---|
| Unauthorized — missing or invalid session/API key |
| Forbidden — insufficient permissions |
| Not found — resource does not exist |
| Conflict — duplicate ID or unique constraint |
| Rate limited — too many requests |
dart
import 'package:appwrite/appwrite.dart';
// AppwriteException已包含在主导入中
try {
final row = await tablesDB.getRow(databaseId: '[DATABASE_ID]', tableId: '[TABLE_ID]', rowId: '[ROW_ID]');
} on AppwriteException catch (e) {
print(e.message); // 人类可读错误信息
print(e.code); // HTTP状态码(整数)
print(e.type); // 错误类型(例如:'document_not_found')
print(e.response); // 完整响应体(Map)
}常见错误码:
| 状态码 | 含义 |
|---|---|
| 未授权 — 缺少或无效的会话/API密钥 |
| 禁止访问 — 权限不足 |
| 未找到 — 资源不存在 |
| 冲突 — 重复ID或违反唯一约束 |
| 请求超限 — 请求次数过多 |
Permissions & Roles (Critical)
权限与角色(重点)
Appwrite uses permission strings to control access to resources. Each permission pairs an action (, , , , or which grants create + update + delete) with a role target. By default, no user has access unless permissions are explicitly set at the document/file level or inherited from the collection/bucket settings. Permissions are arrays of strings built with the and helpers.
readupdatedeletecreatewritePermissionRoledart
import 'package:appwrite/appwrite.dart';
// Permission and Role are included in the main package importAppwrite使用权限字符串控制资源访问权限。每个权限由操作(、、、,或,后者包含create+update+delete)和角色目标组成。默认情况下,所有用户均无访问权限,除非在文档/文件级别明确设置权限,或从集合/存储桶设置中继承权限。权限是由和工具类构建的字符串数组。
readupdatedeletecreatewritePermissionRoledart
import 'package:appwrite/appwrite.dart';
// Permission和Role已包含在主包导入中Database Row with Permissions
带权限的数据库行
dart
final doc = await tablesDB.createRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: ID.unique(),
data: {'title': 'Hello World'},
permissions: [
Permission.read(Role.user('[USER_ID]')), // specific user can read
Permission.update(Role.user('[USER_ID]')), // specific user can update
Permission.read(Role.team('[TEAM_ID]')), // all team members can read
Permission.read(Role.any()), // anyone (including guests) can read
],
);dart
final doc = await tablesDB.createRow(
databaseId: '[DATABASE_ID]',
tableId: '[TABLE_ID]',
rowId: ID.unique(),
data: {'title': 'Hello World'},
permissions: [
Permission.read(Role.user('[USER_ID]')), // 指定用户可读取
Permission.update(Role.user('[USER_ID]')), // 指定用户可更新
Permission.read(Role.team('[TEAM_ID]')), // 所有团队成员可读取
Permission.read(Role.any()), // 任何人(包括访客)可读取
],
);File Upload with Permissions
带权限的文件上传
dart
final file = await storage.createFile(
bucketId: '[BUCKET_ID]',
fileId: ID.unique(),
file: InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png'),
permissions: [
Permission.read(Role.any()),
Permission.update(Role.user('[USER_ID]')),
Permission.delete(Role.user('[USER_ID]')),
],
);When to set permissions: Set document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document permissions empty.
Common mistakes:
- Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
withRole.any()/write/update— allows any user, including unauthenticated guests, to modify or remove the resourcedelete on sensitive data — makes the resource publicly readablePermission.read(Role.any())
dart
final file = await storage.createFile(
bucketId: '[BUCKET_ID]',
fileId: ID.unique(),
file: InputFile.fromPath(path: '/path/to/file.png', filename: 'file.png'),
permissions: [
Permission.read(Role.any()),
Permission.update(Role.user('[USER_ID]')),
Permission.delete(Role.user('[USER_ID]')),
],
);权限设置时机: 当需要按资源粒度控制访问时,设置文档/文件级权限。如果集合中的所有文档都遵循相同规则,可在集合/存储桶级别配置权限,文档级权限留空即可。
常见错误:
- 忘记设置权限 — 资源对所有用户(包括创建者)均不可访问
- 对
/write/update操作使用delete— 允许任何用户(包括未认证访客)修改或删除资源Role.any()- 对敏感数据使用
— 资源变为公开可读Permission.read(Role.any())