appwrite-ruby

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Appwrite Ruby SDK

Appwrite Ruby SDK

Installation

安装

bash
gem install appwrite
bash
gem install appwrite

Setting Up the Client

配置客户端

ruby
require 'appwrite'

include Appwrite

client = Client.new
    .set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
    .set_project(ENV['APPWRITE_PROJECT_ID'])
    .set_key(ENV['APPWRITE_API_KEY'])
ruby
require 'appwrite'

include Appwrite

client = Client.new
    .set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
    .set_project(ENV['APPWRITE_PROJECT_ID'])
    .set_key(ENV['APPWRITE_API_KEY'])

Code Examples

代码示例

User Management

用户管理

ruby
users = Users.new(client)
ruby
users = Users.new(client)

Create user

Create user

user = users.create(user_id: ID.unique, email: 'user@example.com', password: 'password123', name: 'User Name')
user = users.create(user_id: ID.unique, email: 'user@example.com', password: 'password123', name: 'User Name')

List users

List users

list = users.list(queries: [Query.limit(25)])
list = users.list(queries: [Query.limit(25)])

Get user

Get user

fetched = users.get(user_id: '[USER_ID]')
fetched = users.get(user_id: '[USER_ID]')

Delete user

Delete user

users.delete(user_id: '[USER_ID]')
undefined
users.delete(user_id: '[USER_ID]')
undefined

Database Operations

数据库操作

Note: Use
TablesDB
(not the deprecated
Databases
class) for all new code. Only use
Databases
if the existing codebase already relies on it or the user explicitly requests it.
Tip: Prefer keyword arguments (e.g.,
database_id: '...'
) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.
ruby
tables_db = TablesDB.new(client)
注意: 所有新代码请使用
TablesDB
(而非已弃用的
Databases
类)。仅当现有代码库已依赖
Databases
或用户明确要求时才使用它。
提示: 所有SDK方法调用优先使用关键字参数(例如
database_id: '...'
)。仅当现有代码库已使用位置参数或用户明确要求时才使用位置参数。
ruby
tables_db = TablesDB.new(client)

Create database

Create database

db = tables_db.create(database_id: ID.unique, name: 'My Database')
db = tables_db.create(database_id: ID.unique, name: 'My Database')

Create row

Create row

doc = tables_db.create_row( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: ID.unique, data: { title: 'Hello World' } )
doc = tables_db.create_row( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: ID.unique, data: { title: 'Hello World' } )

Query rows

Query rows

results = tables_db.list_rows( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', queries: [Query.equal('title', 'Hello World'), Query.limit(10)] )
results = tables_db.list_rows( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', queries: [Query.equal('title', 'Hello World'), Query.limit(10)] )

Get row

Get row

row = tables_db.get_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
row = tables_db.get_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')

Update row

Update row

tables_db.update_row( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]', data: { title: 'Updated' } )
tables_db.update_row( database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]', data: { title: 'Updated' } )

Delete row

Delete row

tables_db.delete_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
undefined
tables_db.delete_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
undefined

String Column Types

字符串列类型

Note: The legacy
string
type is deprecated. Use explicit column types for all new columns.
TypeMax charactersIndexingStorage
varchar
16,383Full index (if size ≤ 768)Inline in row
text
16,383Prefix onlyOff-page
mediumtext
4,194,303Prefix onlyOff-page
longtext
1,073,741,823Prefix onlyOff-page
  • varchar
    is stored inline and counts towards the 64 KB row size limit. Prefer for short, indexed fields like names, slugs, or identifiers.
  • text
    ,
    mediumtext
    , and
    longtext
    are stored off-page (only a 20-byte pointer lives in the row), so they don't consume the row size budget.
    size
    is not required for these types.
ruby
undefined
注意: 旧版
string
类型已被弃用。所有新列请使用明确的列类型。
类型最大字符数索引方式存储方式
varchar
16,383全索引(若长度 ≤ 768)行内存储
text
16,383仅前缀索引页外存储
mediumtext
4,194,303仅前缀索引页外存储
longtext
1,073,741,823仅前缀索引页外存储
  • varchar
    存储在行内,占用64KB行大小限制。优先用于短的、需要索引的字段,如名称、短链接或标识符。
  • text
    mediumtext
    longtext
    存储在页外(行内仅保留20字节的指针),因此不会占用行大小配额。这些类型不需要指定
    size
ruby
undefined

Create table with explicit string column types

Create table with explicit string column types

tables_db.create_table( database_id: '[DATABASE_ID]', table_id: 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 ] )
undefined
tables_db.create_table( database_id: '[DATABASE_ID]', table_id: 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 ] )
undefined

