Skip to content

Unify web SDK Client auth factories#1481

Open
ChiragAgg5k wants to merge 10 commits intomasterfrom
feat/web-isomorphic-server-client
Open

Unify web SDK Client auth factories#1481
ChiragAgg5k wants to merge 10 commits intomasterfrom
feat/web-isomorphic-server-client

Conversation

@ChiragAgg5k
Copy link
Copy Markdown
Member

@ChiragAgg5k ChiragAgg5k commented Apr 27, 2026

Summary

Refactors the generated Web SDK so browser and server usage share one Client class instead of emitting separate Client and ServerClient classes. The SDK now exposes typed factory methods for the supported auth modes and uses those auth capabilities to gate service methods at compile time.

This keeps the merged web/node SDK direction intact while making the public API simpler: users construct the client from the credential shape they already have, then pass that client to generated services.

import { Client, TablesDB, Account, Realtime } from 'appwrite';

const admin = Client.fromApiKey({
  endpoint: 'https://cloud.appwrite.io/v1',
  projectId: 'project-id',
  apiKey: process.env.APPWRITE_API_KEY!,
});

const session = Client.fromSession({
  endpoint: 'https://cloud.appwrite.io/v1',
  projectId: 'project-id',
  session: sessionSecret,
});

const dev = Client.fromDevKey({
  endpoint: 'https://cloud.appwrite.io/v1',
  projectId: 'project-id',
  devKey: devKey,
});

await new TablesDB(admin).create({ databaseId, tableId, name }); // admin API: ok
await new Account(session).get();                                // browser/session API: ok
new Realtime(dev);                                               // dev key is client-side auth

Client API

The generated Client now supports these static constructors:

Factory Runtime Auth capability Purpose
Client.anonymous(params) / Client.fromAnonymous(params) browser anonymous Public/browser client with endpoint and project configured.
Client.fromSession(params) browser session Browser/session-authenticated client.
Client.fromJWT(params) browser jwt Browser JWT-authenticated client.
Client.fromDevKey(params) browser devKey Client SDK development/testing key for bypassing abuse limits and CORS errors in non-production environments.
Client.fromImpersonation(params) browser impersonation Browser client impersonating exactly one user target by userId, email, or phone.
Client.fromApiKey(params) server apiKey Server/admin client with API key support, optional JWT, and forwarded user agent.

All factories use object parameters. Common fields are endpoint, projectId, optional endpointRealtime, and optional locale. Auth-specific fields are named by credential type, for example session, jwt, devKey, or apiKey.

The existing fluent setters are still present for compatibility. Setters that establish an auth mode narrow the TypeScript type, for example new Client().setEndpoint(...).setProject(...).setKey(...) becomes Client<'apiKey'> and can be used with admin-only methods. These setters continue to mutate this, so existing imperative usage such as client.setKey(key) remains valid. They now also update runtime and x-sdk-platform to match the equivalent factory behavior.

Type Safety

The generated client is generic over an auth capability:

type ClientAuth = 'anonymous' | 'session' | 'jwt' | 'apiKey' | 'devKey' | 'impersonation';
type AdminAuth = 'apiKey';
type BrowserAuth = 'anonymous' | 'session' | 'jwt' | 'devKey' | 'impersonation';

Service generation uses those types directly:

  • Server-only services accept Client<AdminAuth>.
  • Browser-only services accept Client<BrowserAuth>.
  • Mixed services are generated as Service<TAuth extends ClientAuth = ClientAuth> and use TypeScript this parameter gates for individual methods.
  • Realtime accepts only Client<BrowserAuth> in the Web SDK. React Native keeps its non-generic Client type because it reuses the realtime template but has a different client implementation.

This means a mixed service like TablesDB can expose shared methods to both client kinds while rejecting admin-only calls when constructed with Client.fromSession(...) or Client.fromDevKey(...).

Implementation Details

