Loading...
Loading...
Appwrite .NET SDK skill. Use when building server-side C# or .NET applications with Appwrite, including ASP.NET and Blazor integrations. Covers user management, database/table CRUD, file storage, and functions via API keys.
npx skill4agent add appwrite/agent-skills appwrite-dotnetdotnet add package Appwriteusing Appwrite;
using Appwrite.Services;
using Appwrite.Models;
var client = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject(Environment.GetEnvironmentVariable("APPWRITE_PROJECT_ID"))
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));var users = new Users(client);
// Create user
var user = await users.Create(ID.Unique(), "user@example.com", null, "password123", "User Name");
// List users
var list = await users.List(new List<string> { Query.Limit(25) });
// Get user
var fetched = await users.Get("[USER_ID]");
// Delete user
await users.Delete("[USER_ID]");Note: Use(not the deprecatedTablesDBclass) for all new code. Only useDatabasesif the existing codebase already relies on it or the user explicitly requests it.DatabasesTip: Prefer named arguments (e.g.,) for all SDK method calls. Only use positional arguments if the existing codebase already uses them or the user explicitly requests it.databaseId: "..."
var tablesDB = new TablesDB(client);
// Create database
var db = await tablesDB.Create(ID.Unique(), "My Database");
// Create row
var doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } });
// Query rows
var results = await tablesDB.ListRows("[DATABASE_ID]", "[TABLE_ID]",
new List<string> { Query.Equal("title", "Hello World"), Query.Limit(10) });
// Get row
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
// Update row
await tablesDB.UpdateRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]",
new Dictionary<string, object> { { "title", "Updated" } });
// Delete row
await tablesDB.DeleteRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");Note: The legacytype is deprecated. Use explicit column types for all new columns.string
| Type | Max characters | Indexing | Storage |
|---|---|---|---|
| 16,383 | Full index (if size ≤ 768) | Inline in row |
| 16,383 | Prefix only | Off-page |
| 4,194,303 | Prefix only | Off-page |
| 1,073,741,823 | Prefix only | Off-page |
varchartextmediumtextlongtextsize// Create table with explicit string column types
await tablesDB.CreateTable("[DATABASE_ID]", ID.Unique(), "articles",
new List<object> {
new { key = "title", type = "varchar", size = 255, required = true }, // inline, fully indexable
new { key = "summary", type = "text", required = false }, // off-page, prefix index only
new { key = "body", type = "mediumtext", required = false }, // up to ~4 M chars
new { key = "raw_data", type = "longtext", required = false }, // up to ~1 B chars
});// Filtering
Query.Equal("field", "value") // == (or pass array for IN)
Query.NotEqual("field", "value") // !=
Query.LessThan("field", 100) // <
Query.LessThanEqual("field", 100) // <=
Query.GreaterThan("field", 100) // >
Query.GreaterThanEqual("field", 100) // >=
Query.Between("field", 1, 100) // 1 <= field <= 100
Query.IsNull("field") // is null
Query.IsNotNull("field") // is not null
Query.StartsWith("field", "prefix") // starts with
Query.EndsWith("field", "suffix") // ends with
Query.Contains("field", "sub") // contains
Query.Search("field", "keywords") // full-text search (requires index)
// Sorting
Query.OrderAsc("field")
Query.OrderDesc("field")
// Pagination
Query.Limit(25) // max rows (default 25, max 100)
Query.Offset(0) // skip N rows
Query.CursorAfter("[ROW_ID]") // cursor pagination (preferred)
Query.CursorBefore("[ROW_ID]")
// Selection & Logic
Query.Select(new List<string> { "field1", "field2" })
Query.Or(new List<string> { Query.Equal("a", 1), Query.Equal("b", 2) }) // OR
Query.And(new List<string> { Query.GreaterThan("age", 18), Query.LessThan("age", 65) }) // AND (default)var storage = new Storage(client);
// Upload file
var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(), InputFile.FromPath("/path/to/file.png"));
// List files
var files = await storage.ListFiles("[BUCKET_ID]");
// Delete file
await storage.DeleteFile("[BUCKET_ID]", "[FILE_ID]");using Appwrite.Models;
InputFile.FromPath("/path/to/file.png") // from filesystem path
InputFile.FromBytes(byteArray, "file.png", "image/png") // from byte[]
InputFile.FromStream(stream, "file.png", "image/png", size) // from Stream (size required)var teams = new Teams(client);
// Create team
var team = await teams.Create(ID.Unique(), "Engineering");
// List teams
var list = await teams.List();
// Create membership (invite user by email)
var membership = await teams.CreateMembership(
teamId: "[TEAM_ID]",
roles: new List<string> { "editor" },
email: "user@example.com"
);
// List memberships
var members = await teams.ListMemberships("[TEAM_ID]");
// Update membership roles
await teams.UpdateMembership("[TEAM_ID]", "[MEMBERSHIP_ID]", new List<string> { "admin" });
// Delete team
await teams.Delete("[TEAM_ID]");Role-based access: Usefor all team members orRole.Team("[TEAM_ID]")for a specific team role when setting permissions.Role.Team("[TEAM_ID]", "editor")
var functions = new Functions(client);
// Execute function
var execution = await functions.CreateExecution("[FUNCTION_ID]", body: "{\"key\": \"value\"}");
// List executions
var executions = await functions.ListExecutions("[FUNCTION_ID]");// src/Main.cs — Appwrite Function entry point
using System.Text.Json;
public async Task<RuntimeOutput> Main(RuntimeContext context)
{
// context.Req.Body — raw body (string)
// context.Req.BodyJson — parsed JSON (JsonElement)
// context.Req.Headers — headers (Dictionary)
// context.Req.Method — HTTP method
// context.Req.Path — URL path
// context.Req.Query — query params (Dictionary)
context.Log($"Processing: {context.Req.Method} {context.Req.Path}");
if (context.Req.Method == "GET")
return context.Res.Json(new { message = "Hello from Appwrite Function!" });
return context.Res.Json(new { success = true }); // JSON
// context.Res.Text("Hello"); // plain text
// context.Res.Empty(); // 204
// context.Res.Redirect("https://..."); // 302
}using Appwrite;
using Appwrite.Services;
// Admin client (reusable)
var adminClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetKey(Environment.GetEnvironmentVariable("APPWRITE_API_KEY"));
// Session client (create per-request)
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]");
var session = Request.Cookies["a_session_[PROJECT_ID]"];
if (session != null)
{
sessionClient.SetSession(session);
}app.MapPost("/login", async (HttpContext ctx, LoginRequest body) =>
{
var account = new Account(adminClient);
var session = await account.CreateEmailPasswordSession(body.Email, body.Password);
// Cookie name must be a_session_<PROJECT_ID>
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Path = "/",
});
return Results.Ok(new { success = true });
});app.MapGet("/user", async (HttpContext ctx) =>
{
var session = ctx.Request.Cookies["a_session_[PROJECT_ID]"];
if (session == null) return Results.Unauthorized();
var sessionClient = new Client()
.SetEndpoint("https://<REGION>.cloud.appwrite.io/v1")
.SetProject("[PROJECT_ID]")
.SetSession(session);
var account = new Account(sessionClient);
var user = await account.Get();
return Results.Ok(user);
});// Step 1: Redirect to OAuth provider
app.MapGet("/oauth", async () =>
{
var account = new Account(adminClient);
var redirectUrl = await account.CreateOAuth2Token(
provider: OAuthProvider.Github,
success: "https://example.com/oauth/success",
failure: "https://example.com/oauth/failure"
);
return Results.Redirect(redirectUrl);
});
// Step 2: Handle callback — exchange token for session
app.MapGet("/oauth/success", async (HttpContext ctx, string userId, string secret) =>
{
var account = new Account(adminClient);
var session = await account.CreateSession(userId, secret);
ctx.Response.Cookies.Append("a_session_[PROJECT_ID]", session.Secret, new CookieOptions
{
HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Path = "/",
});
return Results.Ok(new { success = true });
});Cookie security: Always use,HttpOnly, andSecureto prevent XSS. The cookie name must beSameSite = SameSiteMode.Strict.a_session_<PROJECT_ID>
Forwarding user agent: Callto record the end-user's browser info for debugging and security.sessionClient.SetForwardedUserAgent(ctx.Request.Headers["User-Agent"])
using Appwrite;
try
{
var row = await tablesDB.GetRow("[DATABASE_ID]", "[TABLE_ID]", "[ROW_ID]");
}
catch (AppwriteException e)
{
Console.WriteLine(e.Message); // human-readable message
Console.WriteLine(e.Code); // HTTP status code (int)
Console.WriteLine(e.Type); // error type (e.g. "document_not_found")
Console.WriteLine(e.Response); // full response body
}| Code | Meaning |
|---|---|
| Unauthorized — missing or invalid session/API key |
| Forbidden — insufficient permissions |
| Not found — resource does not exist |
| Conflict — duplicate ID or unique constraint |
| Rate limited — too many requests |
readupdatedeletecreatewritePermissionRoleusing Appwrite;
// Permission and Role are included in the main namespacevar doc = await tablesDB.CreateRow("[DATABASE_ID]", "[TABLE_ID]", ID.Unique(),
new Dictionary<string, object> { { "title", "Hello World" } },
new List<string>
{
Permission.Read(Role.User("[USER_ID]")), // specific user can read
Permission.Update(Role.User("[USER_ID]")), // specific user can update
Permission.Read(Role.Team("[TEAM_ID]")), // all team members can read
Permission.Read(Role.Any()), // anyone (including guests) can read
});var file = await storage.CreateFile("[BUCKET_ID]", ID.Unique(),
InputFile.FromPath("/path/to/file.png"),
new List<string>
{
Permission.Read(Role.Any()),
Permission.Update(Role.User("[USER_ID]")),
Permission.Delete(Role.User("[USER_ID]")),
});When to set permissions: Set document/file-level permissions when you need per-resource access control. If all documents in a collection share the same rules, configure permissions at the collection/bucket level and leave document permissions empty.
Common mistakes:
- Forgetting permissions — the resource becomes inaccessible to all users (including the creator)
withRole.Any()/write/update— allows any user, including unauthenticated guests, to modify or remove the resourcedelete on sensitive data — makes the resource publicly readablePermission.Read(Role.Any())