Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
appwrite avatar

Ruby

  • 1 installs
  • 10 repo stars
  • Updated July 29, 2026
  • appwrite/claude-plugin

Appwrite Ruby SDK skill is a Claude Code skill that guides building server-side Ruby applications, including Rails and Sinatra, against the Appwrite backend-as-a-service.

About

This skill documents the Appwrite Ruby SDK for server-side Ruby applications, including Rails and Sinatra. A developer uses it to configure the Appwrite client with an API key and then manage users, run TablesDB database and row operations, query data, upload files, and manage teams. It gives Claude current conventions such as keyword arguments and TablesDB over the deprecated Databases class.

  • Server-side Ruby Appwrite client for Rails and Sinatra
  • User management, TablesDB CRUD, queries, and file storage via API keys
  • Uses keyword arguments and TablesDB over the deprecated Databases class

Ruby by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #3,836 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Jul 30, 2026 (Skillselion catalog sync)
At a glance

ruby capabilities & compatibility

Requires an Appwrite project and API key; Appwrite Cloud has a free tier.

Capabilities
appwrite sdk python · appwrite sdk php · backend integration
Works with
github
Use cases
api development · database
Pricing
Bring your own API key
From the docs

What ruby says it does

Appwrite Ruby SDK skill. Use when building server-side Ruby applications with Appwrite, including Rails and Sinatra integrations.
SKILL.md
gem install appwrite
SKILL.md
npx skills add https://github.com/appwrite/claude-plugin --skill ruby

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars10
Last updatedJuly 29, 2026
Repositoryappwrite/claude-plugin

What it does

Build a Ruby, Rails, or Sinatra backend that manages users, data, and storage on Appwrite.

Who is it for?

Ruby, Rails, or Sinatra developers adding Appwrite user, data, and storage features.

Skip if: Client-only browser apps or non-Appwrite backends.

When should I use this skill?

Building server-side Ruby applications with Appwrite, including Rails and Sinatra.

What you get

  • Configured Appwrite Ruby client
  • user and TablesDB CRUD code

By the numbers

  • 4 string column types documented (varchar, text, mediumtext, longtext)

Files

SKILL.mdMarkdownGitHub ↗

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)

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

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

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

# Delete user
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)

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

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

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

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

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

# Delete row
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.
TypeMax charactersIndexingStorage
varchar16,383Full index (if size ≤ 768)Inline in row
text16,383Prefix onlyOff-page
mediumtext4,194,303Prefix onlyOff-page
longtext1,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.
# 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
    ]
)

Query Methods

# 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)

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

# 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]')

# 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)

File Storage

storage = Storage.new(client)

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

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

# Delete file
storage.delete_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]')
InputFile Factory Methods
InputFile.from_path('/path/to/file.png')             # from filesystem path
InputFile.from_string('Hello world', 'hello.txt')    # from string content

Teams

teams = Teams.new(client)

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

# List teams
list = teams.list

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

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

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

# 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.

Serverless Functions

functions = Functions.new(client)

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

# List executions
executions = functions.list_executions(function_id: '[FUNCTION_ID]')
Writing a Function Handler (Ruby runtime)
# 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

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 (reusable)
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 = 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]
    )

    # 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
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
# 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

# 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.

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    # 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
401Unauthorized — missing or invalid session/API key
403Forbidden — insufficient permissions
404Not found — resource does not exist
409Conflict — duplicate ID or unique constraint
429Rate 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 document/file level or inherited from the collection/bucket settings. Permissions are arrays of strings built with the Permission and Role helpers.

# Permission and Role are included in the main require
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]')),     # 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

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

Related skills

FAQ

Does it support Rails and Sinatra?

Yes, the skill targets server-side Ruby applications including Rails and Sinatra integrations.

How are SDK methods called?

Prefer keyword arguments such as database_id: '...' for all SDK method calls.

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.