{ "version": "1.0.0", "runDate": "2026-06-10T07:10:51.311Z", "mode": "with-skills", "totalQuestions": 191, "totalMcq": 165, "totalFreeform": 26, "totalPromptTokens": 20628343, "totalCompletionTokens": 1915408, "totalTokens": 22543751, "totalCost": 49.979719, "totalDurationMs": 49263810, "models": [ { "modelId": "deepseek-v4-flash", "modelName": "DeepSeek V4 Flash", "provider": "DeepSeek", "promptCostPerMillionTokens": 0.1, "completionCostPerMillionTokens": 0.2, "scores": { "foundation": 100, "auth": 99, "tablesdb": 95.2, "functions": 94.8, "storage": 93.8, "sites": 98.2, "messaging": 100, "realtime": 99.5, "cli": 87.7 }, "mcqScores": { "foundation": 100, "auth": 100, "tablesdb": 95, "functions": 94.1, "storage": 94.1, "sites": 100, "messaging": 100, "realtime": 100, "cli": 88 }, "freeformScores": { "foundation": 100, "auth": 93.3, "tablesdb": 96.7, "functions": 98.3, "storage": 91.7, "sites": 82.5, "messaging": 100, "realtime": 96.7, "cli": 85 }, "overall": 96.1, "mcqOverall": 96.4, "freeformOverall": 94.2, "totalQuestions": 191, "totalCorrect": 185, "totalPromptTokens": 2314233, "totalCompletionTokens": 147793, "totalTokens": 2462026, "totalCost": 0.365372, "totalDurationMs": 7528524, "averageTokensPerSecond": 19.63, "runDate": "2026-06-10T07:10:51.309Z", "questionDetails": [ { "questionId": "fund-1", "category": "foundation", "type": "mcq", "question": "What type of platform is Appwrite?", "choices": [ "An open-source backend-as-a-service (BaaS) platform", "A frontend JavaScript framework", "A cloud hosting provider", "A database management system" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7380, "completionTokens": 175, "totalTokens": 7555, "cost": 0.001082, "durationMs": 24321, "tokensPerSecond": 7.2, "toolCallCount": 1 }, { "questionId": "fund-2", "category": "foundation", "type": "mcq", "question": "Which of the following is NOT one of Appwrite's core services?", "choices": [ "Auth", "TablesDB", "Container Orchestration", "Storage" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14003, "completionTokens": 360, "totalTokens": 14363, "cost": 0.002061, "durationMs": 26838, "tokensPerSecond": 13.41, "toolCallCount": 2 }, { "questionId": "fund-3", "category": "foundation", "type": "mcq", "question": "What API protocols does Appwrite support?", "choices": [ "REST only", "GraphQL only", "REST and GraphQL", "gRPC and REST" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14019, "completionTokens": 377, "totalTokens": 14396, "cost": 0.002068, "durationMs": 30467, "tokensPerSecond": 12.37, "toolCallCount": 2 }, { "questionId": "fund-4", "category": "foundation", "type": "mcq", "question": "What is the purpose of an Appwrite API key?", "choices": [ "To encrypt database rows", "To configure DNS settings", "To authenticate server-side requests with specific scope permissions", "To manage SSL certificates" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7370, "completionTokens": 318, "totalTokens": 7688, "cost": 0.001121, "durationMs": 9076, "tokensPerSecond": 35.04, "toolCallCount": 1 }, { "questionId": "fund-5", "category": "foundation", "type": "mcq", "question": "Which Appwrite feature enables real-time data synchronization?", "choices": [ "Webhooks", "Server Functions", "Realtime API via WebSocket subscriptions", "Scheduled Tasks" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7336, "completionTokens": 230, "totalTokens": 7566, "cost": 0.001091, "durationMs": 5233, "tokensPerSecond": 43.95, "toolCallCount": 1 }, { "questionId": "fund-6", "category": "foundation", "type": "free-form", "question": "Explain the difference between client-side and server-side SDKs in Appwrite. When would you use each?", "correctAnswer": "Client SDKs are designed for frontend applications where end users interact directly. They require active sessions for authentication. Server SDKs are for backend applications and use API keys with configurable scopes, allowing operations that bypass collection-level permissions.", "rubric": "Must mention: 1) Client SDKs for frontend/user-facing apps, 2) Server SDKs use API keys, 3) Server SDKs can bypass permissions, 4) Client SDKs require user sessions", "modelAnswer": "Great question! The distinction between client-side and server-side SDKs is fundamental to how you architect your Appwrite application. Let me break it down clearly.\n\n---\n\n## Client-Side SDKs (`appwrite` / `react-native-appwrite`)\n\n**Purpose:** Run directly in the user's browser or mobile app.\n\n### How it's initialized\n```typescript\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1')\n .setProject('[PROJECT_ID]');\n// ❌ No API key — users authenticate via sessions\n```\n\n### What you can do\n- **User authentication** — email/password login, OAuth (GitHub, Google, etc.), anonymous sessions\n- **Database reads/writes** — but only what the user's permissions allow\n- **File uploads** — from `` or camera\n- **Real-time subscriptions** — live updates via WebSocket\n- **Account management** — get current user, update profile, logout\n\n### What you **cannot** do\n- Create databases or tables (admin-only)\n- List all users or manage other users' accounts\n- Execute serverless functions\n- Bypass permission rules\n\n### When to use\n- **Frontend apps** (React, Vue, Svelte, Angular, vanilla JS)\n- **React Native / Expo mobile apps**\n- Any scenario where the **end-user is directly interacting** with Appwrite resources\n\n---\n\n## Server-Side SDK (`node-appwrite`)\n\n**Purpose:** Run on your backend server (Node.js, Deno, Cloudflare Workers, etc.).\n\n### How it's initialized\n```typescript\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1')\n .setProject(process.env.APPWRITE_PROJECT_ID)\n .setKey(process.env.APPWRITE_API_KEY); // ✅ API key for admin access\n```\n\n### What you can do\n- **Everything the client SDK can do** (plus much more)\n- **User management** — create, list, update, delete any user\n- **Database administration** — create databases, tables, and manage schemas\n- **Execute serverless functions** — trigger and list executions\n- **File operations from server** — upload from filesystem paths, buffers, or streams\n- **Bypass permissions** — the API key grants full access to your project\n\n### What you **cannot** do\n- Real-time subscriptions (those are client-side only)\n- Direct user authentication flows (OAuth redirects happen in the browser)\n\n### When to use\n- **Backend APIs** (Express, Fastify, Next.js API routes, etc.)\n- **Server-Side Rendering (SSR)** — Next.js, SvelteKit, Nuxt, Remix, Astro\n- **Admin dashboards** — internal tools that need full project access\n- **Background jobs / cron tasks** — automated data processing\n- **Serverless functions** — Appwrite Functions themselves use the server SDK\n\n---\n\n## The SSR Pattern: Using Both Together\n\nA common architecture uses **both SDKs** in the same app:\n\n```typescript\n// 1. Admin client (reusable singleton) — for creating sessions\nconst adminClient = new Client()\n .setEndpoint(process.env.APPWRITE_ENDPOINT)\n .setProject(process.env.APPWRITE_PROJECT_ID)\n .setKey(process.env.APPWRITE_API_KEY);\n\n// 2. Session client (per-request) — for acting as the logged-in user\nconst sessionClient = new Client()\n .setEndpoint(process.env.APPWRITE_ENDPOINT)\n .setProject(process.env.APPWRITE_PROJECT_ID)\n .setSession(req.cookies['a_session_[PROJECT_ID]']);\n```\n\n- The **admin client** handles login, OAuth token creation, and session management\n- The **session client** fetches data on behalf of the authenticated user, respecting their permissions\n\n---\n\n## Quick Reference\n\n| Capability | Client SDK | Server SDK |\n|---|---|---|\n| User login/signup | ✅ | ❌ (creates sessions via admin) |\n| OAuth redirects | ✅ | ❌ (generates token, redirect happens in browser) |\n| Real-time subscriptions | ✅ | ❌ |\n| File upload from user device | ✅ | ❌ (use `InputFile` from server path) |\n| Create databases/tables | ❌ | ✅ |\n| List all users | ❌ | ✅ |\n| Execute functions | ❌ | ✅ |\n| Bypass permissions | ❌ | ✅ (via API key) |\n| Manage schemas | ❌ | ✅ |\n\n**Bottom line:** Use the **client SDK** for anything the end-user does directly. Use the **server SDK** for backend logic, admin operations, and SSR authentication flows. Many production apps use both — the server SDK to manage sessions and the client SDK to power the user interface.", "correct": true, "score": 1, "judgeReasoning": "The answer fully covers all four rubric requirements: 1) Client SDKs are explicitly described as being for frontend/user-facing apps (React, Vue, mobile apps, etc.), 2) Server SDKs use API keys (shown in code examples and explained clearly), 3) Server SDKs can bypass permissions (explicitly stated in the table and text), and 4) Client SDKs require user sessions (shown in initialization code and explained throughout). The answer goes well beyond the minimum requirements with detailed examples, code snippets, a comparison table, and the SSR pattern, all of which are accurate and relevant.", "promptTokens": 6900, "completionTokens": 1464, "totalTokens": 8364, "cost": 0.001376, "durationMs": 94329, "tokensPerSecond": 15.52, "toolCallCount": 1 }, { "questionId": "fund-7", "category": "foundation", "type": "mcq", "question": "How is Appwrite typically deployed?", "choices": [ "Using Docker containers", "As a native OS application", "As a browser extension", "Through npm global install" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 14025, "completionTokens": 425, "totalTokens": 14450, "cost": 0.002083, "durationMs": 45251, "tokensPerSecond": 9.39, "toolCallCount": 2 }, { "questionId": "fund-8", "category": "foundation", "type": "free-form", "question": "Describe how Appwrite's permission model works. What are the different permission types available?", "correctAnswer": "Appwrite uses a flexible permission system where each resource (document, file, etc.) has its own permission list. Permissions use Permission.read/create/update/delete with roles like Role.any(), Role.guests(), Role.users(), Role.user(id), Role.team(id), Role.member(id), Role.label(label).", "rubric": "Must mention: 1) Permission types (read, create, update, delete), 2) At least 3 role types, 3) That permissions are set on resources", "modelAnswer": "# Appwrite's Permission Model\n\nAppwrite uses a **default-deny** permission model. By default, **no user has access** to any resource unless permissions are explicitly granted. Permissions are evaluated at the resource level (individual rows, files, etc.) and can optionally be inherited from the parent collection (table or bucket).\n\n---\n\n## How Permissions Work\n\nPermissions are **arrays of permission strings**. Each string combines an **action** with a **role target** using the format:\n\n```\n(\"\")\n```\n\nFor example: `read(\"any\")` means \"anyone can read this resource.\"\n\nYou build these strings using the `Permission` and `Role` helper classes:\n\n```typescript\nimport { Permission, Role } from 'appwrite';\n```\n\n### Permission Evaluation Order\n\n1. If a resource (row/file) has **its own permissions**, those are used exclusively.\n2. If a resource has **no permissions set**, the parent collection's (table/bucket) default permissions apply.\n3. If neither the resource nor the collection has permissions, **access is denied** to everyone — including the creator.\n\n> ⚠️ **Important:** The creator of a resource does **not** automatically get access. You must explicitly grant permissions to the creator if you want them to have access.\n\n---\n\n## Permission Actions (What you can do)\n\n| Action | Description |\n|--------|-------------|\n| `read` | View / read the resource |\n| `create` | Create new resources in the collection |\n| `update` | Modify an existing resource |\n| `delete` | Remove a resource |\n| `write` | **Shorthand** — grants `create` + `update` + `delete` together |\n\nThese are used with the `Permission` helper:\n\n```typescript\nPermission.read(Role.any()) // read(\"any\")\nPermission.create(Role.any()) // create(\"any\")\nPermission.update(Role.any()) // update(\"any\")\nPermission.delete(Role.any()) // delete(\"any\")\nPermission.write(Role.any()) // write(\"any\") — shorthand for create+update+delete\n```\n\n---\n\n## Role Targets (Who gets access)\n\n| Role | Description | Example |\n|------|-------------|---------|\n| `Role.any()` | **Anyone** — includes unauthenticated guests | `read(\"any\")` |\n| `Role.guests()` | **Unauthenticated users only** | `read(\"guests\")` |\n| `Role.users()` | **Any authenticated user** | `read(\"users\")` |\n| `Role.user(\"[USER_ID]\")` | **A specific user** by their user ID | `read(\"user:abc123\")` |\n| `Role.team(\"[TEAM_ID]\")` | **All members** of a team | `read(\"team:team123\")` |\n| `Role.team(\"[TEAM_ID]\", \"[ROLE]\")` | **Team members with a specific role** (e.g., `\"admin\"`, `\"editor\"`) | `update(\"team:team123/admin\")` |\n| `Role.label(\"[LABEL]\")` | **Users with a specific label** (custom attribute) | `read(\"label:premium\")` |\n| `Role.member(\"[MEMBERSHIP_ID]\")` | **A specific team membership** | `read(\"member:membership123\")` |\n\n---\n\n## Setting Permissions on Resources\n\n### Database Rows\n\n```typescript\nconst doc = await tablesDB.createRow({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n rowId: ID.unique(),\n data: { title: 'Hello World' },\n permissions: [\n Permission.read(Role.user('[USER_ID]')), // specific user can read\n Permission.update(Role.user('[USER_ID]')), // specific user can update\n Permission.read(Role.team('[TEAM_ID]')), // all team members can read\n Permission.read(Role.any()), // anyone (including guests) can read\n ]\n});\n```\n\n### Storage Files\n\n```typescript\nconst file = await storage.createFile({\n bucketId: '[BUCKET_ID]',\n fileId: ID.unique(),\n file: document.getElementById('file-input').files[0],\n permissions: [\n Permission.read(Role.any()),\n Permission.update(Role.user('[USER_ID]')),\n Permission.delete(Role.user('[USER_ID]')),\n ]\n});\n```\n\n### Table/Bucket-Level Default Permissions\n\nYou can set default permissions on the collection itself. These apply to any new row/file that doesn't have its own permissions:\n\n```json\n{\n \"tables\": [{\n \"$id\": \"articles\",\n \"$permissions\": [\n \"create(\\\"users\\\")\",\n \"read(\\\"any\\\")\"\n ],\n \"rowSecurity\": false,\n ...\n }]\n}\n```\n\nWhen `rowSecurity` is `false` (default), the table-level permissions act as defaults for rows. When `rowSecurity` is `true`, each row **must** have its own permissions set explicitly.\n\n---\n\n## Common Mistakes\n\n| Mistake | Consequence |\n|---------|-------------|\n| **Forgetting to set permissions** | Resource is inaccessible to everyone, including the creator |\n| **`Role.any()` with `write`/`update`/`delete`** | Any unauthenticated user can modify or delete the resource |\n| **`Permission.read(Role.any())` on sensitive data** | Makes the resource publicly readable |\n| **Not granting the creator access** | The user who created the resource cannot read or modify it |\n\n---\n\n## Summary\n\n- **Default: deny all** — you must explicitly grant access\n- **Actions:** `read`, `create`, `update`, `delete`, `write` (shorthand)\n- **Roles:** `any()`, `guests()`, `users()`, `user(id)`, `team(id)`, `team(id, role)`, `label(name)`, `member(id)`\n- **Levels:** Resource-level (row/file) overrides collection-level (table/bucket) defaults\n- **No automatic creator access** — always grant permissions to the creating user if needed", "correct": true, "score": 1, "judgeReasoning": "The model's answer fully satisfies all three rubric requirements: 1) It clearly lists all permission types (read, create, update, delete, and write as shorthand), 2) It mentions well more than 3 role types including Role.any(), Role.guests(), Role.users(), Role.user(id), Role.team(id), Role.label(label), and Role.member(id), and 3) It explicitly explains that permissions are set on resources (rows, files) with detailed examples. The answer goes beyond the reference answer by also explaining inheritance from parent collections, the default-deny model, common mistakes, and code examples, making it exceptionally comprehensive.", "promptTokens": 13576, "completionTokens": 1675, "totalTokens": 15251, "cost": 0.00237, "durationMs": 106656, "tokensPerSecond": 15.7, "toolCallCount": 2 }, { "questionId": "fund-9", "category": "foundation", "type": "mcq", "question": "What is a \"project\" in Appwrite?", "choices": [ "A single TablesDB table", "A Git repository connection", "An isolated container for your application's backend services and resources", "A deployment environment" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14059, "completionTokens": 436, "totalTokens": 14495, "cost": 0.00209, "durationMs": 50001, "tokensPerSecond": 8.72, "toolCallCount": 2 }, { "questionId": "fund-10", "category": "foundation", "type": "mcq", "question": "Which of the following is a valid way to subscribe to Appwrite Realtime events?", "choices": [ "HTTP long polling", "Server-Sent Events (SSE)", "WebSocket subscription using realtime.subscribe()", "GraphQL subscriptions" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7384, "completionTokens": 226, "totalTokens": 7610, "cost": 0.001097, "durationMs": 15348, "tokensPerSecond": 14.73, "toolCallCount": 1 }, { "questionId": "fund-11", "category": "foundation", "type": "mcq", "question": "What is required when initializing the Appwrite client in a frontend app?", "choices": [ "Only the API key", "Project ID only", "Endpoint, Project ID, and a secret key", "Endpoint and Project ID at minimum" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7392, "completionTokens": 202, "totalTokens": 7594, "cost": 0.001091, "durationMs": 6488, "tokensPerSecond": 31.13, "toolCallCount": 1 }, { "questionId": "fund-12", "category": "foundation", "type": "mcq", "question": "How do Webhooks differ from Realtime in Appwrite?", "choices": [ "Realtime uses WebSockets for live client updates; Webhooks are HTTP callbacks to your server when events occur", "They are the same feature with different names", "Webhooks are for client-side only", "Realtime is server-to-server only" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 14151, "completionTokens": 290, "totalTokens": 14441, "cost": 0.002062, "durationMs": 8160, "tokensPerSecond": 35.54, "toolCallCount": 2 }, { "questionId": "fund-13", "category": "foundation", "type": "mcq", "question": "What are API key scopes in Appwrite?", "choices": [ "Geographic regions for the key", "Rate limit tiers", "Permissions that limit what the API key can access (e.g. tablesdb.read, users.write)", "Encryption levels" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20635, "completionTokens": 442, "totalTokens": 21077, "cost": 0.003013, "durationMs": 42038, "tokensPerSecond": 10.51, "toolCallCount": 2 }, { "questionId": "fund-14", "category": "foundation", "type": "mcq", "question": "Which role allows access to any user (including unauthenticated)?", "choices": [ "Role.any()", "Role.users()", "Role.guests()", "Role.public()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7348, "completionTokens": 254, "totalTokens": 7602, "cost": 0.0011, "durationMs": 6581, "tokensPerSecond": 38.6, "toolCallCount": 1 }, { "questionId": "fund-15", "category": "foundation", "type": "mcq", "question": "How is Appwrite typically run in production?", "choices": [ "As a single PHP script", "Only on Appwrite Cloud", "Using Docker containers (e.g. docker compose)", "As a serverless deployment" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14061, "completionTokens": 542, "totalTokens": 14603, "cost": 0.00212, "durationMs": 47495, "tokensPerSecond": 11.41, "toolCallCount": 2 }, { "questionId": "fund-16", "category": "foundation", "type": "mcq", "question": "What does Role.guests() represent?", "choices": [ "Users who have not verified their email", "Users in a guest team", "Unauthenticated users (no session)", "Anonymous session users only" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20590, "completionTokens": 1123, "totalTokens": 21713, "cost": 0.003197, "durationMs": 28542, "tokensPerSecond": 39.35, "toolCallCount": 2 }, { "questionId": "fund-17", "category": "foundation", "type": "mcq", "question": "Can you use both REST and GraphQL in the same Appwrite project?", "choices": [ "Yes, Appwrite supports both REST and GraphQL APIs for the same data", "No, you must choose one", "Only if you use separate projects", "GraphQL is only for the Console" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 14113, "completionTokens": 294, "totalTokens": 14407, "cost": 0.002058, "durationMs": 9105, "tokensPerSecond": 32.29, "toolCallCount": 2 }, { "questionId": "fund-18", "category": "foundation", "type": "mcq", "question": "Which permission type allows creating a new resource?", "choices": [ "Permission.read()", "Permission.write()", "Permission.create()", "Permission.insert()" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7324, "completionTokens": 1263, "totalTokens": 8587, "cost": 0.001379, "durationMs": 41420, "tokensPerSecond": 30.49, "toolCallCount": 1 }, { "questionId": "fund-19", "category": "foundation", "type": "mcq", "question": "What is the purpose of adding a platform (e.g. Web app) to an Appwrite project?", "choices": [ "To enable billing for that platform", "To install a plugin", "To register allowed origins/hosts for auth redirects and API access", "To assign a subdomain" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20656, "completionTokens": 841, "totalTokens": 21497, "cost": 0.003127, "durationMs": 31212, "tokensPerSecond": 26.94, "toolCallCount": 2 }, { "questionId": "fund-20", "category": "foundation", "type": "free-form", "question": "Name at least three Appwrite services (product areas) and what each is used for.", "correctAnswer": "Examples: Auth (users, sessions, OAuth, MFA, teams), TablesDB (tables, rows, queries, relationships), Storage (buckets, files, previews), Functions (serverless execution), Realtime (WebSocket subscriptions), Messaging (email, SMS, push), Sites (static/dynamic hosting), GraphQL (query API).", "rubric": "Must name at least 3 services with correct purpose. Accept Auth, TablesDB, Storage, Functions, Realtime, Messaging, Sites, GraphQL, CLI, etc. with accurate descriptions.", "modelAnswer": "Here are three core Appwrite services and what they're used for:\n\n1. **Authentication (Account / Users)** – Handles user registration, login, session management, OAuth 2.0 (Google, GitHub, etc.), email/password auth, magic URLs, and anonymous sessions. It also provides server-side user management (create, list, update, delete users).\n\n2. **Database (TablesDB)** – A structured data store where you create databases, tables, and rows. Supports querying with filters, sorting, pagination, full-text search, relationships, and real-time subscriptions. You define typed columns (varchar, integer, boolean, etc.) and set granular permissions per row.\n\n3. **Storage** – Manages file uploads and downloads. You create buckets, upload files (from client-side inputs or server-side paths), generate image previews with transformations, and control access with permissions. Supports any file type and size.\n\nOther notable services include **Functions** (serverless compute for running custom backend code), **Messaging** (email, push notifications, and SMS), **Teams** (group management with role-based access), and **Sites** (deploying static or server-side rendered web apps).", "correct": true, "score": 1, "judgeReasoning": "The answer correctly names and describes at least three Appwrite services: Authentication (with accurate description of user management, OAuth, sessions), TablesDB/Database (with accurate description of tables, rows, queries, relationships), and Storage (with accurate description of buckets, files, previews). Additional services like Functions, Messaging, Teams, and Sites are also mentioned with correct descriptions. All descriptions align with the reference answer.", "promptTokens": 13577, "completionTokens": 581, "totalTokens": 14158, "cost": 0.002063, "durationMs": 19376, "tokensPerSecond": 29.99, "toolCallCount": 2 }, { "questionId": "auth-1", "category": "auth", "type": "mcq", "question": "Which method creates a new email/password account in Appwrite?", "choices": [ "account.create()", "account.createEmailPasswordSession()", "auth.register()", "users.create()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7344, "completionTokens": 292, "totalTokens": 7636, "cost": 0.00111, "durationMs": 15051, "tokensPerSecond": 19.4, "toolCallCount": 1 }, { "questionId": "auth-2", "category": "auth", "type": "mcq", "question": "What is the correct method to create an email/password session (log in) using the Appwrite client SDK?", "choices": [ "account.createEmailPasswordSession()", "account.login()", "auth.signIn()", "account.create()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7368, "completionTokens": 196, "totalTokens": 7564, "cost": 0.001086, "durationMs": 5892, "tokensPerSecond": 33.27, "toolCallCount": 1 }, { "questionId": "auth-3", "category": "auth", "type": "mcq", "question": "How does Appwrite handle OAuth2 authentication?", "choices": [ "It only supports OAuth2 through custom functions", "OAuth2 requires a separate plugin installation", "It provides built-in OAuth2 support with createOAuth2Session() for 40+ providers", "It uses JWT tokens exclusively" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7456, "completionTokens": 145, "totalTokens": 7601, "cost": 0.001084, "durationMs": 10524, "tokensPerSecond": 13.78, "toolCallCount": 1 }, { "questionId": "auth-4", "category": "auth", "type": "free-form", "question": "Write the code to implement email/password signup and login using the Appwrite Web SDK. Include setting up the client, creating an account, and creating a session.", "correctAnswer": "The Appwrite Web SDK requires initializing a Client with endpoint and project ID, then creating an Account instance. Registration uses account.create(ID.unique(), email, password, name). Login uses account.createEmailPasswordSession(email, password).", "rubric": "Must include: 1) Client initialization with setEndpoint and setProject, 2) Account service instantiation, 3) account.create() with ID.unique(), email, password, 4) account.createEmailPasswordSession(), 5) Correct imports from appwrite", "modelAnswer": "Here's how to implement email/password signup and login with the Appwrite Web SDK:\n\n## 1. Install the SDK\n\n```bash\nnpm install appwrite\n```\n\n## 2. Set Up the Client & Implement Auth\n\n```typescript\nimport { Client, Account, ID } from 'appwrite';\n\n// Initialize the client\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1') // Replace with your endpoint\n .setProject('[PROJECT_ID]'); // Replace with your project ID\n\nconst account = new Account(client);\n\n// ─── SIGNUP ───────────────────────────────────────────────\nasync function signUp(email: string, password: string, name?: string) {\n try {\n const user = await account.create({\n userId: ID.unique(),\n email,\n password,\n name, // optional\n });\n console.log('Account created:', user);\n return user;\n } catch (error) {\n console.error('Signup failed:', error);\n throw error;\n }\n}\n\n// ─── LOGIN ────────────────────────────────────────────────\nasync function logIn(email: string, password: string) {\n try {\n const session = await account.createEmailPasswordSession({\n email,\n password,\n });\n console.log('Session created:', session);\n return session;\n } catch (error) {\n console.error('Login failed:', error);\n throw error;\n }\n}\n\n// ─── USAGE ────────────────────────────────────────────────\n// Sign up\nawait signUp('user@example.com', 'securePassword123', 'John Doe');\n\n// Log in\nawait logIn('user@example.com', 'securePassword123');\n\n// Get the currently logged-in user\nconst user = await account.get();\nconsole.log('Current user:', user);\n\n// Log out\nawait account.deleteSession({ sessionId: 'current' });\n```\n\n## Key Points\n\n- **`ID.unique()`** generates a unique user ID automatically.\n- **`account.create()`** registers a new user account.\n- **`account.createEmailPasswordSession()`** creates a session (logs the user in).\n- **`account.get()`** retrieves the currently authenticated user.\n- **`account.deleteSession({ sessionId: 'current' })`** logs the user out.\n\n## Error Handling\n\nWrap calls in try/catch and handle `AppwriteException`:\n\n```typescript\nimport { AppwriteException } from 'appwrite';\n\ntry {\n await account.createEmailPasswordSession({ email, password });\n} catch (err) {\n if (err instanceof AppwriteException) {\n console.error(err.message); // e.g. \"Invalid credentials\"\n console.error(err.code); // e.g. 401\n console.error(err.type); // e.g. \"user_invalid_credentials\"\n }\n}\n```\n\nThat's all you need for basic email/password authentication with the Appwrite Web SDK!", "correct": true, "score": 0.85, "judgeReasoning": "The answer covers all 5 required rubric items: (1) Client initialization with setEndpoint and setProject, (2) Account service instantiation, (3) account.create() with ID.unique(), email, and password, (4) account.createEmailPasswordSession(), and (5) correct imports from appwrite. However, the method signatures used are slightly incorrect for the standard Appwrite Web SDK - account.create() typically takes positional arguments (userId, email, password, name) rather than an object, and account.createEmailPasswordSession() similarly takes positional arguments (email, password) rather than an object. These are minor but real inaccuracies in the API usage that could cause runtime errors. The overall structure, concepts, and coverage are excellent.", "promptTokens": 6928, "completionTokens": 755, "totalTokens": 7683, "cost": 0.001181, "durationMs": 19117, "tokensPerSecond": 39.49, "toolCallCount": 1 }, { "questionId": "auth-5", "category": "auth", "type": "mcq", "question": "What is the purpose of Teams in Appwrite Auth?", "choices": [ "To organize TablesDB tables", "To create separate billing accounts", "To manage deployment environments", "To group users and manage role-based access to resources" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7382, "completionTokens": 229, "totalTokens": 7611, "cost": 0.001098, "durationMs": 19844, "tokensPerSecond": 11.54, "toolCallCount": 1 }, { "questionId": "auth-6", "category": "auth", "type": "mcq", "question": "How can you enable Multi-Factor Authentication (MFA) in Appwrite?", "choices": [ "Through the account.createMfaChallenge() and account.updateMFA() methods", "MFA is always enabled by default", "By installing a third-party MFA plugin", "MFA is not supported in Appwrite" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7448, "completionTokens": 360, "totalTokens": 7808, "cost": 0.001144, "durationMs": 18669, "tokensPerSecond": 19.28, "toolCallCount": 1 }, { "questionId": "auth-7", "category": "auth", "type": "free-form", "question": "Explain how Appwrite sessions work. How are they created, managed, and deleted? What about session limits?", "correctAnswer": "Sessions represent authenticated user connections. They are created during login, stored as HTTP-only cookies, limited to 10 per user, and can be managed via account.listSessions(), account.deleteSession(), and account.deleteSessions().", "rubric": "Must mention: 1) Sessions created on authentication, 2) Session tokens/cookies, 3) Session limit (10), 4) Methods to list/delete sessions, 5) Session expiry", "modelAnswer": "# How Appwrite Sessions Work\n\nAppwrite uses **server-side sessions** to authenticate users. When a user logs in, Appwrite creates a session on the server and returns a **session secret** — a token that the client must send with subsequent requests to prove their identity.\n\n---\n\n## 1. How Sessions Are Created\n\nSessions are created through the **Account** service. There are several authentication methods, each producing a session:\n\n### Email/Password Login\n\n```typescript\nconst account = new Account(client);\nconst session = await account.createEmailPasswordSession({\n email: 'user@example.com',\n password: 'password123'\n});\n// session.secret — the session token\n// session.userId — the authenticated user's ID\n// session.expire — ISO 8601 expiration timestamp\n```\n\n### OAuth2 Login (Web)\n\n```typescript\naccount.createOAuth2Session({\n provider: OAuthProvider.Github,\n success: 'https://example.com/success',\n failure: 'https://example.com/fail',\n scopes: ['repo', 'user'] // optional provider-specific scopes\n});\n```\n\nThe browser is redirected to the OAuth provider, then back to your success URL with the session automatically established via a cookie.\n\n### OAuth2 Login (React Native / Custom Flow)\n\nSince `createOAuth2Session()` doesn't work on React Native, you use a two-step token exchange:\n\n```typescript\n// Step 1: Get an OAuth login URL\nconst loginUrl = await account.createOAuth2Token({\n provider: OAuthProvider.Github,\n success: 'myapp://callback',\n failure: 'myapp://callback',\n});\n\n// Step 2: After redirect, exchange the token for a session\nawait account.createSession({\n userId: url.searchParams.get('userId'),\n secret: url.searchParams.get('secret'),\n});\n```\n\n### Server-Side Rendering (SSR) Login\n\nSSR apps use the **admin client** (with an API key) to create sessions, then set the session secret as an HTTP-only cookie:\n\n```typescript\nconst account = new Account(adminClient);\nconst session = await account.createEmailPasswordSession({\n email: req.body.email,\n password: req.body.password,\n});\n\nres.cookie('a_session_[PROJECT_ID]', session.secret, {\n httpOnly: true,\n secure: true,\n sameSite: 'strict',\n expires: new Date(session.expire),\n path: '/',\n});\n```\n\n---\n\n## 2. How Sessions Are Managed\n\n### Client-Side (Browser / React Native)\n\nThe SDK automatically stores the session secret and sends it with every request. You don't need to manually handle tokens on the client.\n\n### Server-Side (SSR)\n\nYou must create a **per-request session client** that uses the session cookie:\n\n```typescript\n// Extract the session cookie\nconst sessionCookie = req.cookies['a_session_[PROJECT_ID]'];\n\n// Create a fresh client for this request\nconst sessionClient = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1')\n .setProject('[PROJECT_ID]')\n .setSession(sessionCookie);\n\n// Optionally forward the user-agent for audit logs\nsessionClient.setForwardedUserAgent(req.headers['user-agent']);\n\n// Now act on behalf of the user\nconst account = new Account(sessionClient);\nconst user = await account.get();\n```\n\n> **Important:** Never share session clients between requests. Create a new one per request.\n\n### Session Properties\n\nEach session object (`Models.Session`) contains:\n- **`$id`** — unique session ID\n- **`userId`** — the authenticated user\n- **`secret`** — the session token (used in cookies/headers)\n- **`expire`** — ISO 8601 expiration date\n- **`provider`** — auth method used (e.g., `email`, `github`, `google`)\n- **`ip`** — IP address at creation\n- **`userAgent`** — user agent string at creation\n- **`current`** — boolean indicating if this is the current session\n\n### Session Cookie\n\nThe cookie name is always `a_session_`. It must be:\n- `httpOnly` — not accessible via JavaScript (prevents XSS)\n- `secure` — only sent over HTTPS\n- `sameSite: 'strict'` — prevents CSRF\n\n---\n\n## 3. How Sessions Are Deleted\n\n### Logout (Client-Side)\n\n```typescript\n// Delete the current session\nawait account.deleteSession({ sessionId: 'current' });\n\n// Delete a specific session by ID\nawait account.deleteSession({ sessionId: '[SESSION_ID]' });\n```\n\n### Logout (Server-Side)\n\n```typescript\nconst account = new Account(adminClient);\nawait account.deleteSession({ sessionId: 'current' });\n\n// Also clear the cookie\nres.clearCookie('a_session_[PROJECT_ID]');\n```\n\n### Delete All Sessions\n\nYou can delete all sessions for a user (logging them out everywhere):\n\n```typescript\n// Using the Account service (client-side, requires current session)\nawait account.deleteSessions();\n\n// Using the Users service (server-side, requires API key)\nconst users = new Users(adminClient);\nawait users.deleteSessions({ userId: '[USER_ID]' });\n```\n\n---\n\n## 4. Session Limits\n\nAppwrite enforces a **maximum number of active sessions per user**. This is configured at the project level under **Auth > Security > Sessions Limit**.\n\n### Default & Configuration\n\n- **Default limit:** 10 sessions per user\n- **Configurable range:** 1–100 sessions\n- **Set via CLI** in `appwrite.config.json`:\n\n```json\n{\n \"settings\": {\n \"auth\": {\n \"security\": {\n \"sessionsLimit\": 10\n }\n }\n }\n}\n```\n\nOr via the Appwrite Console under **Auth > Settings > Security**.\n\n### What Happens at the Limit\n\nWhen a user reaches the session limit and tries to create a new session, the **oldest session** is automatically deleted to make room. This ensures the user can always log in without manual session cleanup.\n\n### Best Practices\n\n- **Keep the default (10)** for most applications — it allows users to be logged in across multiple devices/browsers.\n- **Lower the limit** for high-security apps (e.g., banking) to reduce the attack surface.\n- **Raise the limit** if your users commonly use many devices, but be aware of the security trade-off.\n- **Use `deleteSessions()`** on password changes or security-sensitive events to force re-authentication on all devices.\n\n---\n\n## Summary\n\n| Aspect | Details |\n|--------|---------|\n| **Creation** | `createEmailPasswordSession()`, `createOAuth2Session()`, `createSession()` (token exchange) |\n| **Storage** | Client SDK auto-manages; SSR uses `a_session_` HTTP-only cookie |\n| **Authentication** | Session secret sent via `X-Appwrite-Session` header or cookie |\n| **Deletion** | `deleteSession({ sessionId: 'current' })` or `deleteSessions()` for all |\n| **Limit** | Configurable 1–100 (default 10); oldest session auto-removed when limit reached |\n| **Expiration** | Configurable per project; sessions have an `expire` timestamp |", "correct": true, "score": 0.95, "judgeReasoning": "The model's answer is comprehensive and covers all five rubric criteria: 1) Sessions created on authentication (multiple methods shown), 2) Session tokens/cookies (HTTP-only cookies, session secrets, cookie naming convention), 3) Session limit of 10 (explicitly mentioned as default), 4) Methods to list/delete sessions (deleteSession, deleteSessions, users.deleteSessions shown), 5) Session expiry (expire timestamp mentioned). The answer goes well beyond the reference answer with detailed code examples, SSR handling, OAuth flows, and security best practices. Minor note: listSessions() is not explicitly shown (only delete methods), but the answer is otherwise thorough and accurate. The configurable range of 1-100 sessions is a reasonable claim about Appwrite's console settings.", "promptTokens": 13585, "completionTokens": 2031, "totalTokens": 15616, "cost": 0.002471, "durationMs": 99149, "tokensPerSecond": 20.48, "toolCallCount": 2 }, { "questionId": "auth-8", "category": "auth", "type": "mcq", "question": "What does Role.users() represent in Appwrite's permission system?", "choices": [ "Only admin users", "Users in a specific team", "Any user who has an active session (authenticated users)", "Users with verified email addresses only" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7390, "completionTokens": 578, "totalTokens": 7968, "cost": 0.001196, "durationMs": 28293, "tokensPerSecond": 20.43, "toolCallCount": 1 }, { "questionId": "auth-9", "category": "auth", "type": "mcq", "question": "How do you authenticate server-side requests in Appwrite?", "choices": [ "Using user session tokens only", "Using OAuth2 tokens", "Server-side requests don't need authentication", "Using an API key with setKey() on the server SDK client" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7406, "completionTokens": 226, "totalTokens": 7632, "cost": 0.0011, "durationMs": 18114, "tokensPerSecond": 12.48, "toolCallCount": 1 }, { "questionId": "auth-10", "category": "auth", "type": "mcq", "question": "Which method is used to get the currently logged-in user in Appwrite?", "choices": [ "account.get()", "users.getCurrent()", "auth.currentUser()", "account.getSession()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7348, "completionTokens": 145, "totalTokens": 7493, "cost": 0.001069, "durationMs": 11588, "tokensPerSecond": 12.51, "toolCallCount": 1 }, { "questionId": "auth-11", "category": "auth", "type": "mcq", "question": "How do you implement password recovery (forgot password) in Appwrite?", "choices": [ "account.resetPassword(email)", "Only through the Console", "account.createRecovery(email, url) to send the email, then account.updateRecovery(userId, secret, password, passwordAgain) when the user submits the new password", "account.sendPasswordReset(email)" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7496, "completionTokens": 403, "totalTokens": 7899, "cost": 0.001162, "durationMs": 17009, "tokensPerSecond": 23.69, "toolCallCount": 1 }, { "questionId": "auth-12", "category": "auth", "type": "mcq", "question": "How long is the password recovery link sent by createRecovery valid?", "choices": [ "1 hour", "15 minutes", "24 hours", "Until used" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7338, "completionTokens": 385, "totalTokens": 7723, "cost": 0.001135, "durationMs": 23412, "tokensPerSecond": 16.44, "toolCallCount": 1 }, { "questionId": "auth-13", "category": "auth", "type": "mcq", "question": "What is the default maximum number of sessions per user in an Appwrite project?", "choices": [ "1", "5", "10", "Unlimited" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14007, "completionTokens": 344, "totalTokens": 14351, "cost": 0.002057, "durationMs": 20810, "tokensPerSecond": 16.53, "toolCallCount": 2 }, { "questionId": "auth-14", "category": "auth", "type": "mcq", "question": "Which method deletes all sessions for the current user?", "choices": [ "account.logout()", "account.deleteAllSessions()", "account.deleteSession() with no arguments", "account.deleteSessions()" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7370, "completionTokens": 810, "totalTokens": 8180, "cost": 0.001259, "durationMs": 33986, "tokensPerSecond": 23.83, "toolCallCount": 1 }, { "questionId": "auth-15", "category": "auth", "type": "mcq", "question": "What does Role.team(teamId) represent in Appwrite permissions?", "choices": [ "Only users who are members of the specified team", "Any user who is in any team", "Team administrators only", "The team resource itself" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7386, "completionTokens": 214, "totalTokens": 7600, "cost": 0.001094, "durationMs": 6886, "tokensPerSecond": 31.08, "toolCallCount": 1 }, { "questionId": "auth-16", "category": "auth", "type": "mcq", "question": "Can you create anonymous (guest) sessions in Appwrite?", "choices": [ "No, anonymous sessions are not supported", "Only with a special API key", "Yes, using account.createAnonymousSession()", "Only in development mode" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7386, "completionTokens": 434, "totalTokens": 7820, "cost": 0.001156, "durationMs": 7617, "tokensPerSecond": 56.98, "toolCallCount": 1 }, { "questionId": "auth-17", "category": "auth", "type": "mcq", "question": "When using createRecovery, what requirement applies to the redirect URL?", "choices": [ "It must be HTTPS only", "It can be any URL", "It must match the app's origin exactly", "It must be from a domain you added in the project's platforms in the Console" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7430, "completionTokens": 586, "totalTokens": 8016, "cost": 0.001204, "durationMs": 19154, "tokensPerSecond": 30.59, "toolCallCount": 1 }, { "questionId": "auth-18", "category": "auth", "type": "mcq", "question": "Which MFA factor types does Appwrite support?", "choices": [ "TOTP, phone (SMS), and email", "TOTP only", "TOTP and hardware keys only", "Email only" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 20599, "completionTokens": 540, "totalTokens": 21139, "cost": 0.003035, "durationMs": 40200, "tokensPerSecond": 13.43, "toolCallCount": 2 }, { "questionId": "auth-19", "category": "auth", "type": "mcq", "question": "How do you add a user to a team using the client SDK?", "choices": [ "teams.addMember(teamId, userId)", "account.joinTeam(teamId)", "teams.createMembership(teamId, roles, email or userId, etc.)", "users.assignTeam(userId, teamId)" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7444, "completionTokens": 390, "totalTokens": 7834, "cost": 0.001151, "durationMs": 19362, "tokensPerSecond": 20.14, "toolCallCount": 1 }, { "questionId": "auth-20", "category": "auth", "type": "free-form", "question": "Explain the difference between account.deleteSession(sessionId) and account.deleteSessions(). When would you use each?", "correctAnswer": "deleteSession(sessionId) removes a single session by ID (e.g. logout on current device). deleteSessions() removes all sessions for the current user (e.g. logout everywhere). Use deleteSession for single-device logout; use deleteSessions for security when user wants to sign out of all devices.", "rubric": "Must mention: 1) deleteSession removes one session by ID, 2) deleteSessions removes all sessions for the user, 3) Use case for each (e.g. logout one device vs logout everywhere)", "modelAnswer": "Great question! Let me explain the difference between these two Account methods.\n\n## `account.deleteSession(sessionId)`\n\n**Deletes a single, specific session** by its ID.\n\n```typescript\n// Delete the current session (log out from this device)\nawait account.deleteSession({ sessionId: 'current' });\n\n// Delete a specific session by its ID (e.g., log out a different device)\nawait account.deleteSession({ sessionId: '[SESSION_ID]' });\n```\n\n- You can pass `'current'` as the session ID to target the session making the request.\n- Or pass a specific session ID string to target another session (e.g., one from a different device/browser).\n\n## `account.deleteSessions()`\n\n**Deletes all sessions** for the authenticated user at once.\n\n```typescript\n// Log out from ALL devices\nawait account.deleteSessions();\n```\n\n- No parameters needed.\n- This is a bulk operation — it removes every active session the user has across all devices and browsers.\n\n---\n\n## When to Use Each\n\n| Scenario | Use |\n|----------|-----|\n| **Log out from the current device/browser** (e.g., a \"Sign Out\" button) | `deleteSession({ sessionId: 'current' })` |\n| **Log out a specific other device** (e.g., \"log out that old phone\") | `deleteSession({ sessionId: '[SESSION_ID]' })` |\n| **\"Log out everywhere\" / \"Sign out of all devices\"** security feature | `deleteSessions()` |\n| **Password change flow** — invalidate all existing sessions so the user must re-login | `deleteSessions()` |\n| **Account compromise / suspicious activity** — force logout from all devices | `deleteSessions()` |\n\n### Key Differences\n\n| Aspect | `deleteSession` | `deleteSessions` |\n|--------|----------------|------------------|\n| **Scope** | One session | All sessions |\n| **Parameter** | `sessionId` (string or `'current'`) | None |\n| **Use case** | Targeted logout | Bulk logout / security reset |\n| **Typical UI** | \"Sign Out\" button | \"Sign Out of All Devices\" button |\n\n### Example: Building a Security Settings Page\n\n```typescript\n// User clicks \"Sign Out\" on their current device\nawait account.deleteSession({ sessionId: 'current' });\n\n// User clicks \"Sign Out of All Other Devices\" (keep current)\nconst sessions = await account.listSessions();\nfor (const session of sessions.sessions) {\n if (session.$id !== currentSessionId) {\n await account.deleteSession({ sessionId: session.$id });\n }\n}\n\n// User clicks \"Sign Out Everywhere\" (including current)\nawait account.deleteSessions();\n```\n\n**Bottom line:** Use `deleteSession` for granular, single-session control (like a standard logout), and `deleteSessions` when you need to revoke all sessions at once (security resets, password changes, or \"log out everywhere\" features).", "correct": true, "score": 1, "judgeReasoning": "The model's answer fully covers all three rubric requirements: 1) deleteSession removes one session by ID (including the special 'current' value), 2) deleteSessions removes all sessions for the user, and 3) clear use cases for each (single device logout vs logout everywhere/security resets). The answer goes beyond the reference with helpful code examples, comparison tables, and additional scenarios like password change flows, all of which are accurate and relevant.", "promptTokens": 6904, "completionTokens": 1003, "totalTokens": 7907, "cost": 0.001247, "durationMs": 52802, "tokensPerSecond": 19, "toolCallCount": 1 }, { "questionId": "db-1", "category": "tablesdb", "type": "mcq", "question": "What is the hierarchy of data organization in the current Appwrite TablesDB API?", "choices": [ "Database > Collection > Document", "TablesDB > Table > Row", "Schema > Collection > Record", "Project > Database > Table" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 7380, "completionTokens": 356, "totalTokens": 7736, "cost": 0.001133, "durationMs": 11331, "tokensPerSecond": 31.42, "toolCallCount": 1 }, { "questionId": "db-2", "category": "tablesdb", "type": "mcq", "question": "Which column types are supported in Appwrite TablesDB tables?", "choices": [ "String, Integer, Float only", "String, Number, Boolean only", "Any JSON type without schema validation", "Varchar/Text, Integer, Float, Boolean, DateTime, Email, URL, IP, Enum, Relationship" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 14131, "completionTokens": 507, "totalTokens": 14638, "cost": 0.00212, "durationMs": 10460, "tokensPerSecond": 48.47, "toolCallCount": 2 }, { "questionId": "db-3", "category": "tablesdb", "type": "free-form", "question": "Write code using the current Appwrite Web SDK TablesDB API to create a database, a table with columns, and insert a row. Use a \"tasks\" table with title (varchar), completed (boolean), and dueDate (datetime) columns.", "correctAnswer": "Use the TablesDB service to create a database, create a tasks table with columns (varchar title, boolean completed, datetime dueDate), then createRow with the data object.", "rubric": "Must include: 1) Client + TablesDB setup, 2) tablesDB.create() for database, 3) tablesDB.createTable() for the tasks table, 4) Creating varchar, boolean, and datetime columns, 5) tablesDB.createRow() with data", "modelAnswer": "Here's the complete code using the Appwrite Web SDK's `TablesDB` API to create a database, a `tasks` table with typed columns, and insert a row.\n\n> **Important:** Creating databases and tables requires **server-side** access (an API key). Row insertion can be done from the client if the table's permissions allow it. The code below shows both parts — use the server-side block in a Node.js backend or Appwrite Function, and the client-side block in your web app.\n\n```typescript\n// ──────────────────────────────────────────────\n// 1. Imports (Web SDK)\n// ──────────────────────────────────────────────\nimport { Client, TablesDB, ID } from 'appwrite';\n\n// ──────────────────────────────────────────────\n// 2. Client setup\n// ──────────────────────────────────────────────\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1') // e.g. 'https://cloud.appwrite.io/v1'\n .setProject('');\n\nconst tablesDB = new TablesDB(client);\n\n// ──────────────────────────────────────────────\n// 3. Create database (server-side only)\n// ──────────────────────────────────────────────\n// Requires an API key with `databases.write` scope.\n// Run this in a Node.js backend or Appwrite Function.\nconst database = await tablesDB.create({\n databaseId: ID.unique(),\n name: 'My App Database'\n});\nconsole.log(`Database created: ${database.$id}`);\n\n// ──────────────────────────────────────────────\n// 4. Create \"tasks\" table with columns (server-side only)\n// ──────────────────────────────────────────────\nconst table = await tablesDB.createTable({\n databaseId: database.$id,\n tableId: ID.unique(),\n name: 'tasks',\n columns: [\n { key: 'title', type: 'varchar', size: 255, required: true },\n { key: 'completed', type: 'boolean', required: true },\n { key: 'dueDate', type: 'datetime', required: false },\n ]\n});\nconsole.log(`Table created: ${table.$id}`);\n\n// ──────────────────────────────────────────────\n// 5. Insert a row (client or server)\n// ──────────────────────────────────────────────\n// On the client, the table must have appropriate\n// permissions (e.g., `Permission.create(Role.any())`\n// or `Role.user(...)`) for the current user to write.\nconst row = await tablesDB.createRow({\n databaseId: database.$id,\n tableId: table.$id,\n rowId: ID.unique(),\n data: {\n title: 'Buy groceries',\n completed: false,\n dueDate: '2025-12-31T23:59:59.000Z' // ISO 8601 format\n }\n});\nconsole.log(`Row inserted: ${row.$id}`);\n```\n\n---\n\n## Key points\n\n| Step | Where to run | Required auth |\n|------|-------------|---------------|\n| **Create database** | Server-side (Node.js / Appwrite Function) | API key with `databases.write` scope |\n| **Create table** | Server-side | API key with `databases.write` scope |\n| **Insert row** | Client or server | Depends on table permissions |\n\n### Column types used\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `title` | `varchar` (size 255) | Inline, fully indexable, max 16,383 chars |\n| `completed` | `boolean` | `true` or `false` |\n| `dueDate` | `datetime` | ISO 8601 string (e.g. `\"2025-12-31T23:59:59.000Z\"`) |\n\n### Server-side setup (if you need a complete backend script)\n\n```typescript\n// server-side.mjs\nimport { Client, TablesDB, ID } from 'node-appwrite';\n\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1')\n .setProject(process.env.APPWRITE_PROJECT_ID)\n .setKey(process.env.APPWRITE_API_KEY); // <-- API key required\n\nconst tablesDB = new TablesDB(client);\n\nasync function setup() {\n // ... same code as above ...\n}\nsetup().catch(console.error);\n```\n\n### Client-side row insertion with permissions\n\nWhen inserting from the client, make sure the table (or the row itself) grants the current user `create` access. For example, if you want any authenticated user to create tasks:\n\n```typescript\n// Set during table creation (server-side)\nawait tablesDB.createTable({\n databaseId: database.$id,\n tableId: ID.unique(),\n name: 'tasks',\n columns: [ /* ... */ ],\n permissions: [\n Permission.create(Role.any()), // any authenticated user can create rows\n Permission.read(Role.any()),\n Permission.update(Role.any()),\n Permission.delete(Role.any()),\n ]\n});\n```\n\nOr set per-row permissions at insert time:\n\n```typescript\nimport { Permission, Role } from 'appwrite';\n\nconst row = await tablesDB.createRow({\n databaseId: database.$id,\n tableId: table.$id,\n rowId: ID.unique(),\n data: { title: 'Buy groceries', completed: false, dueDate: '2025-12-31T23:59:59.000Z' },\n permissions: [\n Permission.read(Role.user('')),\n Permission.update(Role.user('')),\n Permission.delete(Role.user('')),\n ]\n});\n```\n\n> **Tip:** Use `ID.unique()` to auto-generate IDs for databases, tables, and rows. The `dueDate` value must be an ISO 8601 string — JavaScript's `new Date().toISOString()` works perfectly.", "correct": true, "score": 0.9, "judgeReasoning": "The answer is comprehensive and covers all 5 required rubric items: 1) Client + TablesDB setup is present, 2) tablesDB.create() for database creation is shown, 3) tablesDB.createTable() for the tasks table is included, 4) All three column types (varchar title, boolean completed, datetime dueDate) are created, 5) tablesDB.createRow() with data is demonstrated. The code is well-structured with clear comments and includes bonus information about permissions and server-side vs client-side usage. The TablesDB API shown (tablesDB.create, tablesDB.createTable, tablesDB.createRow) matches the reference answer's expected API. Minor deduction because TablesDB is not an actual Appwrite SDK service (Appwrite uses Databases service), so this appears to be a hallucinated API, but since the question specifically asks about 'TablesDB API' and the reference answer also uses it, the model correctly follows the question's premise. The implementation is thorough and complete.", "promptTokens": 6960, "completionTokens": 2039, "totalTokens": 8999, "cost": 0.001545, "durationMs": 119005, "tokensPerSecond": 17.13, "toolCallCount": 1 }, { "questionId": "db-4", "category": "tablesdb", "type": "mcq", "question": "In TablesDB, you need rows whose status is exactly either \"open\" or \"blocked\". Which query is the correct Appwrite Query syntax?", "choices": [ "Query.equal('status', ['open', 'blocked'])", "Query.equal(['open', 'blocked'], 'status')", "Query.where('status').in(['open', 'blocked'])", "Query.filter('status', 'in', ['open', 'blocked'])" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7526, "completionTokens": 313, "totalTokens": 7839, "cost": 0.001141, "durationMs": 16050, "tokensPerSecond": 19.5, "toolCallCount": 1 }, { "questionId": "db-5", "category": "tablesdb", "type": "mcq", "question": "Why would you add an index to a TablesDB column that is frequently filtered or sorted?", "choices": [ "To make the column immutable after the first row is inserted", "To let queries on that column run efficiently at scale", "To automatically create relationship columns between tables", "To store the column outside the row size limit" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 896, "completionTokens": 117, "totalTokens": 1013, "cost": 0.000158, "durationMs": 8194, "tokensPerSecond": 14.28, "toolCallCount": 0 }, { "questionId": "db-6", "category": "tablesdb", "type": "free-form", "question": "Explain how to query rows in Appwrite TablesDB using the Query class. Show examples of filtering, sorting, limiting, and pagination.", "correctAnswer": "The Query class provides methods like equal(), greaterThan(), lessThan(), orderAsc(), orderDesc(), limit(), offset(), cursorAfter() that are passed as an array to tablesDB.listRows().", "rubric": "Must include: 1) Query.equal() for filtering, 2) Query.orderDesc() or orderAsc() for sorting, 3) Query.limit() and Query.offset() for pagination, 4) Passing queries array to listRows(), 5) At least one other query method", "modelAnswer": "The Appwrite TypeScript SDK provides a `Query` class with a rich set of methods for filtering, sorting, and paginating rows in **TablesDB**. Here's a complete guide.\n\n---\n\n## Basic Setup\n\n```typescript\nimport { Client, TablesDB, Query } from 'appwrite'; // Web / React Native\n// or\nimport { Client, TablesDB, Query } from 'node-appwrite'; // Node.js / Deno\n\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1')\n .setProject('[PROJECT_ID]')\n .setKey('[API_KEY]'); // server-side only\n\nconst tablesDB = new TablesDB(client);\n```\n\n---\n\n## Filtering\n\nAll filter methods can be combined in a single `queries` array. Multiple filters are combined with **AND** by default.\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n // --- Equality / Inequality ---\n Query.equal('status', 'active'), // field == 'active'\n Query.equal('category', ['news', 'blog']), // field IN ['news', 'blog']\n Query.notEqual('status', 'archived'), // field != 'archived'\n\n // --- Comparison ---\n Query.lessThan('views', 1000), // views < 1000\n Query.lessThanEqual('score', 85), // score <= 85\n Query.greaterThan('price', 9.99), // price > 9.99\n Query.greaterThanEqual('rating', 4), // rating >= 4\n Query.between('age', 18, 65), // 18 <= age <= 65\n\n // --- Null checks ---\n Query.isNull('deletedAt'), // field IS NULL\n Query.isNotNull('email'), // field IS NOT NULL\n\n // --- String matching ---\n Query.startsWith('title', 'How to'), // title LIKE 'How to%'\n Query.endsWith('email', '@example.com'), // email LIKE '%@example.com'\n Query.contains('tags', 'javascript'), // tags array contains 'javascript'\n Query.search('description', 'appwrite query'), // full-text search (requires index)\n ]\n});\n```\n\n### OR Conditions\n\nUse `Query.or()` to group conditions with OR logic:\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.or([\n Query.equal('role', 'admin'),\n Query.equal('role', 'moderator'),\n ]),\n Query.equal('active', true),\n ]\n});\n// Returns rows where (role == 'admin' OR role == 'moderator') AND active == true\n```\n\n### Explicit AND\n\nQueries are AND by default, but you can be explicit:\n\n```typescript\nQuery.and([\n Query.greaterThan('age', 18),\n Query.lessThan('age', 65),\n])\n```\n\n---\n\n## Sorting\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.orderAsc('title'), // A → Z, 0 → 9\n Query.orderDesc('createdAt'), // newest first\n ]\n});\n```\n\nYou can chain multiple sort fields. The order of `orderAsc`/`orderDesc` calls determines priority:\n\n```typescript\nqueries: [\n Query.orderDesc('priority'), // sort by priority descending first\n Query.orderAsc('title'), // then alphabetically by title\n]\n```\n\n---\n\n## Limiting & Pagination\n\n### Basic Limit & Offset\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.limit(25), // max rows returned (default 25, max 100)\n Query.offset(0), // skip N rows (not recommended for large datasets)\n ]\n});\n```\n\n> **⚠️ Offset performance:** `offset` becomes slow on large datasets because the database still scans skipped rows. Use **cursor-based pagination** for production.\n\n### Cursor-Based Pagination (Recommended)\n\nUse `cursorAfter` and `cursorBefore` with the `$id` of a known row:\n\n```typescript\n// First page — get the first 20 rows\nconst page1 = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.limit(20),\n Query.orderAsc('$createdAt'),\n ]\n});\n\n// Next page — use the last row's $id as cursor\nconst lastRowId = page1.documents[page1.documents.length - 1].$id;\n\nconst page2 = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.limit(20),\n Query.cursorAfter(lastRowId), // rows after this ID\n Query.orderAsc('$createdAt'),\n ]\n});\n\n// Previous page\nconst page0 = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.limit(20),\n Query.cursorBefore(page1.documents[0].$id), // rows before this ID\n Query.orderAsc('$createdAt'),\n ]\n});\n```\n\n> **Important:** `cursorAfter`/`cursorBefore` require a consistent sort order. Always pair them with the same `orderAsc`/`orderDesc` you used to fetch the cursor row.\n\n---\n\n## Selecting Specific Fields\n\nReduce payload size by returning only the fields you need:\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.select(['title', 'content', 'status']),\n Query.equal('status', 'published'),\n ]\n});\n// Each document will only contain 'title', 'content', 'status', plus system fields ($id, $createdAt, etc.)\n```\n\n---\n\n## Complete Example: Paginated Blog Posts\n\n```typescript\ninterface Post {\n title: string;\n content: string;\n status: 'draft' | 'published';\n views: number;\n tags: string[];\n createdAt: string;\n}\n\nasync function getPublishedPosts(pageCursor?: string, pageSize = 20) {\n const queries: string[] = [\n Query.equal('status', 'published'),\n Query.orderDesc('createdAt'),\n Query.limit(pageSize),\n Query.select(['title', 'views', 'tags', 'createdAt']),\n ];\n\n if (pageCursor) {\n queries.push(Query.cursorAfter(pageCursor));\n }\n\n const result = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries,\n });\n\n return {\n posts: result.documents as unknown as Post[],\n total: result.total,\n nextCursor: result.documents.length === pageSize\n ? result.documents[result.documents.length - 1].$id\n : null,\n };\n}\n\n// Usage\nconst { posts, total, nextCursor } = await getPublishedPosts();\nconsole.log(`Showing ${posts.length} of ${total} posts`);\n// Pass nextCursor to getPublishedPosts(nextCursor) for the next page\n```\n\n---\n\n## Query Reference Summary\n\n| Method | Purpose |\n|--------|---------|\n| `Query.equal(field, value)` | Exact match (or `IN` if value is array) |\n| `Query.notEqual(field, value)` | Not equal |\n| `Query.lessThan(field, value)` | `<` |\n| `Query.lessThanEqual(field, value)` | `<=` |\n| `Query.greaterThan(field, value)` | `>` |\n| `Query.greaterThanEqual(field, value)` | `>=` |\n| `Query.between(field, a, b)` | `a <= field <= b` |\n| `Query.isNull(field)` | `IS NULL` |\n| `Query.isNotNull(field)` | `IS NOT NULL` |\n| `Query.startsWith(field, prefix)` | String prefix match |\n| `Query.endsWith(field, suffix)` | String suffix match |\n| `Query.contains(field, value)` | Array/string contains |\n| `Query.search(field, keywords)` | Full-text search (requires index) |\n| `Query.orderAsc(field)` | Sort ascending |\n| `Query.orderDesc(field)` | Sort descending |\n| `Query.limit(n)` | Max rows (1–100) |\n| `Query.offset(n)` | Skip rows (avoid for large data) |\n| `Query.cursorAfter(id)` | Paginate forward |\n| `Query.cursorBefore(id)` | Paginate backward |\n| `Query.select([fields])` | Return only specified fields |\n| `Query.or([conditions])` | OR grouping |\n| `Query.and([conditions])` | Explicit AND grouping |", "correct": true, "score": 1, "judgeReasoning": "The model's answer fully satisfies all rubric requirements: 1) Query.equal() for filtering is demonstrated with multiple examples, 2) Query.orderDesc() and Query.orderAsc() for sorting are both shown, 3) Query.limit() and Query.offset() for pagination are covered, 4) All queries are passed as arrays to tablesDB.listRows(), 5) Multiple additional query methods are shown including greaterThan(), lessThan(), cursorAfter(), cursorBefore(), select(), between(), isNull(), search(), and more. The answer goes well beyond the minimum requirements with comprehensive examples, a complete reference table, cursor-based pagination guidance, and a real-world complete example.", "promptTokens": 6914, "completionTokens": 2202, "totalTokens": 9116, "cost": 0.001585, "durationMs": 117624, "tokensPerSecond": 18.72, "toolCallCount": 1 }, { "questionId": "db-7", "category": "tablesdb", "type": "mcq", "question": "What types of relationships does Appwrite support between tables?", "choices": [ "One-to-one, one-to-many, many-to-one, and many-to-many", "Only one-to-one", "Only foreign key references", "Relationships are not supported" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 14089, "completionTokens": 474, "totalTokens": 14563, "cost": 0.002105, "durationMs": 13357, "tokensPerSecond": 35.49, "toolCallCount": 2 }, { "questionId": "db-8", "category": "tablesdb", "type": "mcq", "question": "How do permissions work on rows in Appwrite?", "choices": [ "Rows inherit permissions from the project only", "Permissions are set globally for all rows", "Each row can have its own permission list, or inherit from the table", "Permissions are only managed through API keys" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7436, "completionTokens": 331, "totalTokens": 7767, "cost": 0.001134, "durationMs": 8987, "tokensPerSecond": 36.83, "toolCallCount": 1 }, { "questionId": "db-9", "category": "tablesdb", "type": "mcq", "question": "What is the correct method to update a row with the current TablesDB API?", "choices": [ "tablesDB.update()", "tablesDB.patchRow()", "tablesDB.updateRow({ databaseId, tableId, rowId, data })", "rows.update()" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7406, "completionTokens": 256, "totalTokens": 7662, "cost": 0.001109, "durationMs": 6985, "tokensPerSecond": 36.65, "toolCallCount": 1 }, { "questionId": "db-10", "category": "tablesdb", "type": "mcq", "question": "Which Query method performs a full-text search on a row column?", "choices": [ "Query.search()", "Query.contains()", "Query.fullText()", "Query.like()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7340, "completionTokens": 139, "totalTokens": 7479, "cost": 0.001067, "durationMs": 6557, "tokensPerSecond": 21.2, "toolCallCount": 1 }, { "questionId": "db-11", "category": "tablesdb", "type": "mcq", "question": "How do transactions work in Appwrite TablesDB?", "choices": [ "Transactions are not supported", "Wrap operations in a single tablesDB.transaction() call", "Create a transaction, pass the transactionId to row create/update/delete operations, then commit the transaction", "Use a special transaction table" ], "correctAnswer": "C", "modelAnswer": "A", "correct": false, "score": 0, "promptTokens": 14131, "completionTokens": 312, "totalTokens": 14443, "cost": 0.002066, "durationMs": 18719, "tokensPerSecond": 16.67, "toolCallCount": 2 }, { "questionId": "db-12", "category": "tablesdb", "type": "mcq", "question": "What are the possible statuses of a TablesDB transaction?", "choices": [ "pending, committing, committed, failed", "pending, committing, committed, rolled_back, failed", "open, closed", "started, finished" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14063, "completionTokens": 692, "totalTokens": 14755, "cost": 0.002163, "durationMs": 48977, "tokensPerSecond": 14.13, "toolCallCount": 2 }, { "questionId": "db-13", "category": "tablesdb", "type": "mcq", "question": "Which Query method is used for cursor-based pagination (get next page after a row)?", "choices": [ "Query.cursorAfter(rowId) or Query.cursorBefore(rowId)", "Query.offset()", "Query.nextPage()", "Query.cursor()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7400, "completionTokens": 223, "totalTokens": 7623, "cost": 0.001098, "durationMs": 12662, "tokensPerSecond": 17.61, "toolCallCount": 1 }, { "questionId": "db-14", "category": "tablesdb", "type": "mcq", "question": "How do you delete a row with the current TablesDB API?", "choices": [ "tablesDB.delete()", "rows.delete()", "tablesDB.removeRow()", "tablesDB.deleteRow({ databaseId, tableId, rowId })" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 7388, "completionTokens": 203, "totalTokens": 7591, "cost": 0.001091, "durationMs": 9084, "tokensPerSecond": 22.35, "toolCallCount": 1 }, { "questionId": "db-15", "category": "tablesdb", "type": "mcq", "question": "When creating a row, can you set permissions on that row?", "choices": [ "Yes, pass a permissions array to createRow", "No, rows always inherit table permissions", "Only via the Console", "Only for the row creator" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7390, "completionTokens": 241, "totalTokens": 7631, "cost": 0.001102, "durationMs": 8688, "tokensPerSecond": 27.74, "toolCallCount": 1 }, { "questionId": "db-16", "category": "tablesdb", "type": "mcq", "question": "What is a relationship column in Appwrite?", "choices": [ "A link to an external API", "A special index type", "A column that references rows in the same or another table (one-to-one, one-to-many, etc.)", "A computed field" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20665, "completionTokens": 596, "totalTokens": 21261, "cost": 0.00306, "durationMs": 25407, "tokensPerSecond": 23.46, "toolCallCount": 2 }, { "questionId": "db-17", "category": "tablesdb", "type": "mcq", "question": "You want the first page of a TablesDB query to return no more than 20 rows. Which query should be included in the queries array?", "choices": [ "Query.page(20)", "Query.max(20)", "Query.limit(20)", "Query.count(20)" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7392, "completionTokens": 256, "totalTokens": 7648, "cost": 0.001107, "durationMs": 8648, "tokensPerSecond": 29.6, "toolCallCount": 1 }, { "questionId": "db-18", "category": "tablesdb", "type": "mcq", "question": "Which query sorts TablesDB rows by a `score` column from highest to lowest?", "choices": [ "Query.orderDesc('score')", "Query.sort('score', 'desc')", "Query.descending('score')", "Query.orderBy('score', Query.DESC)" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7414, "completionTokens": 244, "totalTokens": 7658, "cost": 0.001106, "durationMs": 8588, "tokensPerSecond": 28.41, "toolCallCount": 1 }, { "questionId": "db-19", "category": "tablesdb", "type": "mcq", "question": "What happens if a transaction expires before it is committed?", "choices": [ "Operations are automatically rolled back and the transaction is deleted", "The transaction is committed automatically", "Staged operations fail with a transaction expired error; the transaction has a TTL", "Nothing; transactions do not expire" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20680, "completionTokens": 800, "totalTokens": 21480, "cost": 0.003119, "durationMs": 47862, "tokensPerSecond": 16.71, "toolCallCount": 2 }, { "questionId": "db-20", "category": "tablesdb", "type": "free-form", "question": "Explain how to use Query to filter rows where a numeric field is greater than 100 and return at most 20 results sorted by that field descending.", "correctAnswer": "Use tablesDB.listRows() with queries array: Query.greaterThan('columnId', 100), Query.orderDesc('columnId'), Query.limit(20). Pass these to the listRows queries parameter.", "rubric": "Must include: 1) Query.greaterThan for the filter, 2) Query.orderDesc for sort, 3) Query.limit(20), 4) Passing queries to listRows()", "modelAnswer": "To filter rows where a numeric field is greater than 100, return at most 20 results, and sort by that field descending, combine `Query.greaterThan`, `Query.limit`, and `Query.orderDesc` in your `listRows` call:\n\n```typescript\nconst results = await tablesDB.listRows({\n databaseId: '[DATABASE_ID]',\n tableId: '[TABLE_ID]',\n queries: [\n Query.greaterThan('fieldName', 100), // field > 100\n Query.limit(20), // at most 20 results\n Query.orderDesc('fieldName') // sorted descending by that field\n ]\n});\n```\n\n**Key points:**\n\n- **`Query.greaterThan('fieldName', 100)`** — filters for rows where the numeric field is strictly greater than 100. Use `Query.greaterThanEqual` if you want `>= 100`.\n- **`Query.limit(20)`** — caps the result set at 20 rows (default is 25, max is 100).\n- **`Query.orderDesc('fieldName')`** — sorts results in descending order by the same field. Use `Query.orderAsc` for ascending.\n- The queries are combined with **AND logic** by default (all conditions must match).\n- The field must be indexed in your table's schema for sorting to work efficiently.", "correct": true, "score": 1, "judgeReasoning": "The model's answer includes all four required elements: 1) Query.greaterThan for filtering, 2) Query.orderDesc for sorting, 3) Query.limit(20) for capping results, and 4) passing all queries to listRows(). The answer also provides a complete TypeScript code example with proper syntax, along with helpful additional context about each query method. The answer fully satisfies the rubric.", "promptTokens": 6918, "completionTokens": 535, "totalTokens": 7453, "cost": 0.001118, "durationMs": 14450, "tokensPerSecond": 37.02, "toolCallCount": 1 }, { "questionId": "db-21", "category": "tablesdb", "type": "mcq", "question": "What are the minimum and maximum values for an integer column in Appwrite?", "choices": [ "-2147483648 to 2147483647 (32-bit signed integer)", "-9223372036854775808 to 9223372036854775807 (64-bit signed integer)", "0 to 18446744073709551615 (64-bit unsigned integer)", "-999999999999 to 999999999999 (12-digit limit)" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14255, "completionTokens": 802, "totalTokens": 15057, "cost": 0.00222, "durationMs": 23631, "tokensPerSecond": 33.94, "toolCallCount": 2 }, { "questionId": "db-22", "category": "tablesdb", "type": "mcq", "question": "Which of the following is NOT a valid string-based column type in Appwrite TablesDB?", "choices": [ "varchar", "mediumtext", "tinytext", "longtext" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14011, "completionTokens": 484, "totalTokens": 14495, "cost": 0.002097, "durationMs": 16139, "tokensPerSecond": 29.99, "toolCallCount": 2 }, { "questionId": "db-23", "category": "tablesdb", "type": "mcq", "question": "Which of the following is NOT a valid string-related column type in Appwrite TablesDB?", "choices": [ "email", "url", "enum", "uuid" ], "correctAnswer": "D", "modelAnswer": "D", "correct": true, "score": 1, "promptTokens": 20503, "completionTokens": 1807, "totalTokens": 22310, "cost": 0.003376, "durationMs": 58882, "tokensPerSecond": 30.69, "toolCallCount": 2 }, { "questionId": "func-1", "category": "functions", "type": "mcq", "question": "Which runtimes does Appwrite Functions support?", "choices": [ "Node.js only", "Node.js, Python, PHP, Ruby, Dart, Deno, Swift, Kotlin, Java, Bun, and more", "Node.js and Python only", "Any Docker container" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14109, "completionTokens": 544, "totalTokens": 14653, "cost": 0.002128, "durationMs": 15954, "tokensPerSecond": 34.1, "toolCallCount": 2 }, { "questionId": "func-2", "category": "functions", "type": "mcq", "question": "How are environment variables provided to Appwrite Functions?", "choices": [ "Through a .env file in the function directory", "Set in the Appwrite Console or API, accessible at runtime", "Hardcoded in the function code", "Through command-line arguments" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14095, "completionTokens": 793, "totalTokens": 14888, "cost": 0.002195, "durationMs": 34775, "tokensPerSecond": 22.8, "toolCallCount": 2 }, { "questionId": "func-3", "category": "functions", "type": "free-form", "question": "Write an Appwrite Function in Node.js that receives a JSON body with a \"name\" field and returns a personalized greeting. Use the correct function signature and response format.", "correctAnswer": "Appwrite Functions use export default async ({ req, res, log, error }) => {} signature. Request body is in req.body. Response uses res.json(), res.text(), res.empty(), or res.redirect().", "rubric": "Must include: 1) Default export of async function, 2) Destructured context with req, res, log or error, 3) Parsing req.body, 4) Using res.json() for response, 5) Correct function signature pattern", "modelAnswer": "Here's an Appwrite Function in Node.js that receives a JSON body with a `\"name\"` field and returns a personalized greeting:\n\n```javascript\n// src/main.js\nexport default async ({ req, res, log, error }) => {\n // Log the incoming request method and path\n log(`Received ${req.method} request on ${req.path}`);\n\n // Only accept POST requests\n if (req.method !== 'POST') {\n return res.json({ error: 'Method not allowed. Use POST.' }, 405);\n }\n\n // Parse the JSON body\n const data = req.bodyJson;\n\n // Validate that the \"name\" field is present\n if (!data || !data.name) {\n error('Missing required field: name');\n return res.json({ error: 'A \"name\" field is required in the JSON body.' }, 400);\n }\n\n // Return the personalized greeting\n return res.json({\n message: `Hello, ${data.name}! Welcome to Appwrite Functions.`\n });\n};\n```\n\n### Key points:\n\n- **Function signature**: The handler is a default async export that receives a context object with `{ req, res, log, error }`.\n- **`req.bodyJson`**: Automatically parses the incoming JSON body. Returns `undefined` if the body isn't valid JSON.\n- **`res.json()`**: Sends a JSON response with the correct `Content-Type: application/json` header. Accepts a second argument for the HTTP status code.\n- **`log()` / `error()`**: Built-in logging helpers that appear in the Appwrite Console execution logs.\n- **Validation**: Always check for required fields and return appropriate error codes (400 for bad input, 405 for wrong method).\n\n### Example usage:\n\n```bash\n# Trigger the function via CLI\nappwrite functions create-execution \\\n --function-id \\\n --body '{\"name\": \"Alice\"}'\n```\n\n**Response:**\n```json\n{\n \"message\": \"Hello, Alice! Welcome to Appwrite Functions.\"\n}\n```", "correct": true, "score": 0.95, "judgeReasoning": "The answer is nearly fully correct and includes all required elements: 1) Default export of async function ✓, 2) Destructured context with req, res, log, and error ✓, 3) Parsing req.body (uses req.bodyJson which is the correct Appwrite property for auto-parsed JSON) ✓, 4) Using res.json() for response ✓, 5) Correct function signature pattern ✓. The explanation is thorough and accurate. Minor deduction: uses req.bodyJson instead of req.body, which while valid in Appwrite (it's an alternative accessor), the reference answer specifically mentions req.body. However, req.bodyJson is actually the more precise way to access parsed JSON in Appwrite Functions, so this is arguably more correct. Overall an excellent, complete answer.", "promptTokens": 13609, "completionTokens": 1016, "totalTokens": 14625, "cost": 0.00219, "durationMs": 63601, "tokensPerSecond": 15.97, "toolCallCount": 2 }, { "questionId": "func-4", "category": "functions", "type": "mcq", "question": "What are the ways to trigger an Appwrite Function execution?", "choices": [ "HTTP requests only", "HTTP requests, event triggers, scheduled (CRON), and manual execution via API/Console", "Only through the Appwrite Console", "Only through webhooks" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14095, "completionTokens": 448, "totalTokens": 14543, "cost": 0.002099, "durationMs": 16829, "tokensPerSecond": 26.62, "toolCallCount": 2 }, { "questionId": "func-5", "category": "functions", "type": "mcq", "question": "What is the purpose of the \"build command\" in an Appwrite Function configuration?", "choices": [ "To compile the function to machine code", "To run commands during deployment to install dependencies and prepare the function", "To build the Appwrite server", "To create a Docker image" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 8463, "completionTokens": 298, "totalTokens": 8761, "cost": 0.001268, "durationMs": 11560, "tokensPerSecond": 25.78, "toolCallCount": 1 }, { "questionId": "func-6", "category": "functions", "type": "mcq", "question": "How can an Appwrite Function access other Appwrite services like TablesDB?", "choices": [ "Functions cannot access other Appwrite services", "By importing the Node.js SDK and using the function's API key or the APPWRITE_FUNCTION_API_ENDPOINT and APPWRITE_FUNCTION_PROJECT_ID environment variables", "Through REST API calls only with no SDK support", "By directly accessing the database filesystem" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14213, "completionTokens": 537, "totalTokens": 14750, "cost": 0.00214, "durationMs": 11887, "tokensPerSecond": 45.18, "toolCallCount": 2 }, { "questionId": "func-7", "category": "functions", "type": "free-form", "question": "Explain how to deploy an Appwrite Function using the Appwrite CLI. What are the steps and key configuration options?", "correctAnswer": "Appwrite CLI enables function deployment through init (appwrite init functions) and push (appwrite push functions) commands. Configuration is stored in appwrite.config.json including runtime, entrypoint, build commands, execute permissions, variables, and schedule.", "rubric": "Must mention: 1) CLI installation, 2) appwrite init functions, 3) Configuration in appwrite.config.json, 4) appwrite push functions, 5) Key options like runtime, entrypoint, build commands, permissions, variables, or schedule", "modelAnswer": "# Deploying an Appwrite Function with the CLI\n\nHere’s a complete walkthrough of the steps and key configuration options.\n\n---\n\n## 1. Prerequisites\n\nFirst, install the CLI and authenticate:\n\n```bash\n# Install\nnpm install -g appwrite-cli\n\n# Login to Appwrite Cloud or a self-hosted instance\nappwrite login\n\n# For self-hosted:\nappwrite login --endpoint \"https://your-instance.com/v1\"\n\n# Initialize a project (creates appwrite.config.json)\nappwrite init project\n```\n\n---\n\n## 2. Create a New Function\n\n```bash\nappwrite init functions\n```\n\nThis scaffolds a function directory (e.g., `functions/myFunction/`) with a starter file and a `.gitignore`. You can also **pull** existing functions from the Console:\n\n```bash\nappwrite pull functions\n```\n\n---\n\n## 3. Configure the Function\n\nFunctions are defined in `appwrite.config.json` (or in a separate JSON array file referenced via `includes`). Here’s a complete example:\n\n```json\n{\n \"functions\": [\n {\n \"$id\": \"myFunction\",\n \"name\": \"My Awesome Function\",\n \"enabled\": true,\n \"logging\": true,\n \"runtime\": \"node-22\",\n \"buildSpecification\": \"s-1vcpu-512mb\",\n \"runtimeSpecification\": \"s-1vcpu-512mb\",\n \"deploymentRetention\": 7,\n \"scopes\": [],\n \"events\": [],\n \"schedule\": \"\",\n \"timeout\": 15,\n \"entrypoint\": \"src/main.js\",\n \"commands\": \"npm install\",\n \"ignore\": \"node_modules\\n.tmp\",\n \"path\": \"functions/myFunction\"\n }\n ]\n}\n```\n\n### Key Configuration Fields\n\n| Field | Description |\n|-------|-------------|\n| `$id` | Unique function ID (used in CLI commands and API calls) |\n| `enabled` | Enable/disable the function |\n| `logging` | Store execution logs for debugging |\n| `runtime` | Execution runtime (e.g., `node-22`, `python-3.11`, `php-8.3`, `ruby-3.3`, `deno-2.0`) |\n| `buildSpecification` | Compute spec used during **build** (e.g., `s-1vcpu-512mb`) |\n| `runtimeSpecification` | Compute spec used during **execution** |\n| `deploymentRetention` | Days to keep old deployments before auto-deletion |\n| `scopes` | API scopes granted to the function’s execution key |\n| `events` | Event patterns that trigger the function (e.g., `[\"users.*.create\"]`) |\n| `schedule` | Cron expression for scheduled execution (empty = no schedule) |\n| `timeout` | Max execution duration in seconds (default 15) |\n| `entrypoint` | File inside `path` that starts the function |\n| `commands` | Build/install command run before deployment (e.g., `npm install`) |\n| `ignore` | Extra newline-separated ignore rules (`.gitignore` is read automatically) |\n| `path` | Local directory containing your function source code |\n\n> **Tip:** You can split function definitions into a separate file using `includes` in `appwrite.config.json`:\n> ```json\n> {\n> \"includes\": {\n> \"functions\": \"appwrite/functions.json\"\n> }\n> }\n> ```\n> When using `includes`, the `path` values are resolved **relative to the include file’s directory**.\n\n---\n\n## 4. Deploy the Function\n\n```bash\n# Deploy all functions defined in the config\nappwrite push functions\n\n# Deploy a specific function by ID\nappwrite push functions --function-id myFunction\n\n# Deploy and immediately activate the deployment (set as live)\nappwrite push functions --function-id myFunction --activate\n\n# Deploy without switching live traffic\nappwrite push functions --function-id myFunction --activate=false\n```\n\nThe CLI will:\n1. Read the function configuration from `appwrite.config.json`\n2. Package the code from the `path` directory (respecting `.gitignore` and `ignore` rules)\n3. Run the `commands` (e.g., `npm install`) on the build server\n4. Upload the deployment\n5. Optionally activate it\n\n---\n\n## 5. Managing Function Variables\n\n**Do not** define variables in `appwrite.config.json`. Instead, place a `.env` file inside your function’s `path`:\n\n```\n# functions/myFunction/.env\nDATABASE_URL=mongodb://...\nSECRET_KEY=my-secret\n```\n\nThen sync them with:\n\n```bash\n# Push code AND sync variables from .env\nappwrite push functions --function-id myFunction --with-variables\n\n# Push code only (leave remote variables unchanged)\nappwrite push functions --function-id myFunction\n```\n\nYou can also manage variables individually:\n\n```bash\nappwrite functions create-variable --function-id myFunction --key MY_VAR --value \"hello\"\nappwrite functions list-variables --function-id myFunction\nappwrite functions delete-variable --function-id myFunction --variable-id \n```\n\n---\n\n## 6. Activating a Specific Deployment\n\nIf you have multiple deployments, you can manually set which one is live:\n\n```bash\n# List deployments\nappwrite functions list-deployments --function-id myFunction\n\n# Set a specific deployment as active\nappwrite functions update-deployment --function-id myFunction --deployment-id \n```\n\n---\n\n## 7. Local Development & Testing\n\n```bash\n# Run the function locally (uses the configured runtime)\nappwrite run functions\n\n# Run locally with variables fetched from the remote function settings\nappwrite run functions --with-variables\n```\n\n---\n\n## 8. Triggering the Function\n\n```bash\n# Trigger with a JSON body\nappwrite functions create-execution \\\n --function-id myFunction \\\n --body '{\"key\": \"value\"}'\n\n# List past executions\nappwrite functions list-executions --function-id myFunction\n\n# Get a specific execution log\nappwrite functions get-execution --function-id myFunction --execution-id \n```\n\n---\n\n## 9. CI/CD / Non-Interactive Mode\n\nFor automated pipelines, use `--force` to skip confirmations:\n\n```bash\nappwrite push functions --all --force\nappwrite push functions --function-id myFunction --force\n```\n\nYou can also set the endpoint and project ID via environment variables or the `appwrite client` command for headless use:\n\n```bash\nappwrite client --endpoint \"https://.cloud.appwrite.io/v1\"\nappwrite client --project-id \"\"\n```\n\n---\n\n## Summary of the Typical Workflow\n\n```\n1. appwrite login\n2. appwrite init project\n3. appwrite init functions # scaffold function directory\n4. Edit appwrite.config.json # configure runtime, entrypoint, etc.\n5. Write your function code # in the path directory\n6. Add a .env file # for variables (optional)\n7. appwrite push functions --activate # deploy and go live\n8. appwrite functions create-execution # test it\n```\n\nThat’s it! The CLI handles packaging, building, uploading, and activating your function with a single command.", "correct": true, "score": 1, "judgeReasoning": "The model's answer comprehensively covers all five required rubric points: 1) CLI installation (npm install -g appwrite-cli), 2) appwrite init functions command, 3) Configuration in appwrite.config.json with detailed explanation, 4) appwrite push functions command with various options, and 5) Key options including runtime, entrypoint, build commands (commands field), permissions/scopes, variables, and schedule. The answer goes well beyond the minimum requirements with additional details about local development, CI/CD, triggering functions, and managing deployments, all of which are accurate and relevant.", "promptTokens": 7949, "completionTokens": 1782, "totalTokens": 9731, "cost": 0.001612, "durationMs": 77118, "tokensPerSecond": 23.11, "toolCallCount": 1 }, { "questionId": "func-8", "category": "functions", "type": "mcq", "question": "What context parameters does an Appwrite Function receive?", "choices": [ "request and response only", "req, res, log, and error", "event, context, and callback", "input, output, and logger" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14063, "completionTokens": 479, "totalTokens": 14542, "cost": 0.002103, "durationMs": 11672, "tokensPerSecond": 41.04, "toolCallCount": 2 }, { "questionId": "func-9", "category": "functions", "type": "mcq", "question": "How do you schedule an Appwrite Function to run periodically?", "choices": [ "Using JavaScript setTimeout()", "Using a CRON expression in the function configuration", "Through an external scheduler service", "Scheduled execution is not supported" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14061, "completionTokens": 369, "totalTokens": 14430, "cost": 0.002072, "durationMs": 19370, "tokensPerSecond": 19.05, "toolCallCount": 2 }, { "questionId": "func-10", "category": "functions", "type": "mcq", "question": "What response methods are available in Appwrite Functions?", "choices": [ "res.send() only", "res.json(), res.text(), res.empty(), res.redirect()", "res.write() and res.end()", "return JSON.stringify(data)" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 20620, "completionTokens": 1448, "totalTokens": 22068, "cost": 0.003292, "durationMs": 64381, "tokensPerSecond": 22.49, "toolCallCount": 2 }, { "questionId": "func-11", "category": "functions", "type": "mcq", "question": "What are the possible statuses of an Appwrite Function execution?", "choices": [ "pending, running, done, error", "waiting, processing, completed, failed, and scheduled", "queued, active, success, failure", "created, started, finished, crashed" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14101, "completionTokens": 524, "totalTokens": 14625, "cost": 0.002121, "durationMs": 14627, "tokensPerSecond": 35.82, "toolCallCount": 2 }, { "questionId": "func-12", "category": "functions", "type": "mcq", "question": "When you call the SDK's createExecution with async: true, what happens?", "choices": [ "The request fails because async is not supported", "The API returns immediately with execution status 'waiting'; the function runs in the background and you can poll Get Execution for updates", "The client must provide a webhook URL to receive the result", "The execution is queued but never runs" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 7508, "completionTokens": 327, "totalTokens": 7835, "cost": 0.001143, "durationMs": 17917, "tokensPerSecond": 18.25, "toolCallCount": 1 }, { "questionId": "func-13", "category": "functions", "type": "mcq", "question": "Can an Appwrite Function be triggered by an event from another function's execution (e.g. functions.*.executions.*.create)?", "choices": [ "Yes, function execution events are supported like any other event", "No; triggering a function on a function event (events starting with 'functions.') is not allowed", "Only in the Console, not via event configuration", "Yes, but only for completed executions" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14191, "completionTokens": 749, "totalTokens": 14940, "cost": 0.002196, "durationMs": 77323, "tokensPerSecond": 9.69, "toolCallCount": 2 }, { "questionId": "func-14", "category": "functions", "type": "mcq", "question": "When can you successfully execute an Appwrite Function via the API?", "choices": [ "As soon as the function resource is created", "Only after the function has an active deployment whose build status is 'ready'", "After the first deployment is created, regardless of build status", "Only when the deployment status is 'building'" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14143, "completionTokens": 939, "totalTokens": 15082, "cost": 0.002243, "durationMs": 37241, "tokensPerSecond": 25.21, "toolCallCount": 2 }, { "questionId": "func-15", "category": "functions", "type": "mcq", "question": "Besides functionId and data, what parameters does the createExecution API support?", "choices": [ "None", "async only", "async, path, method, headers, and scheduledAt (ISO 8601)", "path and method only" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 20608, "completionTokens": 1384, "totalTokens": 21992, "cost": 0.003273, "durationMs": 56675, "tokensPerSecond": 24.42, "toolCallCount": 2 }, { "questionId": "func-16", "category": "functions", "type": "mcq", "question": "When passing custom headers to createExecution, what restriction applies?", "choices": [ "Headers are not allowed; only the request body is passed", "Header names cannot start with 'x-appwrite-'; they are reserved by Appwrite", "Only Content-Type is allowed", "There are no restrictions on header names" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 20707, "completionTokens": 1041, "totalTokens": 21748, "cost": 0.00319, "durationMs": 46611, "tokensPerSecond": 22.33, "toolCallCount": 2 }, { "questionId": "func-17", "category": "functions", "type": "mcq", "question": "Which environment variable is set at runtime to indicate how the function was triggered (e.g. http, event, schedule)?", "choices": [ "APPWRITE_TRIGGER_TYPE", "APPWRITE_FUNCTION_TRIGGER", "APPWRITE_EXECUTION_SOURCE", "APPWRITE_FUNCTION_EVENT" ], "correctAnswer": "B", "modelAnswer": "B", "correct": true, "score": 1, "promptTokens": 14115, "completionTokens": 705, "totalTokens": 14820, "cost": 0.002174, "durationMs": 43103, "tokensPerSecond": 16.36, "toolCallCount": 2 }, { "questionId": "func-18", "category": "functions", "type": "mcq", "question": "What are the deployment build statuses for an Appwrite Function?", "choices": [ "pending, building, ready", "processing, building, ready, and canceled", "queued, in_progress, success, failed", "uploaded, compiling, deployed" ], "correctAnswer": "B", "modelAnswer": "A", "correct": false, "score": 0, "promptTokens": 14085, "completionTokens": 7683, "totalTokens": 21768, "cost": 0.004123, "durationMs": 542673, "tokensPerSecond": 14.16, "toolCallCount": 2 }, { "questionId": "func-19", "category": "functions", "type": "mcq", "question": "Which API key or auth type can be used to trigger a function execution (createExecution)?", "choices": [ "Only Server API key", "Only Session (logged-in user)", "Server API key, Session, scoped API key (Key), or JWT", "Only JWT" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 14099, "completionTokens": 1126, "totalTokens": 15225, "cost": 0.002289, "durationMs": 30626, "tokensPerSecond": 36.77, "toolCallCount": 2 }, { "questionId": "func-20", "category": "functions", "type": "free-form", "question": "Name at least three APPWRITE_FUNCTION_* environment variables that are available inside an Appwrite Function at runtime, and what each is used for.", "correctAnswer": "Examples: APPWRITE_FUNCTION_ID (function ID), APPWRITE_FUNCTION_NAME, APPWRITE_FUNCTION_PROJECT_ID, APPWRITE_FUNCTION_API_ENDPOINT (for SDK), APPWRITE_FUNCTION_TRIGGER (http/event/schedule), APPWRITE_FUNCTION_EVENT (event name when event-triggered), APPWRITE_FUNCTION_JWT (user JWT when present), APPWRITE_FUNCTION_USER_ID, APPWRITE_FUNCTION_DATA / APPWRITE_FUNCTION_EVENT_DATA (body), APPWRITE_FUNCTION_DEPLOYMENT, APPWRITE_FUNCTION_RUNTIME_NAME, APPWRITE_FUNCTION_RUNTIME_VERSION, APPWRITE_FUNCTION_CPUS, APPWRITE_FUNCTION_MEMORY.", "rubric": "Must name at least 3 APPWRITE_FUNCTION_* variables with correct purpose. Accept any subset of the documented runtime env vars (ID, name, project ID, API endpoint, trigger, event, JWT, user ID, data, deployment, runtime, resources).", "modelAnswer": "Here are three key `APPWRITE_FUNCTION_*` environment variables available inside an Appwrite Function at runtime:\n\n1. **`APPWRITE_FUNCTION_ID`** – The unique ID of the function being executed. Useful for identifying which function is running, especially if you reuse the same code across multiple functions or need to reference the function in logs or API calls.\n\n2. **`APPWRITE_FUNCTION_TRIGGER`** – Indicates how the function was triggered. Possible values include `\"http\"` (direct HTTP call), `\"event\"` (triggered by an Appwrite event), or `\"schedule\"` (cron-based execution). This lets you branch logic based on the invocation context.\n\n3. **`APPWRITE_FUNCTION_JWT`** – A JSON Web Token (JWT) scoped to the user who triggered the function (if any). You can use this token to make authenticated Appwrite API calls on behalf of that user from within the function, without needing a separate API key.\n\nOther commonly available variables include `APPWRITE_FUNCTION_NAME`, `APPWRITE_FUNCTION_DEPLOYMENT_ID`, `APPWRITE_FUNCTION_PROJECT_ID`, `APPWRITE_FUNCTION_RUNTIME`, `APPWRITE_FUNCTION_EVENT`, `APPWRITE_FUNCTION_DATA`, `APPWRITE_FUNCTION_USER_ID`, `APPWRITE_FUNCTION_API_KEY`, and `APPWRITE_FUNCTION_ENDPOINT`.", "correct": true, "score": 1, "judgeReasoning": "The answer correctly identifies at least three APPWRITE_FUNCTION_* environment variables with accurate descriptions: APPWRITE_FUNCTION_ID (unique function identifier), APPWRITE_FUNCTION_TRIGGER (how the function was invoked with correct possible values), and APPWRITE_FUNCTION_JWT (user JWT for authenticated API calls). All three match the documented runtime environment variables and their purposes align with the reference answer. The additional variables mentioned are also largely correct, demonstrating comprehensive knowledge of the runtime environment.", "promptTokens": 13604, "completionTokens": 1616, "totalTokens": 15220, "cost": 0.002357, "durationMs": 76134, "tokensPerSecond": 21.23, "toolCallCount": 2 }, { "questionId": "stor-1", "category": "storage", "type": "mcq", "question": "What is a \"bucket\" in Appwrite Storage?", "choices": [ "A container for organizing and managing file uploads with shared permissions and settings", "A type of TablesDB table", "A caching layer for static assets", "A backup storage location" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 20638, "completionTokens": 352, "totalTokens": 20990, "cost": 0.002988, "durationMs": 18743, "tokensPerSecond": 18.78, "toolCallCount": 2 }, { "questionId": "stor-2", "category": "storage", "type": "mcq", "question": "How do you upload a file to Appwrite Storage?", "choices": [ "storage.createFile(bucketId, fileId, file)", "storage.upload()", "storage.putObject()", "files.create()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7370, "completionTokens": 193, "totalTokens": 7563, "cost": 0.001086, "durationMs": 7007, "tokensPerSecond": 27.54, "toolCallCount": 1 }, { "questionId": "stor-3", "category": "storage", "type": "free-form", "question": "Write code using the Appwrite Web SDK to create a bucket, upload a file, and get a preview URL for an image file. Include permission configuration.", "correctAnswer": "Storage service provides createBucket() for bucket management, createFile() for uploads, and getFilePreview() for image transformations. Buckets support file size limits, allowed extensions, and permission configuration.", "rubric": "Must include: 1) Storage service instantiation, 2) Bucket creation with permissions, 3) storage.createFile() with bucket ID, file ID, and File object, 4) storage.getFilePreview() for image preview, 5) Correct imports", "modelAnswer": "Here's a complete example using the **Appwrite Web SDK** (`appwrite` package for browser/JavaScript) to create a bucket, upload an image with permissions, and generate a preview URL.\n\n```javascript\nimport { Client, Storage, ID, Permission, Role } from 'appwrite';\n\n// 1. Initialize the client\nconst client = new Client()\n .setEndpoint('https://.cloud.appwrite.io/v1') // e.g. 'https://cloud.appwrite.io/v1'\n .setProject('');\n\nconst storage = new Storage(client);\n\n// ─────────────────────────────────────────────\n// 2. Create a bucket (requires admin/API key)\n// ─────────────────────────────────────────────\n// Note: Creating a bucket is a privileged operation.\n// In the browser, you need a user with the appropriate\n// team role (e.g., owner/developer) or use an API key\n// on the server side. If you're using the Web SDK\n// client-side, ensure the logged-in user has the\n// `buckets.create` permission via team roles.\n\nasync function createBucket() {\n const bucket = await storage.createBucket({\n bucketId: ID.unique(),\n name: 'My Image Bucket',\n // Optional: set default file-level permissions\n // (applied to all files unless overridden)\n permission: 'file', // 'file' (per-file) or 'bucket' (inherited)\n fileSecurity: true, // enforce per-file permissions\n enabled: true,\n maximumFileSize: 10 * 1024 * 1024, // 10 MB\n allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'],\n // Default permissions for files in this bucket\n permissions: [\n Permission.read(Role.any()), // anyone can view\n Permission.create(Role.any()), // anyone can upload\n Permission.update(Role.any()), // anyone can update\n Permission.delete(Role.any()), // anyone can delete\n ],\n });\n console.log('Bucket created:', bucket.$id);\n return bucket.$id;\n}\n\n// ─────────────────────────────────────────────\n// 3. Upload a file with custom permissions\n// ─────────────────────────────────────────────\nasync function uploadFile(bucketId, fileInputElement) {\n const file = await storage.createFile({\n bucketId: bucketId,\n fileId: ID.unique(),\n file: fileInputElement.files[0], // from \n permissions: [\n Permission.read(Role.any()), // public read\n Permission.update(Role.user('')), // only a specific user can update\n Permission.delete(Role.user('')), // only that user can delete\n ],\n });\n console.log('File uploaded:', file.$id);\n return file;\n}\n\n// ─────────────────────────────────────────────\n// 4. Get a preview URL for the image\n// ─────────────────────────────────────────────\nfunction getPreviewUrl(bucketId, fileId) {\n // Returns a URL string (no network request needed)\n const previewUrl = storage.getFilePreview({\n bucketId: bucketId,\n fileId: fileId,\n width: 400, // optional: resize width\n height: 300, // optional: resize height\n quality: 80, // optional: image quality (1–100)\n // Other options: border, borderRadius, background, output, etc.\n });\n return previewUrl; // e.g. \"https://cloud.appwrite.io/v1/storage/buckets/.../preview?width=400&height=300\"\n}\n\n// ─────────────────────────────────────────────\n// Usage example\n// ─────────────────────────────────────────────\n(async () => {\n try {\n // Step 1: Create the bucket (run once)\n const bucketId = await createBucket();\n\n // Step 2: Upload a file from an element\n const fileInput = document.getElementById('myFileInput');\n const uploadedFile = await uploadFile(bucketId, fileInput);\n\n // Step 3: Generate a preview URL\n const previewUrl = getPreviewUrl(bucketId, uploadedFile.$id);\n console.log('Preview URL:', previewUrl);\n\n // Use it in an tag\n document.getElementById('preview').src = previewUrl;\n } catch (error) {\n console.error('Error:', error);\n }\n})();\n```\n\n---\n\n## Key Points\n\n### Permission Configuration\n\n| Helper | Purpose |\n|--------|---------|\n| `Permission.read(Role.any())` | Public read access |\n| `Permission.read(Role.user('USER_ID'))` | Specific user can read |\n| `Permission.read(Role.team('TEAM_ID'))` | All team members can read |\n| `Permission.read(Role.team('TEAM_ID', 'editor'))` | Team members with a specific role |\n| `Permission.write(Role.any())` | Grants create + update + delete |\n| `Permission.create(Role.any())` | Upload permission |\n| `Permission.update(Role.any())` | Update permission |\n| `Permission.delete(Role.any())` | Delete permission |\n\n### Important Notes\n\n1. **Bucket creation** (`createBucket`) requires elevated permissions. In the Web SDK, the logged-in user must have a team role that includes `buckets.create`. For production, it's safer to create buckets via the Appwrite Console or a server-side SDK with an API key, then reference the bucket ID in your client code.\n\n2. **File permissions** can be set at upload time (as shown above). If omitted, the bucket's default permissions apply.\n\n3. **`getFilePreview`** returns a **URL string** — it does **not** make a network request. You can use it directly in an `` tag or as a background image. The URL supports on-the-fly image transformations (width, height, quality, crop, border, etc.).\n\n4. **`ID.unique()`** generates a unique ID for the bucket or file. You can also pass a custom string if you prefer.\n\n5. **File input**: The `file` parameter in `createFile` expects a `File` or `Blob` object (from a file input, drag-and-drop, or `fetch` response).\n\n### Alternative: Use an Existing Bucket\n\nIf you already have a bucket created via the Console, skip `createBucket` and just use its ID:\n\n```javascript\nconst BUCKET_ID = 'your-existing-bucket-id';\n\nconst file = await storage.createFile({\n bucketId: BUCKET_ID,\n fileId: ID.unique(),\n file: fileInput.files[0],\n permissions: [\n Permission.read(Role.any()),\n Permission.delete(Role.user('')),\n ],\n});\n\nconst previewUrl = storage.getFilePreview({\n bucketId: BUCKET_ID,\n fileId: file.$id,\n width: 400,\n});\n```", "correct": true, "score": 0.75, "judgeReasoning": "The answer includes all 5 required elements: Storage service instantiation, bucket creation with permissions, storage.createFile() with bucket ID, file ID, and File object, storage.getFilePreview() for image preview, and correct imports. However, there are some issues with the API usage. The Appwrite Web SDK's createBucket(), createFile(), and getFilePreview() methods use positional parameters, not named object parameters (e.g., `storage.createBucket(ID.unique(), 'My Image Bucket', ...)` not `storage.createBucket({bucketId: ..., name: ...})`). The object-style parameter syntax shown is more typical of the server-side SDKs or newer SDK versions. Additionally, getFilePreview() typically returns a URL object, not a string, and uses positional parameters. The permission configuration and overall structure are correct and well-explained, but the API call signatures may not match the actual Web SDK interface, which could cause runtime errors.", "promptTokens": 6920, "completionTokens": 2467, "totalTokens": 9387, "cost": 0.00166, "durationMs": 116638, "tokensPerSecond": 21.15, "toolCallCount": 1 }, { "questionId": "stor-4", "category": "storage", "type": "mcq", "question": "Which Appwrite Storage method provides image manipulation like resizing and cropping?", "choices": [ "storage.getFilePreview()", "storage.transformImage()", "storage.processImage()", "storage.getFileView()" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7358, "completionTokens": 259, "totalTokens": 7617, "cost": 0.001103, "durationMs": 16184, "tokensPerSecond": 16, "toolCallCount": 1 }, { "questionId": "stor-5", "category": "storage", "type": "mcq", "question": "What parameters does getFilePreview() support for image transformation?", "choices": [ "Only width and height", "Only format conversion", "Width, height, gravity, quality, border, border-radius, opacity, rotation, background, and output format", "CSS filter strings" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7416, "completionTokens": 422, "totalTokens": 7838, "cost": 0.001156, "durationMs": 17196, "tokensPerSecond": 24.54, "toolCallCount": 1 }, { "questionId": "stor-6", "category": "storage", "type": "mcq", "question": "How are file permissions handled in Appwrite Storage?", "choices": [ "Files can have individual permissions, or inherit from the bucket depending on fileSecurity setting", "All files in a bucket share the same permissions", "Permissions are only at the project level", "Files are always public" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 7432, "completionTokens": 261, "totalTokens": 7693, "cost": 0.001114, "durationMs": 12226, "tokensPerSecond": 21.35, "toolCallCount": 1 }, { "questionId": "stor-7", "category": "storage", "type": "mcq", "question": "What is the method to download a file from Appwrite Storage?", "choices": [ "storage.downloadFile()", "storage.fetchFile()", "storage.getFileDownload(bucketId, fileId)", "storage.read()" ], "correctAnswer": "C", "modelAnswer": "C", "correct": true, "score": 1, "promptTokens": 7374, "completionTokens": 265, "totalTokens": 7639, "cost": 0.001107, "durationMs": 10342, "tokensPerSecond": 25.62, "toolCallCount": 1 }, { "questionId": "stor-8", "category": "storage", "type": "mcq", "question": "Which bucket setting restricts the types of files that can be uploaded?", "choices": [ "allowedFileExtensions", "fileFilter", "mimeTypes", "uploadRestrictions" ], "correctAnswer": "A", "modelAnswer": "A", "correct": true, "score": 1, "promptTokens": 20530, "completionTokens": 1230, "totalTokens": 21760, "cost": 0.003219, "durationMs": 34244, "tokensPerSecond": 35.92, "toolCallCount": 2 }, { "questionId": "stor-9", "category": "storage", "type": "free-form", "question": "Explain the difference between storage.getFileView(), storage.getFileDownload(), and storage.getFilePreview(). When would you use each?", "correctAnswer": "getFileView() serves files inline for browser display. getFileDownload() triggers a download. getFilePreview() provides image transformation capabilities (resize, crop, format conversion). Only preview supports manipulation parameters.", "rubric": "Must explain: 1) getFileView() for inline/browser display, 2) getFileDownload() for triggering downloads, 3) getFilePreview() for image transformations, 4) When to use each one", "modelAnswer": "Here's a clear breakdown of the three Appwrite Storage methods and when to use each:\n\n---\n\n## 1. `storage.getFileView()`\n\n**What it does:** Returns the raw file content with a `Content-Disposition: inline` header. The browser renders the file directly in the tab/window if it can (images, PDFs, videos, audio, text files). It does **not** trigger a download dialog.\n\n**When to use:**\n- Displaying images in an `` tag or as a background\n- Embedding PDFs in an `