File Change
templates/web/src/client.ts.twig Unifies browser/server auth into one generated Client, adds static from* factories, adds runtime-aware request behavior, exports auth capability types, treats dev keys as browser/client auth, and keeps fluent setters mutation-compatible while aligning runtime/platform headers with factories.
templates/web/src/server-client.ts.twig Removed. The generator no longer emits a separate server client file.
src/SDK/Language/Web.php Removes src/server-client.ts from the generated Web SDK file list.
templates/web/src/services/template.ts.twig Replaces `Client
templates/web/src/services/realtime.ts.twig Narrows Realtime to browser-capable clients only for Web generation; React Native output remains non-generic.
templates/web/src/index.ts.twig Re-exports ClientAuth, AdminAuth, and BrowserAuth from client.ts.
.github/workflows/sdk-build-validation.yml Adds a Web server-platform build validation row so CI catches server-mode generation/build regressions.

Review Feedback Addressed

  • Removed the public ServerClient type alias/export so the public surface now matches the intended single-Client factory API.
  • Updated setKey, setSession, and impersonation setters so legacy setter chains and Client.from* factories agree on runtime and x-sdk-platform behavior.
  • Preserved in-place setter mutation compatibility, including discarded-return usage like client.setKey(key).
  • Made the Realtime Client<BrowserAuth> narrowing Web-only so React Native builds do not import Web-only auth capability types.
  • Moved dev keys to browser/client auth based on the Appwrite dev-key docs: dev keys are for Client SDKs in development/testing, while API keys are for server SDKs and CLI production use.
  • Made generated service auth-type imports conditional to avoid unused ClientAuth/AdminAuth/BrowserAuth aliases in pure browser or pure server services.
  • Relaxed location/webAuth method gates so dual browser/server-platform methods remain callable from both valid auth tiers.
  • Added method-level auth import flags so all-dual-platform mixed services do not import unused AdminAuth/BrowserAuth aliases.

Runtime Behavior

The unified client keeps the same HTTP plumbing but now tracks whether it is being used in browser or server mode. API-key factories/setters use server runtime. Session, JWT, dev-key, anonymous, and impersonation factories/setters use browser runtime.

Browser-only behavior, such as fallback cookies, credential inclusion, opaque response handling, and browser FormData/File assumptions, is kept behind browser-runtime checks. Server auth fields such as API key, forwarded user agent, JWT, and locale are configured on the same client config object.

fromImpersonation validates that exactly one target is provided at runtime, and the parameter type also rejects multiple targets at compile time.

Validation

Local validation performed:

  • php example.php web server
  • cd examples/web && npm run build
  • temporary TypeScript checks covering discarded-return fluent setter mutation, runtime header parity, and dev-key browser-vs-admin type classification
  • php example.php react-native client
  • cd examples/react-native && npm run build
  • composer lint-twig
  • php example.php web server after conditional service import changes
  • cd examples/web && npm run build after conditional service import changes
  • php example.php web server, cd examples/web && npm run build, and composer lint-twig after dual-platform location/webAuth gate cleanup
  • php example.php web server, cd examples/web && npm run build, and composer lint-twig after method-level auth import flag cleanup

CI has shown Greptile, Twig lint, web (client), web (server), web (console), react-native (client), node (server), and other validation jobs passing on the prior commit while the broader matrix continues to drain through queued runners. The latest dev-key correction has been pushed and will re-run those checks.

The temporary local type-check scripts and regenerated example output were not committed.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

This PR unifies the Web SDK into a single generic Client<TAuth> class with typed static factory methods (fromApiKey, fromSession, fromJWT, fromDevKey, fromImpersonation) and eliminates the separate ServerClient file. The conditional import logic for ClientAuth/AdminAuth/BrowserAuth in the service template correctly handles all tier combinations (pure-browser, pure-server, mixed, and all-dual-platform mixed), resolving previous noUnusedLocals concerns. The this-gate overload pattern, browser-runtime guards for cookie fallback and opaque responses, and the chunkedUpload header copy fix are all sound.

Confidence Score: 4/5

Safe to merge; one minor type-chain quirk in fromImpersonation does not affect runtime correctness.

No P0/P1 issues found in the new code. The only finding is a P2 style concern in fromImpersonation where the TypeScript phantom type narrows mid-chain to Client<'session'> before the impersonation setters are called. This compiles and behaves correctly today but is fragile if this-constraints are ever added to the impersonation setters.

templates/web/src/client.ts.twig — specifically the fromImpersonation factory chain around line 489.

Important Files Changed

Filename Overview
templates/web/src/client.ts.twig Unifies browser/server auth into a single generic Client<TAuth> class with static factory methods and in-place fluent setters; phantom-type pattern is sound but fromImpersonation mid-chain narrows to Client<'session'> unnecessarily.
templates/web/src/services/template.ts.twig Replaces flat `Client
templates/web/src/services/realtime.ts.twig Narrows Realtime constructor to Client<BrowserAuth> for Web only via language guard; React Native path unchanged.
templates/web/src/index.ts.twig Re-exports new ClientAuth, AdminAuth, and BrowserAuth type aliases; adds missing trailing newline.
.github/workflows/sdk-build-validation.yml Adds web (server) CI matrix row to catch server-mode generation and build regressions.

