Appwrite Client SDK vs Server SDK: Database Access for Web Apps
Choose the Client SDK for browser-based Appwrite database access and the Server SDK only for trusted backends. A decision guide with permission trade-offs and validation steps.
11 Apr 2026, 02:16 UTC

The decision: where should your web app talk to Appwrite?
You are building a web application that reads and writes documents in an Appwrite database. You can initialize the Appwrite Client SDK in the browser, or you can call the Server SDK from a backend service. These are not interchangeable. The Client SDK authenticates end users through a JWT or session and lets Appwrite enforce collection and document permissions on every request. The Server SDK authenticates with a secret API key and operates with admin privileges, bypassing row-level and collection-level permission checks.
Useful takeaway: Use the Client SDK in browsers, mobile apps, and other untrusted clients so users can only read or write documents they are authorized to access. Use the Server SDK only in trusted backend environments—such as Appwrite Functions, cron jobs, or admin services—where the API key never reaches the client.
Constraints that decide the SDK
- Where does the code run? Browser code is untrusted. Backend code is trusted.
- Who is making the request? An end user acting on their own data, or a system process acting on behalf of the project?
- Can the secret stay secret? If the key ships in JavaScript or a mobile binary, it is no longer secret.
Supported options compared
| Factor | Client SDK | Server SDK |
|---|---|---|
| Authentication | End-user JWT or session | Secret project API key |
| Privilege level | Authenticated user | Project admin |
| Permission enforcement | Appwrite enforces collection and document permissions | Bypasses collection and document-level checks |
| Safe in browser? | Yes | No |
| Typical environments | Web frontend, mobile apps | Appwrite Functions, backend services, cron jobs |
| CORS | Requires allowed origins in the Appwrite console | Not applicable; server-to-server |
Trade-offs
The Client SDK shifts authorization to Appwrite. If a user lacks write permission on a collection, the database returns a 403 response automatically. You do not need to re-implement access rules in frontend code, though you should still handle errors gracefully in the UI.
The Server SDK removes that safety net. Because it runs with admin privileges, your backend code becomes the authorization layer. You must check, for example, that the requesting user owns a document before updating it. The benefit is that backend processes can perform operations no end user should perform, such as generating reports or cleaning up orphaned records.
The critical risk is mixing the two. Embedding a server API key in frontend JavaScript grants full database access to anyone who can inspect the bundle. That key belongs in environment variables on the server only.
Concrete implementation pattern
Frontend: Client SDK in the browser
Run this in the browser after the user has authenticated. Replace placeholders with your project values.
import { Client, Databases, Account } from "appwrite";
const client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("YOUR_PROJECT_ID");
const account = new Account(client);
const databases = new Databases(client);
// Sign in first, then read documents the user is allowed to see.
await account.createSession("[contact removed]", "YOUR_PASSWORD");
const response = await databases.listDocuments(
"YOUR_DATABASE_ID",
"YOUR_COLLECTION_ID"
);
console.log(response.documents);
Do not pass X-Appwrite-Key from frontend code. The Client SDK uses the user's session, not the project API key.
Backend: Server SDK in a trusted service
Run this in Node.js on a server or in an Appwrite Function. Keep the API key in an environment variable, never in source control.
import { Client, Databases } from "appwrite";
const client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("YOUR_PROJECT_ID")
.setKey(process.env.APPWRITE_API_KEY);
const databases = new Databases(client);
// Admin operation: Appwrite does not enforce document permissions here.
// Your code must enforce authorization before writing.
await databases.createDocument(
"YOUR_DATABASE_ID",
"YOUR_COLLECTION_ID",
"unique()",
{ name: "Example record" }
);
Validation steps
- Create a test collection in the Appwrite console and restrict read and write permissions to a specific role.
- From a browser, authenticate as a user without that role and attempt a Client SDK write. Expect a 403 response.
- From a backend script, initialize the Server SDK with the project API key and write to the same collection. Expect success because admin access bypasses the collection restriction.
- Open the browser Network tab while the frontend runs. Confirm no
X-Appwrite-Keyheader is sent from client-side code. - Review the API keys page in the Appwrite console. Confirm only server-side services hold the secret key.
If step 2 does not return 403, review the collection permissions and the user's assigned roles. If step 5 shows a frontend service with the server key, remove it immediately and rotate the key.
Limitations
- Client SDK requests are subject to CORS. Add your web domain to the allowed origins list in the Appwrite console.
- Server SDK admin access ignores document-level permissions. Backend code must enforce its own authorization logic before mutating data.
- SDK behavior and supported runtimes vary by version. Match the SDK release to your project language and Appwrite deployment.
There is no rollback for this decision because selecting an SDK is a design choice, not a state-changing operation. If you discover a server key has been exposed, rotate the API key in the Appwrite console and redeploy affected clients.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.