| name | appwrite-ruby |
| description | Appwrite Ruby SDK skill. Use when building server-side Ruby applications with Appwrite, including Rails and Sinatra integrations. Covers user management, database/table CRUD, file storage, and functions via API keys. |
Appwrite Ruby SDK
Installation
gem install appwrite
Setting Up the Client
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
users = Users.new(client)
user = users.create(user_id: ID.unique, email: 'user@example.com', password: 'password123', name: 'User Name')
list = users.list(queries: [Query.limit(25)])
fetched = users.get(user_id: '[USER_ID]')
users.delete(user_id: '[USER_ID]')
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.
tables_db = TablesDB.new(client)
db = tables_db.create(database_id: ID.unique, name: 'My Database')
doc = tables_db.create_row(
database_id: '[DATABASE_ID]',
table_id: '[TABLE_ID]',
row_id: ID.unique,
data: { title: 'Hello World' }
)
results = tables_db.list_rows(
database_id: '[DATABASE_ID]',
table_id: '[TABLE_ID]',
queries: [Query.equal('title', 'Hello World'), Query.limit(10)]
)
row = tables_db.get_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
tables_db.update_row(
database_id: '[DATABASE_ID]',
table_id: '[TABLE_ID]',
row_id: '[ROW_ID]',
data: { title: 'Updated' }
)
tables_db.delete_row(database_id: '[DATABASE_ID]', table_id: '[TABLE_ID]', row_id: '[ROW_ID]')
String Column Types
Note: The legacy string type is deprecated. Use explicit column types for all new columns.
| Type | Max characters | Indexing | Storage |
|---|
varchar | 16,383 | Full index (if size ≤ 768) | Inline in row |
text | 16,383 | Prefix only | Off-page |
mediumtext | 4,194,303 | Prefix only | Off-page |
longtext | 1,073,741,823 | Prefix only | Off-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.
tables_db.create_table(
database_id: '[DATABASE_ID]',
table_id: 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 },
{ key: 'raw_data', type: 'longtext', required: false },
]
)
Query Methods
Query.equal('field', 'value')
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)
Query.is_null('field')
Query.is_not_null('field')
Query.starts_with('field', 'prefix')
Query.ends_with('field', 'suffix')
Query.contains('field', 'sub')
Query.search('field', 'keywords')
Query.order_asc('field')
Query.order_desc('field')
Query.limit(25)
Query.offset(0)
Query.cursor_after('[ROW_ID]')
Query.cursor_before('[ROW_ID]')
Query.select(['field1', 'field2'])
Query.or([Query.equal('a', 1), Query.equal('b', 2)])
Query.and([Query.greater_than('age', 18), Query.less_than('age', 65)])
File Storage
storage = Storage.new(client)
file = storage.create_file(bucket_id: '[BUCKET_ID]', file_id: ID.unique, file: InputFile.from_path('/path/to/file.png'))
files = storage.list_files(bucket_id: '[BUCKET_ID]')
storage.delete_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]')
InputFile Factory Methods
InputFile.from_path('/path/to/file.png')
InputFile.from_string('Hello world', 'hello.txt')
Teams
teams = Teams.new(client)
team = teams.create(team_id: ID.unique, name: 'Engineering')
list = teams.list
membership = teams.create_membership(
team_id: '[TEAM_ID]',
roles: ['editor'],
email: 'user@example.com'
)
members = teams.list_memberships(team_id: '[TEAM_ID]')
teams.update_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]', roles: ['admin'])
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.
Serverless Functions
functions = Functions.new(client)
execution = functions.create_execution(function_id: '[FUNCTION_ID]', body: '{"key": "value"}')
executions = functions.list_executions(function_id: '[FUNCTION_ID]')
Writing a Function Handler (Ruby runtime)
def main(context)
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 })
end
Server-Side Rendering (SSR) Authentication
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)
require 'appwrite'
include Appwrite
admin_client = Client.new
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1')
.set_project('[PROJECT_ID]')
.set_key(ENV['APPWRITE_API_KEY'])
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
Email/Password Login (Sinatra)
post '/login' do
account = Account.new(admin_client)
session = account.create_email_password_session(
email: params[:email],
password: params[:password]
)
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
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
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/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.
Error Handling
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
puts e.code
puts e.type
puts e.response
end
Common error codes:
| Code | Meaning |
|---|
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 |
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 row/file level or inherited from the table/bucket settings. Permissions are arrays of strings built with the Permission and Role helpers.
require 'appwrite'
include Appwrite
Database Row with Permissions
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]')),
Permission.update(Role.user('[USER_ID]')),
Permission.read(Role.team('[TEAM_ID]')),
Permission.read(Role.any),
]
)
File Upload with Permissions
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 row/file-level permissions when you need per-resource access control. If all rows in a table share the same rules, configure permissions at the table/bucket level and leave row 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