Reviews (16): Last reviewed commit: "Avoid unused auth imports in dual servic..." | Re-trigger Greptile

Comment thread templates/web/src/server-client.ts.twig Outdated
Comment thread templates/web/src/services/template.ts.twig Outdated
Comment thread templates/web/src/server-client.ts.twig Outdated
@ChiragAgg5k ChiragAgg5k force-pushed the feat/web-isomorphic-server-client branch from 31090ee to 2e8c91b Compare April 29, 2026 09:43
Comment thread templates/web/src/services/template.ts.twig
@ChiragAgg5k ChiragAgg5k force-pushed the feat/web-isomorphic-server-client branch 5 times, most recently from 8d88082 to 5d5bfba Compare April 29, 2026 10:44
Comment thread templates/web/src/server-client.ts.twig Outdated
@ChiragAgg5k ChiragAgg5k force-pushed the feat/web-isomorphic-server-client branch from 5d5bfba to 6d4e4fa Compare April 29, 2026 10:54
Comment thread templates/web/src/services/template.ts.twig
Comment thread templates/web/src/services/template.ts.twig Outdated
@ChiragAgg5k ChiragAgg5k changed the title Add ServerClient to web SDK with type-gated admin methods Unify web SDK Client auth factories Apr 30, 2026
Comment thread templates/web/src/client.ts.twig Outdated
Comment thread templates/web/src/client.ts.twig
Comment thread templates/web/src/client.ts.twig
Comment thread templates/web/src/client.ts.twig
Comment thread templates/web/src/services/template.ts.twig Outdated
Comment thread templates/web/src/services/template.ts.twig Outdated
Adds a ServerClient sibling class to the web SDK alongside the existing
Client. Service classes are generic over `Client | ServerClient` when
they have any client-tier methods, with TypeScript `this`-types gating
admin methods (e.g. `Databases.createCollection` requires
`Databases<ServerClient>`). Services with no client-tier methods
(Health, Tokens, Sites, Users) are non-generic and require a
ServerClient at construction.

Tier detection is driven entirely off existing `x-appwrite.platforms`
spec tags. The existing Client surface is unchanged — purely additive
for current `appwrite` web users; sets up the path to consolidate
`appwrite` + `node-appwrite` into a single isomorphic package.

- Filter `Key` out of Client header iterations so Client cannot setKey
- Add server-client.ts.twig (setKey/setJWT/setLocale + HTTP plumbing,
  no realtime, no session/devkey/impersonate)
- Type-gate service methods via `this: Service<ServerClient>` (or
  `<Client>` for the few client-only methods like webAuth/location)
- Re-export ServerClient from index.ts
- Register the new template in Web.php getFiles()

Verified on regenerated examples/web/: tsc --noEmit passes; negative
tests confirm `new Health(browserClient)` and admin calls on a
Client-bound service fail to type-check; djlint passes.
@ChiragAgg5k ChiragAgg5k force-pushed the feat/web-isomorphic-server-client branch from d8a6359 to 071a493 Compare April 30, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant