Skip to content

Feat/sdk v0.2.0 upgrade#90

Merged
yash-pouranik merged 6 commits intomainfrom
feat/sdk-v0.2.0-upgrade
Apr 11, 2026
Merged

Feat/sdk v0.2.0 upgrade#90
yash-pouranik merged 6 commits intomainfrom
feat/sdk-v0.2.0-upgrade

Conversation

@yash-pouranik
Copy link
Copy Markdown
Collaborator

@yash-pouranik yash-pouranik commented Apr 10, 2026

This PR upgrades the @urbackend/sdk from v0.1.1 to v0.2.0. This is a significant update that brings the TypeScript SDK to full parity with the
urBackend Public API, addressing several feature gaps, fixing bugs in token management, and adding comprehensive support for Row-Level Security
(RLS) and query building.

Key Changes

🔐 Authentication Module

  • Token Management Fix: Migrated from storing the deprecated token field to accessToken. Added a deprecation warning for legacy backend
    responses.
  • Full Auth Coverage: Added methods for:
    • Profile Management: updateProfile, changePassword.
    • Account Verification: verifyEmail, resendVerificationOtp.
    • Password Recovery: requestPasswordReset, resetPassword.
    • User Discovery: publicProfile(username).
  • Token Rotation: Implemented refreshToken() supporting both browser cookies and manual header modes.
  • Social Auth: Added socialStart(provider) to build redirect URLs and socialExchange(payload) to complete the OAuth flow.
  • Enhanced Logout: logout() now calls the server-side revocation endpoint before clearing local state.

🗄️ Database Module

  • Query Builder: getAll() now accepts a QueryParams object supporting filter, sort, limit, page, and populate. Filters automatically map to the
    backend's suffix pattern (e.g., { price_gt: 100 }).
  • RLS Support: Added an optional token parameter to insert, update, patch, and delete to allow authenticated writes from the frontend using
    Publishable Keys.
  • Partial Updates: Introduced db.patch() for PATCH requests.
  • Relationship Loading: Added populate support to getOne().

📦 New Modules

  • Schema Module: Added client.schema.getSchema(collection) for introspection of collection definitions.
  • Mail Module: Added client.mail.send(payload) for server-side email dispatch (requires Secret Key).

🛠️ Client & Types

  • Lazy Loading: Modules are now initialized only when accessed.
  • Improved Security UX: The browser warning for Secret Keys now specifically detects sk_live_ prefixes, preventing false positives for
    Publishable Keys.
  • Type Safety: Significantly expanded src/types/index.ts to provide strict typing for all new payloads and API responses.
  • Version Bump: Updated User-Agent to urbackend-sdk-js/0.2.0.

Technical Notes

  • Stateless Client: The main UrBackendClient remains a thin, stateless wrapper. Auth state is managed within the AuthModule, making it easy to
    pass tokens into database operations: client.db.insert('posts', data, client.auth.getToken()).
  • Standardized Responses: Updated SDK handling to correctly unwrap standardized API responses ({ success, data, message }).

Verification Results

  • Unit Tests: All 19 tests passed across 5 test files (auth, database, storage, schema, mail).
  • Build: Successfully compiled CJS, ESM, and DTS bundles via tsup.
  • Lint: Passed ESLint with zero errors.

1 Test Files 5 passed (5)
2 Tests 19 passed (19)
3 Duration 1.20s


Built with ❤️ for urBackend.

Summary by CodeRabbit

  • New Features

    • Expanded SDK: richer auth flows (profile, password change/reset, email verification, social auth), mail sending, schema retrieval, and enhanced database queries/partial updates with optional token support.
  • Documentation

    • Removed prior roadmap/version note from AGENTS.md.
  • Tests

    • Added and updated tests for auth, database (query/patch/token flows), mail, and schema.
  • Chores

    • Version bumped to v0.9.0 (root and SDK packages).

- Bump SDK version to v0.2.0 and monorepo to v0.9.0.
- Implement new modules: Mail and Schema introspection.
- Enhance AuthModule with profile updates, social auth, and session management.
- Add DatabaseModule query builder (filter, sort, page, populate) and patch support.
- Fix vitest type configuration and test expectations.
- Update client with unified SK/PK security warnings and module integration.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

Bumps package versions and removes an outdated roadmap from AGENTS.md; expands the TypeScript SDK with Schema and Mail modules, extends Auth (social auth, token handling, profile/password flows) and Database (query params, patch, token forwarding) modules, updates types, and adds/updates tests.

Changes