Query Methods

查询方法

ruby
undefined
ruby
undefined

Filtering

Filtering

Query.equal('field', 'value') # == (or pass array for IN) Query.not_equal('field', 'value') # != Query.less_than('field', 100) # < Query.less_than_equal('field', 100) # <= Query.greater_than('field', 100) # > Query.greater_than_equal('field', 100) # >= Query.between('field', 1, 100) # 1 <= field <= 100 Query.is_null('field') # is null Query.is_not_null('field') # is not null Query.starts_with('field', 'prefix') # starts with Query.ends_with('field', 'suffix') # ends with Query.contains('field', 'sub') # contains Query.search('field', 'keywords') # full-text search (requires index)
Query.equal('field', 'value') # == (or pass array for IN) Query.not_equal('field', 'value') # != Query.less_than('field', 100) # < Query.less_than_equal('field', 100) # <= Query.greater_than('field', 100) # > Query.greater_than_equal('field', 100) # >= Query.between('field', 1, 100) # 1 <= field <= 100 Query.is_null('field') # is null Query.is_not_null('field') # is not null Query.starts_with('field', 'prefix') # starts with Query.ends_with('field', 'suffix') # ends with Query.contains('field', 'sub') # contains Query.search('field', 'keywords') # full-text search (requires index)

Sorting

Sorting

Query.order_asc('field') Query.order_desc('field')
Query.order_asc('field') Query.order_desc('field')

Pagination

Pagination

Query.limit(25) # max rows (default 25, max 100) Query.offset(0) # skip N rows Query.cursor_after('[ROW_ID]') # cursor pagination (preferred) Query.cursor_before('[ROW_ID]')
Query.limit(25) # max rows (default 25, max 100) Query.offset(0) # skip N rows Query.cursor_after('[ROW_ID]') # cursor pagination (preferred) Query.cursor_before('[ROW_ID]')

Selection & Logic

Selection & Logic

Query.select(['field1', 'field2']) # return only specified fields Query.or([Query.equal('a', 1), Query.equal('b', 2)]) # OR Query.and([Query.greater_than('age', 18), Query.less_than('age', 65)]) # AND (default)
undefined
Query.select(['field1', 'field2']) # return only specified fields Query.or([Query.equal('a', 1), Query.equal('b', 2)]) # OR Query.and([Query.greater_than('age', 18), Query.less_than('age', 65)]) # AND (default)
undefined

File Storage

文件存储

ruby
storage = Storage.new(client)
ruby
storage = Storage.new(client)

Upload file

Upload file

file = storage.create_file(bucket_id: '[BUCKET_ID]', file_id: ID.unique, file: InputFile.from_path('/path/to/file.png'))
file = storage.create_file(bucket_id: '[BUCKET_ID]', file_id: ID.unique, file: InputFile.from_path('/path/to/file.png'))

List files

List files

files = storage.list_files(bucket_id: '[BUCKET_ID]')
files = storage.list_files(bucket_id: '[BUCKET_ID]')

Delete file

Delete file

storage.delete_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]')
undefined
storage.delete_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]')
undefined

InputFile Factory Methods

InputFile工厂方法

ruby
InputFile.from_path('/path/to/file.png')             # from filesystem path
InputFile.from_string('Hello world', 'hello.txt')    # from string content
ruby
InputFile.from_path('/path/to/file.png')             # from filesystem path
InputFile.from_string('Hello world', 'hello.txt')    # from string content

Teams

团队管理

ruby
teams = Teams.new(client)
ruby
teams = Teams.new(client)

Create team

Create team

team = teams.create(team_id: ID.unique, name: 'Engineering')
team = teams.create(team_id: ID.unique, name: 'Engineering')

List teams

List teams

list = teams.list
list = teams.list

Create membership (invite user by email)

Create membership (invite user by email)

membership = teams.create_membership( team_id: '[TEAM_ID]', roles: ['editor'], email: 'user@example.com' )
membership = teams.create_membership( team_id: '[TEAM_ID]', roles: ['editor'], email: 'user@example.com' )

List memberships

List memberships

members = teams.list_memberships(team_id: '[TEAM_ID]')
members = teams.list_memberships(team_id: '[TEAM_ID]')

Update membership roles

Update membership roles

teams.update_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]', roles: ['admin'])
teams.update_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]', roles: ['admin'])

Delete team

Delete team

teams.delete(team_id: '[TEAM_ID]')

> **Role-based access:** Use `Role.team('[TEAM_ID]')` for all team members or `Role.team('[TEAM_ID]', 'editor')` for a specific team role when setting permissions.
teams.delete(team_id: '[TEAM_ID]')

> **基于角色的访问控制:** 设置权限时,使用`Role.team('[TEAM_ID]')`表示所有团队成员,或使用`Role.team('[TEAM_ID]', 'editor')`表示特定团队角色。

Serverless Functions

无服务器函数

ruby
functions = Functions.new(client)
ruby
functions = Functions.new(client)

Execute function

Execute function

execution = functions.create_execution(function_id: '[FUNCTION_ID]', body: '{"key": "value"}')
execution = functions.create_execution(function_id: '[FUNCTION_ID]', body: '{"key": "value"}')

List executions

List executions

executions = functions.list_executions(function_id: '[FUNCTION_ID]')
undefined
executions = functions.list_executions(function_id: '[FUNCTION_ID]')
undefined

Writing a Function Handler (Ruby runtime)

编写函数处理器(Ruby运行时)

ruby
undefined
ruby
undefined

src/main.rb — Appwrite Function entry point

src/main.rb — Appwrite Function entry point

def main(context) # context.req.body — raw body (String) # context.req.body_json — parsed JSON (Hash or nil) # context.req.headers — headers (Hash) # context.req.method — HTTP method # context.req.path — URL path # context.req.query — query params (Hash)
context.log("Processing: #{context.req.method} #{context.req.path}")

if context.req.method == 'GET'
    return context.res.json({ message: 'Hello from Appwrite Function!' })
end

context.res.json({ success: true })          # JSON
# context.res.text('Hello')                  # plain text
# context.res.empty                          # 204
# context.res.redirect('https://...')         # 302
end
undefined
def main(context) # context.req.body — raw body (String) # context.req.body_json — parsed JSON (Hash or nil) # context.req.headers — headers (Hash) # context.req.method — HTTP method # context.req.path — URL path # context.req.query — query params (Hash)
context.log("Processing: #{context.req.method} #{context.req.path}")

if context.req.method == 'GET'
    return context.res.json({ message: 'Hello from Appwrite Function!' })
end

context.res.json({ success: true })          # JSON
# context.res.text('Hello')                  # plain text
# context.res.empty                          # 204
# context.res.redirect('https://...')         # 302
end
undefined

Server-Side Rendering (SSR) Authentication

服务器端渲染(SSR)认证

SSR apps using Ruby frameworks (Rails, Sinatra, etc.) use the server SDK to handle auth. You need two clients:
  • 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)