Cohort / File(s) Summary
Version & Docs
AGENTS.md, package.json, sdks/urbackend-sdk/package.json
Removed the "Current version: v0.8.0" roadmap block from AGENTS.md; bumped root version to 0.9.0 and SDK package to 0.2.0.
SDK Core & Exports
sdks/urbackend-sdk/src/client.ts, sdks/urbackend-sdk/src/index.ts
Client: added lazy getters for schema and mail, stricter browser secret-key warning, merged options.headers/options.credentials into requests, adjusted JSON response extraction, added getBaseUrl()/getApiKey(), updated User-Agent. Index: re-exported new modules.
Auth Module
sdks/urbackend-sdk/src/modules/auth.ts, sdks/urbackend-sdk/src/types/...
Added many auth flows and helpers (updateProfile, changePassword, email verification, password reset flows, publicProfile, refreshToken, socialStart, socialExchange, setToken, getToken), converted logout() to async server+local flow, prefer accessToken over deprecated token.
Database Module
sdks/urbackend-sdk/src/modules/database.ts, sdks/urbackend-sdk/src/types/...
Added QueryParams to getAll, populate/expand to getOne, optional token forwarded on writes/deletes, added patch() (PATCH semantics), delete() now returns { deleted: boolean }, and introduced a query-string helper.
New Modules
sdks/urbackend-sdk/src/modules/schema.ts, sdks/urbackend-sdk/src/modules/mail.ts
Added SchemaModule.getSchema(collection) and MailModule.send(payload) with typed request/response behavior and client injection.
Types
sdks/urbackend-sdk/src/types/index.ts
Extended RequestOptions with credentials/headers, added QueryParams and multiple auth/mail/schema/patch interfaces, adjusted AuthResponse to prefer accessToken and made fields optional, extended UploadResponse.
Tests
sdks/urbackend-sdk/tests/*
Standardized mock setup (beforeEach, vi.resetAllMocks()), switched mocked API key to a publishable key, updated auth/database tests for token semantics, refresh/logout behaviors, query-string and token header assertions, and added tests for mail and schema modules.

Sequence Diagram(s)

sequenceDiagram
    participant App as Client Application
    participant SDK as urbackend-sdk (AuthModule)
    participant API as Backend API
    participant OAuth as OAuth Provider

    App->>SDK: socialStart(provider)
    SDK->>API: GET /api/auth/social/start?provider=github&key=pk_live...
    API-->>SDK: { authorizationUrl }
    SDK-->>App: authorizationUrl

    App->>OAuth: Redirect to authorizationUrl
    OAuth-->>App: Redirect back with code

    App->>SDK: socialExchange({ code, provider })
    SDK->>API: POST /api/auth/social/exchange { code, provider }
    API->>OAuth: Exchange code for tokens
    OAuth-->>API: access token / profile
    API-->>SDK: { accessToken, user, ... }
    SDK-->>App: AuthResponse (accessToken, user)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through lines, bumped versions high,
New mail and schema wings to fly,
Social doors opened with a key so neat,
Tokens refreshed, and tests keep the beat —
Hooray for SDK growth, a carrot-treat! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: upgrading the SDK from v0.1.1 to v0.2.0 with significant feature additions. It aligns with the PR's primary objective and is specific enough to understand the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sdk-v0.2.0-upgrade

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the @urbackend/sdk to v0.2.0, expanding the TypeScript SDK to cover additional Public API capabilities (schema/mail modules, richer auth/database operations) while improving request handling and type definitions.

Changes:

  • Added new SDK modules for schema introspection and server-side mail sending, plus new Vitest coverage.
  • Expanded Auth and Database modules (token refresh/rotation helpers, social auth helpers, PATCH + query params, optional bearer token for RLS writes).
  • Updated client internals (lazy module getters, request option support, User-Agent/version bumps) and bumped repo/sdk versions.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
sdks/urbackend-sdk/tests/schema.test.ts Adds schema module tests
sdks/urbackend-sdk/tests/mail.test.ts Adds mail module tests
sdks/urbackend-sdk/tests/database.test.ts Updates DB tests for query params, patch, tokenized writes
sdks/urbackend-sdk/tests/auth.test.ts Updates auth tests for accessToken, logout, refresh, social/public profile
sdks/urbackend-sdk/src/types/index.ts Adds/extends request/query/auth/db/mail/schema response types
sdks/urbackend-sdk/src/modules/schema.ts Introduces SchemaModule
sdks/urbackend-sdk/src/modules/mail.ts Introduces MailModule
sdks/urbackend-sdk/src/modules/database.ts Adds query builder, patch, tokenized write support
sdks/urbackend-sdk/src/modules/auth.ts Adds expanded auth surface (refresh/logout/social/profile/etc.)
sdks/urbackend-sdk/src/index.ts Re-exports modules/types for consumers
sdks/urbackend-sdk/src/client.ts Lazy module init; request handling updates; UA bump
sdks/urbackend-sdk/package.json SDK version bump to 0.2.0
package.json Monorepo version bump to 0.9.0
package-lock.json Lockfile version updates for bumps
AGENTS.md Removes outdated version/planning notes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +166 to +171
public async socialExchange(payload: SocialExchangePayload): Promise<ApiResponse<SocialExchangeResponse>> {
const response = await this.client.request<ApiResponse<SocialExchangeResponse>>('POST', '/api/userAuth/social/exchange', {
body: payload,
});
return response;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

socialExchange() is typed to return ApiResponse<SocialExchangeResponse>, but UrBackendClient.request() unwraps { success, data, message } responses by returning json.data when present. Since the public API actually returns { success: true, data: { refreshToken }, ... } for this endpoint, this method will resolve to { refreshToken: ... } (data only), not an ApiResponse. Update the return type/implementation to return SocialExchangeResponse (or add a request option to disable unwrapping and return the full envelope) so consumers don't break when they try to access .data.refreshToken.

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +160
/**
* Returns the start URL for social authentication.
* Redirect the user's browser to this URL to begin the flow.
*/
public socialStart(provider: 'github' | 'google'): string {
return `${this.client['baseUrl']}/api/userAuth/social/${provider}/start?key=${this.client['apiKey']}`;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

socialStart() is reaching into UrBackendClient private fields via bracket access (this.client['baseUrl'], this.client['apiKey']). This bypasses encapsulation and can break with minification/refactors. Expose public getters (e.g. getBaseUrl(), getApiKey()), or have the client provide a helper to build absolute URLs for navigation endpoints.

Suggested change
/**
* Returns the start URL for social authentication.
* Redirect the user's browser to this URL to begin the flow.
*/
public socialStart(provider: 'github' | 'google'): string {
return `${this.client['baseUrl']}/api/userAuth/social/${provider}/start?key=${this.client['apiKey']}`;
private getClientBaseUrl(): string {
const clientWithGetters = this.client as UrBackendClient & {
getBaseUrl?: () => string;
};
if (typeof clientWithGetters.getBaseUrl !== 'function') {
throw new Error('UrBackendClient must expose a public getBaseUrl() method for social auth navigation.');
}
return clientWithGetters.getBaseUrl();
}
private getClientApiKey(): string {
const clientWithGetters = this.client as UrBackendClient & {
getApiKey?: () => string;
};
if (typeof clientWithGetters.getApiKey !== 'function') {
throw new Error('UrBackendClient must expose a public getApiKey() method for social auth navigation.');
}
return clientWithGetters.getApiKey();
}
/**
* Returns the start URL for social authentication.
* Redirect the user's browser to this URL to begin the flow.
*/
public socialStart(provider: 'github' | 'google'): string {
return `${this.getClientBaseUrl()}/api/userAuth/social/${provider}/start?key=${this.getClientApiKey()}`;

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +150
public async refreshToken(refreshToken?: string): Promise<AuthResponse> {
const options: any = {};
if (refreshToken) {
options.headers = { 'x-refresh-token': refreshToken, 'x-refresh-token-mode': 'header' };
} else {
options.credentials = 'include';
}

const response = await this.client.request<AuthResponse>('POST', '/api/userAuth/refresh-token', options);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refreshToken() uses const options: any = {} and relies on a non-typed headers property. Since the client already has a RequestOptions type, this should be strongly typed (and RequestOptions should include an optional headers field) to avoid type holes and accidental misuse.

Copilot uses AI. Check for mistakes.
body?: unknown;
token?: string;
isMultipart?: boolean;
credentials?: RequestCredentials;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestOptions is missing a headers?: Record<string,string> field, but the client merges (options as any).headers into the request headers. Add headers to RequestOptions so module code (e.g. refresh-token header mode) can be type-safe and you can remove the any casts in client.request().

Suggested change
credentials?: RequestCredentials;
credentials?: RequestCredentials;
headers?: Record<string, string>;

Copilot uses AI. Check for mistakes.
Comment on lines 84 to 91
export interface AuthResponse {
accessToken: string;
/** @deprecated use accessToken instead */
token: string;
user: AuthUser;
expiresIn?: string;
userId?: string;
user?: AuthUser;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthResponse currently requires both accessToken and the deprecated token. However, the SDK logic explicitly supports servers that return only one of them (response.accessToken || response.token). Make token optional (and likely accessToken optional as well) so the public types match actual/forward-compatible API behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +87
if ((options as any).headers) {
Object.assign(headers, (options as any).headers);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client.request() currently uses (options as any).headers to merge custom headers. Once RequestOptions.headers exists, remove the any cast and use the typed property; this prevents silently accepting non-string header values and improves type safety across modules.

Suggested change
if ((options as any).headers) {
Object.assign(headers, (options as any).headers);
if (options.headers) {
Object.assign(headers, options.headers);

Copilot uses AI. Check for mistakes.
* Fetch the schema definition for a collection
*/
public async getSchema(collection: string): Promise<CollectionSchema> {
return this.client.request<CollectionSchema>('GET', `/api/schemas/${collection}`);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Public API /api/schemas/:collectionName route returns { message, collection } (see apps/public-api/src/controllers/schema.controller.js). getSchema() currently returns the entire response as CollectionSchema, which means callers will actually receive an object with message/collection keys and not the schema itself. Adjust this method to return the collection field (or update the request generic accordingly) so the runtime value matches Promise<CollectionSchema>.

Suggested change
return this.client.request<CollectionSchema>('GET', `/api/schemas/${collection}`);
const response = await this.client.request<{
message: string;
collection: CollectionSchema;
}>('GET', `/api/schemas/${collection}`);
return response.collection;

Copilot uses AI. Check for mistakes.
vi.fn().mockResolvedValue({
ok: true,
headers: new Headers({ 'content-type': 'application/json' }),
json: () => Promise.resolve({ success: true, data: mockSchema }),
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test stubs the schema endpoint to return { success: true, data: mockSchema }, but the actual Public API schema endpoint responds with { message: 'Schema exists', collection: <schema> }. Update the mock (and/or the SDK implementation) so the test reflects the real response shape and prevents false positives.

Suggested change
json: () => Promise.resolve({ success: true, data: mockSchema }),
json: () => Promise.resolve({ message: 'Schema exists', collection: mockSchema }),

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +89
const result = await this.client.request<any>('DELETE', `/api/data/${collection}/${id}`, {
token
});
// The API might return { message: "Document deleted", id: "..." }
return { deleted: !!result };
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete() maps any truthy API response to { deleted: true } via !!result, which will report success even if the server returns an unexpected non-empty payload. Since the Public API returns { message: 'Document deleted', id }, consider validating expected keys (e.g. result.id or a known message) or at least checking result is an object to avoid incorrectly returning deleted: true for malformed responses.

Suggested change
const result = await this.client.request<any>('DELETE', `/api/data/${collection}/${id}`, {
token
});
// The API might return { message: "Document deleted", id: "..." }
return { deleted: !!result };
const result = await this.client.request<{ message?: string; id?: string } | null>(
'DELETE',
`/api/data/${collection}/${id}`,
{
token
}
);
const deleted =
typeof result === 'object' &&
result !== null &&
(result.id === id || result.message === 'Document deleted');
return { deleted };

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +9
beforeEach(() => {
vi.resetAllMocks();
client.auth.logout();
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthModule.logout() is now async and may attempt a network call when a token is present. In beforeEach, it's invoked without await, which can cause cross-test leakage (token not cleared before next test) and can accidentally hit fetch when no mock is installed. Make the hook async and await client.auth.logout() (or clear the token synchronously) to keep tests deterministic.

Suggested change
beforeEach(() => {
vi.resetAllMocks();
client.auth.logout();
beforeEach(async () => {
vi.resetAllMocks();
await client.auth.logout();

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdks/urbackend-sdk/src/client.ts (1)

85-97: ⚠️ Potential issue | 🟡 Minor

Content-Type override negates merged custom headers, and falsy JSON bodies are skipped.

Lines 85–88 merge custom headers, but Line 96 overwrites Content-Type unconditionally. Also, Line 95 uses a truthy check, so payloads like false/0 won’t be sent.

Proposed fix
-    } else if (options.body) {
-      headers['Content-Type'] = 'application/json';
+    } else if (options.body !== undefined) {
+      const hasContentType = Object.keys(headers).some(
+        (key) => key.toLowerCase() === 'content-type',
+      );
+      if (!hasContentType) {
+        headers['Content-Type'] = 'application/json';
+      }
       requestBody = JSON.stringify(options.body);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/client.ts` around lines 85 - 97, The merged custom
headers are being overwritten and some valid falsy bodies are skipped: update
the request construction in client.ts so that when options.isMultipart is true
you use options.body as FormData, otherwise treat presence of a body by checking
"body" in options (not truthiness) and always JSON.stringify(options.body) even
if it's false/0; set headers['Content-Type']='application/json' only if no
Content-Type was provided by merged headers and options.isMultipart is false.
Modify the code around headers, requestBody, options.isMultipart and
options.body to respect existing headers and allow falsy JSON payloads.
🧹 Nitpick comments (4)
sdks/urbackend-sdk/src/client.ts (1)

6-16: Please gate this merge with broader regression suites beyond SDK unit tests.

Given the expanded auth/schema-capable client surface, run the public-api and dashboard-api test suites before release to catch integration drift early.

Based on learnings: Before shipping auth, RLS, or schema changes: run apps/public-api tests, run apps/dashboard-api tests, run apps/web-dashboard lint, run apps/web-dashboard build when frontend changed.

Also applies to: 52-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/client.ts` around lines 6 - 16, This change expands
the UrBackendClient surface (symbols: UrBackendClient, _auth, _schema,
AuthModule, SchemaModule) and must be gated by broader integration/regression
suites: run the apps/public-api and apps/dashboard-api test suites and, if
frontend assets changed, run apps/web-dashboard lint and build before merging;
include the test results in the PR and/or add CI jobs to automatically run these
suites so integration drift is caught prior to releasing changes to
Auth/RLS/Schema (note same requirement applies to the other related block around
the _db/_storage fields).
sdks/urbackend-sdk/src/modules/auth.ts (2)

142-153: Avoid using any type for options object.

The options variable is typed as any, which loses type safety. Consider using a proper type that extends RequestOptions.

Proposed fix
   public async refreshToken(refreshToken?: string): Promise<AuthResponse> {
-    const options: any = {};
+    const options: { headers?: Record<string, string>; credentials?: RequestCredentials } = {};
     if (refreshToken) {
       options.headers = { 'x-refresh-token': refreshToken, 'x-refresh-token-mode': 'header' };
     } else {
       options.credentials = 'include';
     }

     const response = await this.client.request<AuthResponse>('POST', '/api/userAuth/refresh-token', options);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/modules/auth.ts` around lines 142 - 153, The options
object in refreshToken is typed as any; change it to the proper request options
type (e.g., RequestOptions or Partial<RequestOptions> as used by
this.client.request) so you keep type safety, update any necessary import of
RequestOptions, and then assign options.headers and options.credentials with the
correct types; ensure the call
this.client.request<AuthResponse>('POST','/api/userAuth/refresh-token', options)
matches the new options type and adjust the types for sessionToken assignment if
needed.

159-161: Accessing private client properties via bracket notation.

Using this.client['baseUrl'] and this.client['apiKey'] bypasses TypeScript's access control. Consider adding public getters to UrBackendClient or passing these values to the AuthModule constructor for cleaner encapsulation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/modules/auth.ts` around lines 159 - 161, The
socialStart method uses bracket notation to read private fields
(this.client['baseUrl'], this.client['apiKey']); update the design to avoid
bypassing TypeScript access control by either adding public getters on
UrBackendClient (e.g., getBaseUrl(), getApiKey() or readonly properties
baseUrl/apiKey) and change AuthModule.socialStart to call these getters, or
modify AuthModule's constructor to accept baseUrl and apiKey and store them on
the module instance; adjust the socialStart implementation to use the new public
getters or the stored constructor values and remove any bracket-access to
this.client.
sdks/urbackend-sdk/src/modules/database.ts (1)

84-90: The deleted check may incorrectly return false for valid deletions.

The expression { deleted: !!result } relies on the truthiness of result. If the API returns { message: "Deleted" } (as per the comment), !!result will be true. However, if the API returns an empty object {} or if client.request returns undefined for some edge case, the result could be misleading. Consider checking for a successful response more explicitly.

Proposed improvement for clarity
   public async delete(collection: string, id: string, token?: string): Promise<{ deleted: boolean }> {
-    const result = await this.client.request<any>('DELETE', `/api/data/${collection}/${id}`, {
+    await this.client.request<unknown>('DELETE', `/api/data/${collection}/${id}`, {
       token
     });
-    // The API might return { message: "Document deleted", id: "..." }
-    return { deleted: !!result };
+    // If request succeeds without throwing, the delete was successful
+    return { deleted: true };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/modules/database.ts` around lines 84 - 90, The delete
method's returned { deleted: !!result } is too coarse; update the logic in
delete(collection, id, token?) to explicitly determine success from the API
response returned by this.client.request (e.g., check for known success
indicators such as result.id, result.deleted === true, or a result.message like
"Deleted"), and fall back to false only when result is null/undefined and no
success fields exist; ensure the returned object is { deleted: boolean } derived
from those explicit checks rather than just truthiness of result.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sdks/urbackend-sdk/src/modules/schema.ts`:
- Around line 10-11: The getSchema method currently interpolates the collection
param directly into the URL; update getSchema to guard against empty or
whitespace-only collection names and to URL-encode the collection before
inserting into the path (e.g., trim and check collection !== '' then use
encodeURIComponent(collection)) so reserved characters can't break the route;
keep the return type Promise<CollectionSchema> and the call to
this.client.request('GET', ...) but pass the encoded collection value.

In `@sdks/urbackend-sdk/src/types/index.ts`:
- Around line 84-91: The AuthResponse interface declares accessToken as required
but legacy backends may return only the deprecated token field; update the
AuthResponse type (interface AuthResponse) to make accessToken optional
(accessToken?: string) so code paths in login() and refreshToken() that use
response.accessToken || response.token compile correctly, keep the existing
deprecated comment on token to indicate legacy usage.

In `@sdks/urbackend-sdk/tests/auth.test.ts`:
- Around line 7-10: The beforeEach hook calls the async client.auth.logout()
without awaiting it, which can cause races; make the beforeEach async and await
client.auth.logout() (i.e., change the beforeEach to an async function and use
await client.auth.logout()) so the logout completes before each test runs,
ensuring deterministic teardown for tests that depend on auth state.

---

Outside diff comments:
In `@sdks/urbackend-sdk/src/client.ts`:
- Around line 85-97: The merged custom headers are being overwritten and some
valid falsy bodies are skipped: update the request construction in client.ts so
that when options.isMultipart is true you use options.body as FormData,
otherwise treat presence of a body by checking "body" in options (not
truthiness) and always JSON.stringify(options.body) even if it's false/0; set
headers['Content-Type']='application/json' only if no Content-Type was provided
by merged headers and options.isMultipart is false. Modify the code around
headers, requestBody, options.isMultipart and options.body to respect existing
headers and allow falsy JSON payloads.

---

Nitpick comments:
In `@sdks/urbackend-sdk/src/client.ts`:
- Around line 6-16: This change expands the UrBackendClient surface (symbols:
UrBackendClient, _auth, _schema, AuthModule, SchemaModule) and must be gated by
broader integration/regression suites: run the apps/public-api and
apps/dashboard-api test suites and, if frontend assets changed, run
apps/web-dashboard lint and build before merging; include the test results in
the PR and/or add CI jobs to automatically run these suites so integration drift
is caught prior to releasing changes to Auth/RLS/Schema (note same requirement
applies to the other related block around the _db/_storage fields).

In `@sdks/urbackend-sdk/src/modules/auth.ts`:
- Around line 142-153: The options object in refreshToken is typed as any;
change it to the proper request options type (e.g., RequestOptions or
Partial<RequestOptions> as used by this.client.request) so you keep type safety,
update any necessary import of RequestOptions, and then assign options.headers
and options.credentials with the correct types; ensure the call
this.client.request<AuthResponse>('POST','/api/userAuth/refresh-token', options)
matches the new options type and adjust the types for sessionToken assignment if
needed.
- Around line 159-161: The socialStart method uses bracket notation to read
private fields (this.client['baseUrl'], this.client['apiKey']); update the
design to avoid bypassing TypeScript access control by either adding public
getters on UrBackendClient (e.g., getBaseUrl(), getApiKey() or readonly
properties baseUrl/apiKey) and change AuthModule.socialStart to call these
getters, or modify AuthModule's constructor to accept baseUrl and apiKey and
store them on the module instance; adjust the socialStart implementation to use
the new public getters or the stored constructor values and remove any
bracket-access to this.client.

In `@sdks/urbackend-sdk/src/modules/database.ts`:
- Around line 84-90: The delete method's returned { deleted: !!result } is too
coarse; update the logic in delete(collection, id, token?) to explicitly
determine success from the API response returned by this.client.request (e.g.,
check for known success indicators such as result.id, result.deleted === true,
or a result.message like "Deleted"), and fall back to false only when result is
null/undefined and no success fields exist; ensure the returned object is {
deleted: boolean } derived from those explicit checks rather than just
truthiness of result.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75441482-3c9d-4453-b60e-bca6259a2cb4

📥 Commits

Reviewing files that changed from the base of the PR and between 037dfa5 and 2b916ab.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • AGENTS.md
  • package.json
  • sdks/urbackend-sdk/package.json
  • sdks/urbackend-sdk/src/client.ts
  • sdks/urbackend-sdk/src/index.ts
  • sdks/urbackend-sdk/src/modules/auth.ts
  • sdks/urbackend-sdk/src/modules/database.ts
  • sdks/urbackend-sdk/src/modules/mail.ts
  • sdks/urbackend-sdk/src/modules/schema.ts
  • sdks/urbackend-sdk/src/types/index.ts
  • sdks/urbackend-sdk/tests/auth.test.ts
  • sdks/urbackend-sdk/tests/database.test.ts
  • sdks/urbackend-sdk/tests/mail.test.ts
  • sdks/urbackend-sdk/tests/schema.test.ts
💤 Files with no reviewable changes (1)
  • AGENTS.md

@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

@copilot apply changes based on the comments in this thread

- Add headers to RequestOptions for type-safe header passing
- Make accessToken/token optional in AuthResponse
- Remove (options as any).headers cast in client.ts
- Add public getBaseUrl()/getApiKey() getters to UrBackendClient
- Fix refreshToken() to use RequestOptions instead of any
- Fix socialStart() to use public getters instead of bracket access
- Fix socialExchange() return type to SocialExchangeResponse
- Fix getSchema() to extract response.collection
- Fix delete() to validate response keys properly
- Update schema/database test mocks to match real API shapes
- Make beforeEach in auth test async and await logout()

Agent-Logs-Url: https://github.com/geturbackend/urBackend/sessions/2c6f46de-4645-4d3e-8ae1-10e938a9e258

Co-authored-by: yash-pouranik <172860064+yash-pouranik@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 11, 2026

@copilot apply changes based on the comments in this thread

Applied all 10 review comments in commit c4217f5:

  1. RequestOptions.headers — Added headers?: Record<string, string> field to the type.
  2. AuthResponse optional fields — Made both accessToken and token optional to match forward-compatible API behavior.
  3. client.ts any cast — Removed (options as any).headers; now uses the typed options.headers directly.
  4. Public getters — Added getBaseUrl() and getApiKey() to UrBackendClient; socialStart() now calls those instead of bracket-accessing private fields.
  5. refreshToken() typing — Replaced const options: any = {} with const options: RequestOptions = {}.
  6. socialExchange() return type — Changed from ApiResponse<SocialExchangeResponse> to SocialExchangeResponse to match what the client actually returns after unwrapping json.data.
  7. getSchema() extraction — Updated to return response.collection from the { message, collection } shaped API response.
  8. Schema test mock — Updated to { message: 'Schema exists', collection: mockSchema }.
  9. delete() validation — Now checks result.id === id || result.message === 'Document deleted' instead of a bare truthy check; updated the database test mock to include both message and id fields.
  10. beforeEach async — Made the hook async and awaits client.auth.logout() to prevent cross-test token leakage.

All 19 tests pass and TypeScript type check is clean.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 3 unresolved review comments.

Files modified:

  • sdks/urbackend-sdk/src/modules/schema.ts

Commit: 1730e40f9b835d71a7f660976387088d9a07aaf5

The changes have been pushed to the feat/sdk-v0.2.0-upgrade branch.

Time taken: 2m 25s

Fixed 1 file(s) based on 3 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
sdks/urbackend-sdk/src/modules/database.ts (1)

84-97: Response format dependency is brittle.

The deleted determination relies on specific server response shapes (result.id === id or result.message === 'Document deleted'). If the API response format changes, this could return deleted: false even on successful deletion.

Consider documenting the expected API response contract, or alternatively returning deleted: true for any 2xx response without an error (relying on HTTP semantics rather than body inspection).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/src/modules/database.ts` around lines 84 - 97, The delete
method's deleted determination is brittle because it inspects response body
fields; update the delete function to treat any successful HTTP response (i.e.,
the client.request call completed without throwing and/or returns a 2xx status)
as deleted instead of relying on result.id or result.message. Locate the delete
method and the this.client.request call in database.ts and change the logic to
set deleted = true when the request succeeds (or when a 2xx status is present if
client.request exposes it), and only use body-inspection as a fallback; also add
a short comment documenting the expected API contract for deletions in the
delete method.
sdks/urbackend-sdk/tests/auth.test.ts (1)

27-45: Tighten token-rotation coverage, not just request shape.

These cases only protect the accessToken happy path and the transport mode. sdks/urbackend-sdk/src/modules/auth.ts still hydrates auth state from accessToken || token, so a regression in rotated-token persistence or legacy token fallback would still pass here. Please add an assertion on client.auth.getToken() for refreshToken() and keep one deprecated-response test.

Also applies to: 109-148

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/tests/auth.test.ts` around lines 27 - 45, The test only
asserts accessToken from login; extend coverage to assert persisted token
behavior for token rotation and legacy fallback by adding assertions that
client.auth.getToken() returns the right value after calling refreshToken()
(verify refresh returns and persists a new refreshToken/accessToken pair) and
add one deprecated-response test that supplies a legacy "token" field in the
mocked JSON and asserts client.auth.getToken() still hydrates from that legacy
token; modify tests around client.auth.login and the existing refreshToken()
call to validate both returned values and persisted state rather than only the
request shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sdks/urbackend-sdk/tests/auth.test.ts`:
- Around line 4-5: The tests create a shared client instance (const client =
urBackend({ apiKey: mockApiKey })) which can carry auth state across tests;
change this to recreate the client in a beforeEach hook: declare a let client
variable at the top, keep mockApiKey, and instantiate client = urBackend({
apiKey: mockApiKey }) inside beforeEach so every test gets a fresh urBackend
client (remove reliance on logout cleanup and any top-level client
initialization).

---

Nitpick comments:
In `@sdks/urbackend-sdk/src/modules/database.ts`:
- Around line 84-97: The delete method's deleted determination is brittle
because it inspects response body fields; update the delete function to treat
any successful HTTP response (i.e., the client.request call completed without
throwing and/or returns a 2xx status) as deleted instead of relying on result.id
or result.message. Locate the delete method and the this.client.request call in
database.ts and change the logic to set deleted = true when the request succeeds
(or when a 2xx status is present if client.request exposes it), and only use
body-inspection as a fallback; also add a short comment documenting the expected
API contract for deletions in the delete method.

In `@sdks/urbackend-sdk/tests/auth.test.ts`:
- Around line 27-45: The test only asserts accessToken from login; extend
coverage to assert persisted token behavior for token rotation and legacy
fallback by adding assertions that client.auth.getToken() returns the right
value after calling refreshToken() (verify refresh returns and persists a new
refreshToken/accessToken pair) and add one deprecated-response test that
supplies a legacy "token" field in the mocked JSON and asserts
client.auth.getToken() still hydrates from that legacy token; modify tests
around client.auth.login and the existing refreshToken() call to validate both
returned values and persisted state rather than only the request shape.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c8f1daa5-696c-409b-b943-f9b5a8e8b796

📥 Commits

Reviewing files that changed from the base of the PR and between 2b916ab and 1730e40.

📒 Files selected for processing (8)
  • sdks/urbackend-sdk/src/client.ts
  • sdks/urbackend-sdk/src/modules/auth.ts
  • sdks/urbackend-sdk/src/modules/database.ts
  • sdks/urbackend-sdk/src/modules/schema.ts
  • sdks/urbackend-sdk/src/types/index.ts
  • sdks/urbackend-sdk/tests/auth.test.ts
  • sdks/urbackend-sdk/tests/database.test.ts
  • sdks/urbackend-sdk/tests/schema.test.ts
✅ Files skipped from review due to trivial changes (1)
  • sdks/urbackend-sdk/tests/schema.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdks/urbackend-sdk/src/modules/schema.ts
  • sdks/urbackend-sdk/tests/database.test.ts
  • sdks/urbackend-sdk/src/types/index.ts
  • sdks/urbackend-sdk/src/modules/auth.ts

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 1 file(s) based on 1 unresolved review comment.

Files modified:

  • sdks/urbackend-sdk/tests/auth.test.ts

Commit: 5fb59f44d0a64e789d7a2add8eb02ec11368b3a1

The changes have been pushed to the feat/sdk-v0.2.0-upgrade branch.

Time taken: 2m 40s

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@yash-pouranik yash-pouranik temporarily deployed to feat/sdk-v0.2.0-upgrade - urBackend-frankfrut PR #90 April 11, 2026 07:52 — with Render Destroyed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
sdks/urbackend-sdk/tests/auth.test.ts (2)

165-168: publicProfile assertion is too permissive on request options.

expect.any(Object) can pass even with wrong HTTP verb/options. Tighten this to assert at least the method.

Proposed test hardening
   expect(fetchMock).toHaveBeenCalledWith(
     expect.stringContaining('/api/userAuth/public/yash'),
-    expect.any(Object),
+    expect.objectContaining({
+      method: 'GET',
+    }),
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/tests/auth.test.ts` around lines 165 - 168, The test
currently uses expect.any(Object) for the fetch options which allows wrong HTTP
verbs; update the assertion that calls fetchMock (the expect(fetchMock)... line)
to assert the request options include the correct HTTP method (e.g., use
expect.objectContaining({ method: 'GET' }) or the right verb used by
publicProfile) and any required headers instead of expect.any(Object) so the
test fails if the request uses the wrong HTTP verb or missing critical options.

97-104: Strengthen logout request contract assertion.

logout() currently also depends on cookie mode; asserting credentials: 'include' here would better protect against regressions.

Proposed test hardening
   expect(logoutFetchMock).toHaveBeenCalledWith(
     expect.stringContaining('/api/userAuth/logout'),
     expect.objectContaining({
       method: 'POST',
+      credentials: 'include',
       headers: expect.objectContaining({
         Authorization: 'Bearer test-token',
       }),
     }),
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdks/urbackend-sdk/tests/auth.test.ts` around lines 97 - 104, The test for
logout() currently asserts the logout fetch URL and Authorization header but
omits cookie mode; update the assertion on logoutFetchMock to also expect
objectContaining({ credentials: 'include' }) so the fetch call includes cookies.
Locate the call checked in auth.test.ts (logoutFetchMock) and add the
credentials: 'include' expectation alongside method and headers to harden the
contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@sdks/urbackend-sdk/tests/auth.test.ts`:
- Around line 165-168: The test currently uses expect.any(Object) for the fetch
options which allows wrong HTTP verbs; update the assertion that calls fetchMock
(the expect(fetchMock)... line) to assert the request options include the
correct HTTP method (e.g., use expect.objectContaining({ method: 'GET' }) or the
right verb used by publicProfile) and any required headers instead of
expect.any(Object) so the test fails if the request uses the wrong HTTP verb or
missing critical options.
- Around line 97-104: The test for logout() currently asserts the logout fetch
URL and Authorization header but omits cookie mode; update the assertion on
logoutFetchMock to also expect objectContaining({ credentials: 'include' }) so
the fetch call includes cookies. Locate the call checked in auth.test.ts
(logoutFetchMock) and add the credentials: 'include' expectation alongside
method and headers to harden the contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f32ea2cb-7b54-4de2-832c-c3270acde920

📥 Commits

Reviewing files that changed from the base of the PR and between 1730e40 and 5fb59f4.

📒 Files selected for processing (1)
  • sdks/urbackend-sdk/tests/auth.test.ts

@yash-pouranik yash-pouranik merged commit 6c16968 into main Apr 11, 2026
6 checks passed
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.

3 participants