ruby
require 'appwrite'
include Appwrite
使用Ruby框架(Rails、Sinatra等)的SSR应用通过服务器SDK处理认证。你需要两个客户端:
  • 管理员客户端 — 使用API密钥,可创建会话,绕过速率限制(可复用单例)
  • 会话客户端 — 使用会话Cookie,代表用户执行操作(每个请求创建一次,切勿共享)
ruby
require 'appwrite'
include Appwrite

Admin client (reusable)

Admin client (reusable)

admin_client = Client.new .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') .set_project('[PROJECT_ID]') .set_key(ENV['APPWRITE_API_KEY'])
admin_client = Client.new .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') .set_project('[PROJECT_ID]') .set_key(ENV['APPWRITE_API_KEY'])

Session client (create per-request)

Session client (create per-request)

session_client = Client.new .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') .set_project('[PROJECT_ID]')
session = cookies['a_session_[PROJECT_ID]'] session_client.set_session(session) if session
undefined
session_client = Client.new .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') .set_project('[PROJECT_ID]')
session = cookies['a_session_[PROJECT_ID]'] session_client.set_session(session) if session
undefined

Email/Password Login (Sinatra)

邮箱/密码登录(Sinatra)

ruby
post '/login' do
    account = Account.new(admin_client)
    session = account.create_email_password_session(
        email: params[:email],
        password: params[:password]
    )

    # Cookie name must be a_session_<PROJECT_ID>
    response.set_cookie('a_session_[PROJECT_ID]', {
        value: session.secret,
        httponly: true,
        secure: true,
        same_site: :strict,
        path: '/',
    })

    content_type :json
    { success: true }.to_json
end
ruby
post '/login' do
    account = Account.new(admin_client)
    session = account.create_email_password_session(
        email: params[:email],
        password: params[:password]
    )

    # Cookie name must be a_session_<PROJECT_ID>
    response.set_cookie('a_session_[PROJECT_ID]', {
        value: session.secret,
        httponly: true,
        secure: true,
        same_site: :strict,
        path: '/',
    })

    content_type :json
    { success: true }.to_json
end

Authenticated Requests

已认证请求

ruby
get '/user' do
    session = request.cookies['a_session_[PROJECT_ID]']
    halt 401, { error: 'Unauthorized' }.to_json unless session

    session_client = Client.new
        .set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
        .set_project('[PROJECT_ID]')
        .set_session(session)

    account = Account.new(session_client)
    user = account.get

    content_type :json
    user.to_json
end
ruby
get '/user' do
    session = request.cookies['a_session_[PROJECT_ID]']
    halt 401, { error: 'Unauthorized' }.to_json unless session

    session_client = Client.new
        .set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
        .set_project('[PROJECT_ID]')
        .set_session(session)

    account = Account.new(session_client)
    user = account.get

    content_type :json
    user.to_json
end

OAuth2 SSR Flow

OAuth2 SSR流程

ruby
undefined
ruby
undefined

Step 1: Redirect to OAuth provider

Step 1: Redirect to OAuth provider

get '/oauth' do account = Account.new(admin_client) redirect_url = account.create_o_auth2_token( provider: OAuthProvider::GITHUB, success: 'https://example.com/oauth/success', failure: 'https://example.com/oauth/failure' ) redirect redirect_url end
get '/oauth' do account = Account.new(admin_client) redirect_url = account.create_o_auth2_token( provider: OAuthProvider::GITHUB, success: 'https://example.com/oauth/success', failure: 'https://example.com/oauth/failure' ) redirect redirect_url end

Step 2: Handle callback — exchange token for session

Step 2: Handle callback — exchange token for session

get '/oauth/success' do account = Account.new(admin_client) session = account.create_session( user_id: params[:userId], secret: params[:secret] )
response.set_cookie('a_session_[PROJECT_ID]', {
    value: session.secret,
    httponly: true, secure: true, same_site: :strict, path: '/',
})

content_type :json
{ success: true }.to_json
end

> **Cookie security:** Always use `httponly`, `secure`, and `same_site: :strict` to prevent XSS. The cookie name must be `a_session_<PROJECT_ID>`.

> **Forwarding user agent:** Call `session_client.set_forwarded_user_agent(request.user_agent)` to record the end-user's browser info for debugging and security.
get '/oauth/success' do account = Account.new(admin_client) session = account.create_session( user_id: params[:userId], secret: params[:secret] )
response.set_cookie('a_session_[PROJECT_ID]', {
    value: session.secret,
    httponly: true, secure: true, same_site: :strict, path: '/',
})

content_type :json
{ success: true }.to_json
end

> **Cookie安全:** 始终使用`httponly`、`secure`和`same_site: :strict`以防止XSS攻击。Cookie名称必须为`a_session_<PROJECT_ID>`。

> **转发用户代理:** 调用`session_client.set_forwarded_user_agent(request.user_agent)`以记录终端用户的浏览器信息,用于调试和安全目的。

Error Handling

错误处理

ruby
require 'appwrite'
include Appwrite

begin
    row = tables_db.get_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
rescue Appwrite::Exception => e
    puts e.message    # human-readable message
    puts e.code       # HTTP status code (Integer)
    puts e.type       # error type (e.g. 'document_not_found')
    puts e.response   # full response body (Hash)
end
Common error codes:
CodeMeaning
401
Unauthorized — missing or invalid session/API key
403
Forbidden — insufficient permissions
404
Not found — resource does not exist
409
Conflict — duplicate ID or unique constraint
429
Rate limited — too many requests
ruby
require 'appwrite'
include Appwrite

begin
    row = tables_db.get_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
rescue Appwrite::Exception => e
    puts e.message    # human-readable message
    puts e.code       # HTTP status code (Integer)
    puts e.type       # error type (e.g. 'document_not_found')
    puts e.response   # full response body (Hash)
end
常见错误码:
状态码含义
401
未授权 — 缺少或无效的会话/API密钥
403
禁止访问 — 权限不足
404
未找到 — 资源不存在
409
冲突 — 重复ID或唯一约束冲突
429
请求超限 — 请求次数过多

Permissions & Roles (Critical)

权限与角色(重点)

Appwrite uses permission strings to control access to resources. Each permission pairs an action (
read
,
update
,
delete
,
create
, or
write
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
Permission
and
Role
helpers.
ruby
undefined
Appwrite使用权限字符串控制资源访问。每个权限将操作(
read
update
delete
create
,或包含create+update+delete的
write
)与角色目标配对。默认情况下,所有用户都无访问权限,除非在文档/文件级别明确设置权限,或从集合/存储桶设置继承权限。权限是使用
Permission
Role
助手构建的字符串数组。
ruby
undefined

Permission and Role are included in the main require

Permission and Role are included in the main require

require 'appwrite' include Appwrite
undefined
require 'appwrite' include Appwrite
undefined

Database Row with Permissions

带权限的数据库行

ruby
doc = tables_db.create_row(
    database_id: '[DATABASE_ID]',
    table_id: '[TABLE_ID]',
    row_id: 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
    ]
)
ruby
doc = tables_db.create_row(
    database_id: '[DATABASE_ID]',
    table_id: '[TABLE_ID]',
    row_id: 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
    ]
)

File Upload with Permissions

带权限的文件上传

ruby
file = storage.create_file(
    bucket_id: '[BUCKET_ID]',
    file_id: ID.unique,
    file: InputFile.from_path('/path/to/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)
  • Role.any
    with
    write
    /
    update
    /
    delete
    — allows any user, including unauthenticated guests, to modify or remove the resource
  • Permission.read(Role.any)
    on sensitive data
    — makes the resource publicly readable
ruby
file = storage.create_file(
    bucket_id: '[BUCKET_ID]',
    file_id: ID.unique,
    file: InputFile.from_path('/path/to/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)
    — 使资源可公开读取