Skip to content

Conversation

@eluce2
Copy link
Collaborator

@eluce2 eluce2 commented Feb 3, 2026

BREAKING: Config now takes database (fmodata Database instance)
instead of odata: { serverUrl, auth, database }.

  • Delete custom OData fetch wrapper (src/odata/index.ts)
  • All CRUD ops delegate to db._makeRequest()
  • Migrate uses db.schema.createTable/addFields
  • Add _getDatabaseName getter and _makeRequest to fmodata Database
  • Drop neverthrow, odata-query, zod deps
  • Update tests to mock Database instead of global fetch
  • Update docs with FMServerConnection pattern

Note

High Risk
Breaking change replaces the adapter’s request/migration plumbing and CLI integration, affecting all Better Auth FileMaker DB reads/writes and schema migrations. Misconfigured Database instances or differences in request behavior could break authentication flows or migration execution.

Overview
BREAKING: FileMakerAdapter now requires a database (an @proofkit/fmodata Database instance) instead of an odata: { serverUrl, auth, database } config, and docs are updated to show creating it via FMServerConnection.

All adapter CRUD operations now delegate to db._makeRequest() and the custom raw OData fetch wrapper (src/odata/index.ts) plus related deps (zod, odata-query, neverthrow, fm-odata-client) are removed. Migrations/CLI are refactored to use db.getMetadata() and db.schema.createTable/addFields, including better CLI target display and optional Full Access credential overrides by constructing a new FMServerConnection.

@proofkit/fmodata’s Database gains internal helpers (_getDatabaseName, _makeRequest) to support adapter/CLI needs; tests are rewritten to mock a Database instead of global fetch, and a small TS type fix is made in the typegen data grid.

Written by Cursor Bugbot for commit fba5e73. This will update automatically on new commits. Configure here.

BREAKING: Config now takes `database` (fmodata Database instance)
instead of `odata: { serverUrl, auth, database }`.

- Delete custom OData fetch wrapper (src/odata/index.ts)
- All CRUD ops delegate to db._makeRequest()
- Migrate uses db.schema.createTable/addFields
- Add _getDatabaseName getter and _makeRequest to fmodata Database
- Drop neverthrow, odata-query, zod deps
- Update tests to mock Database instead of global fetch
- Update docs with FMServerConnection pattern
@vercel
Copy link

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
proofkit-docs Skipped Skipped Feb 3, 2026 7:31pm

Request Review

Copy link
Collaborator Author

eluce2 commented Feb 3, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@eluce2 eluce2 marked this pull request as ready for review February 3, 2026 18:09
@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

This pull request refactors the better-auth FileMaker adapter from a fetch-based OData configuration approach to using the fmodata Database instance directly. The adapter's public API now requires a Database object, the odata module is removed entirely, and all requests are replaced with Database._makeRequest calls. Migrations, CLI, tests, and dependencies are updated accordingly.

Changes

Cohort / File(s) Summary
Changeset & Documentation
.changeset/better-auth-fmodata-adapter.md, apps/docs/content/docs/better-auth/installation.mdx
Documents breaking change from OData config to Database object; updates code examples showing FMServerConnection initialization and new FileMakerAdapter signature.
Adapter Core
packages/better-auth/src/adapter.ts
Replaces fetch-based requests with Database._makeRequest; changes FileMakerAdapterConfig from optional with defaults to required with mandatory database field; removes odata config; updates all CRUD methods (create, findOne, findMany, count, update, delete, etc.) to use new database abstraction; exposes database on adapter factory.
Migration System
packages/better-auth/src/migrate.ts
Converts getMetadata, planMigration, and executeMigration signatures to accept Database instead of fetch; replaces zod-based MigrationPlan with explicit FmField and MigrationStep interfaces; updates error handling to try/catch around database operations; removes hard-coded string type assumptions.
OData Module Removal
packages/better-auth/src/odata/index.ts
Completely removes FmOdataConfig interface, ODataAuth types, validateUrl, and createRawFetch function; eliminates all fetch-based configuration and wrapper logic.
CLI Integration
packages/better-auth/src/cli/index.ts
Replaces fetch-based execution with FMServerConnection-backed Database; updates planMigration and executeMigration calls to pass Database instance; adds CLI-override logic for server URL and database name derivation.
Dependencies & Configuration
packages/better-auth/package.json, packages/better-auth/tsconfig.json
Adds @proofkit/fmodata dependency; removes neverthrow, odata-query, and fm-odata-client; updates TypeScript target to ES2022 and adds DOM library.
Test Utilities & Mocks
packages/better-auth/tests/utils/mock-fetch.ts
Replaces mock fetch utilities with MockDatabase approach; introduces _makeRequest method; updates all mock helpers (createMockDatabase, createMockDatabaseSequence, createODataSuccessMock, createODataErrorMock) to return MockDatabase objects.
Unit & Integration Tests
packages/better-auth/tests/adapter.test.ts, packages/better-auth/tests/e2e/adapter.test.ts, packages/better-auth/tests/e2e/migrate.test.ts, packages/better-auth/tests/parseWhere.test.ts
Updates test setup to use mock Database instances instead of fetch mocks; replaces getMetadata and CRUD calls with Database-based equivalents; removes validateUrl tests; adjusts table/field type definitions to align with new API (varchar → string).
fmodata Library Extensions
packages/fmodata/src/client/database.ts
Adds _getDatabaseName getter and _makeRequest method to Database class for better-auth integration.

Sequence Diagram

sequenceDiagram
    participant App as Application Code
    participant FSC as FMServerConnection
    participant DB as Database Instance
    participant HTTP as HTTP Layer
    
    App->>FSC: new FMServerConnection({ serverUrl, auth, ... })
    FSC->>DB: database(databaseName)
    DB-->>App: Database instance
    
    App->>App: FileMakerAdapter({ database: db })
    
    App->>DB: query via _makeRequest()
    DB->>HTTP: fetch with path & options
    HTTP-->>DB: Response
    DB-->>App: Result<T> { data or error }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fmdata #76: Introduces the fmodata Database abstraction that this PR depends on; the _makeRequest and _getDatabaseName methods added to Database directly support this adapter migration.
  • betterauth raw odata #53: Implements the raw OData fetch-based approach that this PR replaces; represents the inverse architectural direction.
  • typegen web ui #78: Also modifies the fmodata Database client; may have overlapping changes or dependencies related to the _makeRequest method additions.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: refactoring the better-auth adapter to replace raw OData fetch operations with the fmodata Database abstraction.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description clearly outlines the breaking change replacing raw OData config with a fmodata Database instance and provides specific details about deletions, refactoring, and test updates.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 02-03-refactor_better-auth_replace_raw_odata_fetch_with_fmodata_database

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

Copy link

@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

🤖 Fix all issues with AI agents
In `@packages/better-auth/src/adapter.ts`:
- Around line 131-158: The buildQueryString function must percent-encode OData
parameter values: when adding $filter and $orderby use
encodeURIComponent(params.filter) and encodeURIComponent(params.orderBy)
respectively, and for $select map over params.select and encode each identifier
with encodeURIComponent before joining with commas (e.g.,
params.select.map(encodeURIComponent).join(",")) so special characters (spaces,
&, =, { } [ ] ", etc.) are safely encoded; update the buildQueryString function
to apply these encodings for params.filter, params.orderBy, and params.select.

In `@packages/better-auth/src/cli/index.ts`:
- Around line 55-91: Replace the unsafe "as any" casts when accessing internal
fmodata properties on configDb by using "as unknown as { ... }" shaped types:
cast configDb to a shape that exposes _getDatabaseName to read dbName
(referenced as dbName) and cast configDb to a shape with optional context and
_getBaseUrl() to read baseUrl; keep the rest of the logic (building serverUrl,
creating new FMServerConnection and calling connection.database(dbName))
unchanged so you still create a connection and assign db when options.username
and options.password are provided.

In `@packages/better-auth/src/migrate.ts`:
- Around line 18-27: The catch block in getMetadata discards the thrown error;
change the catch to capture the error (e.g., catch (err)) and include the error
details in the log so failures from db.getMetadata are visible—update the
console.error call in getMetadata to log a descriptive message plus the error
(and optionally the db or format context) to aid debugging.
🧹 Nitpick comments (9)
packages/better-auth/tests/utils/mock-fetch.ts (1)

26-41: Consider adding path/options matching for more specific test assertions.

The current implementation ignores path and options parameters, which works for simple mocks but limits the ability to verify that correct endpoints are being called.

💡 Optional: Add path tracking for test assertions
 export function createMockDatabase(response: MockResponse): MockDatabase {
+  const calls: Array<{ path: string; options?: RequestInit }> = [];
   return {
     _makeRequest: <T>(path: string, options?: RequestInit): Promise<MakeRequestResult<T>> => {
+      calls.push({ path, options });
       if (response.status >= 200 && response.status < 300) {
         return Promise.resolve({ data: response.response as T, error: undefined });
       }
       return Promise.resolve({ data: undefined, error: new Error(`HTTP ${response.status}`) });
     },
     schema: {
       createTable: async () => ({}),
       addFields: async () => ({}),
     },
     getMetadata: async () => ({}),
     _getDatabaseName: "test.fmp12",
+    // Expose for assertions
+    _calls: calls,
   };
 }
packages/better-auth/src/adapter.ts (1)

267-289: Sequential deletes may be slow for large datasets.

The deleteMany operation issues individual DELETE requests in sequence. For large record sets, this could be slow compared to batch operations.

Consider documenting this behavior or evaluating if the fmodata Database supports batch delete operations that could improve performance. This is acceptable for typical Better Auth use cases with small record counts.

packages/better-auth/src/migrate.ts (2)

67-84: Type narrowing for field type could be more explicit.

The type variable is assigned as a string but represents a union of "string" | "numeric" | "timestamp". Consider using a typed variable or the mapFieldType helper for consistency.

💡 Optional: Use explicit type for consistency
             .map(([fieldKey, fieldValue]) => {
-              let type = "string";
+              let type: "string" | "numeric" | "timestamp" = "string";
               if (fieldValue.$Type === "Edm.String") {
                 type = "string";
               } else if (fieldValue.$Type === "Edm.DateTimeOffset") {

169-182: Preserve error stack trace when re-throwing.

Wrapping the error with string interpolation loses the original stack trace, making debugging harder.

🔧 Proposed fix to preserve error context
       try {
         await db.schema.createTable(step.tableName, fmodataFields);
       } catch (error) {
         console.error(`Failed to create table ${step.tableName}:`, error);
-        throw new Error(`Migration failed: ${error}`);
+        throw error instanceof Error 
+          ? new Error(`Migration failed for table ${step.tableName}: ${error.message}`, { cause: error })
+          : new Error(`Migration failed for table ${step.tableName}: ${error}`);
       }
     } else if (step.operation === "update") {
       console.log("Adding fields to table:", step.tableName);
       try {
         await db.schema.addFields(step.tableName, fmodataFields);
       } catch (error) {
         console.error(`Failed to update table ${step.tableName}:`, error);
-        throw new Error(`Migration failed: ${error}`);
+        throw error instanceof Error
+          ? new Error(`Migration failed for table ${step.tableName}: ${error.message}`, { cause: error })
+          : new Error(`Migration failed for table ${step.tableName}: ${error}`);
       }
packages/better-auth/tests/e2e/migrate.test.ts (1)

44-48: Consider logging unexpected errors during cleanup.

The empty catch block is fine for the expected "table doesn't exist" scenario, but it may silently swallow unexpected errors (e.g., permission issues, network failures). Consider differentiating error types or adding a debug log.

🔧 Suggested improvement
     // Delete table if it exists (cleanup)
     try {
       await db.schema.deleteTable(tableName);
-    } catch {
-      // Table might not exist
+    } catch (error) {
+      // Table might not exist - ignore 404-like errors
+      // Log other errors for debugging
+      if (process.env.DEBUG) {
+        console.log("Cleanup error (may be expected):", error);
+      }
     }
packages/better-auth/tests/e2e/adapter.test.ts (4)

6-17: Remove duplicate environment variable checks.

The environment variable validation at lines 6-17 is duplicated inside the describe block at lines 52-63. Since the module-level checks already throw before the describe block runs, the inner checks are unreachable.

♻️ Suggested fix

Remove lines 52-63 as they are redundant:

   await runAdapterTest({
     // biome-ignore lint/suspicious/useAwait: must be an async function
     getAdapter: async (betterAuthOptions = {}) => {
       return adapter(betterAuthOptions);
     },
   });
-
-  if (!process.env.FM_SERVER) {
-    throw new Error("FM_SERVER is not set");
-  }
-  if (!process.env.FM_DATABASE) {
-    throw new Error("FM_DATABASE is not set");
-  }
-  if (!process.env.FM_USERNAME) {
-    throw new Error("FM_USERNAME is not set");
-  }
-  if (!process.env.FM_PASSWORD) {
-    throw new Error("FM_PASSWORD is not set");
-  }

Also applies to: 52-63


93-94: Consider removing debug console.log statement.

This appears to be a debug artifact. Consider removing it or using a conditional debug flag to reduce test output noise.


123-123: Consider removing debug console.log statement.

Same as above - appears to be a leftover debug statement.


97-139: Test is outside the main describe block.

The "should properly filter by dates" test is defined at the module level rather than inside the "My Adapter Tests" describe block. While this works in vitest, consider moving it inside the describe block for better organization and consistent test output grouping.

Comment on lines 131 to 158
/**
* Build an OData query string from parameters.
*/
function buildQueryString(params: {
top?: number;
skip?: number;
filter?: string;
orderBy?: string;
select?: string[];
}): string {
const parts: string[] = [];
if (params.top !== undefined) {
parts.push(`$top=${params.top}`);
}
if (params.skip !== undefined) {
parts.push(`$skip=${params.skip}`);
}
if (params.filter) {
parts.push(`$filter=${params.filter}`);
}
if (params.orderBy) {
parts.push(`$orderby=${params.orderBy}`);
}
if (params.select?.length) {
parts.push(`$select=${params.select.join(",")}`);
}
return parts.length > 0 ? `?${parts.join("&")}` : "";
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

OData $filter parameter URL encoding requirements specification

💡 Result:

What spec governs $filter URL encoding?

OData URLs (including the $filter query option value) follow RFC 3986 URI rules plus OData’s URL Conventions + OData-ABNF rules. OData explicitly assumes inputs are percent-encoding normalized per RFC 3986 §6.2.2.2 before applying the grammar. [1]

Practical encoding requirements for $filter

When placing a $filter expression into a URL query string, you must percent-encode characters that are not allowed in a URI query component or that would change query parsing:

  • Spaces are not allowed unencoded in a URI query; encode as %20 (not +). (+ is only a space substitute in application/x-www-form-urlencoded, not in generic URI syntax.) [3]
  • % must be encoded as %25 if it is data (because % introduces percent-encoding). [2]
  • Query-string delimiters & and = must be encoded as %26 and %3D if they appear inside the $filter expression, otherwise they will be interpreted as separating query options / key-value pairs. (This follows RFC 3986 reserved-character rules.) [2]
  • OData-specific note: for JSON-style literals used in URLs (e.g., via parameter aliases), characters like { } [ ] " MUST be percent-encoded (even if some clients/browsers tolerate them). [4]
  • OData examples show that some characters (like quotes/parentheses) may appear percent-encoded and still represent the same OData URL (e.g., encoding ' as %27, ( as %28). [1]

Example

Readable:

  • ?$filter=Discontinued eq false

Correctly percent-encoded (at least the spaces):

  • ?$filter=Discontinued%20eq%20false

Sources: OData URL Conventions (v4.02) on percent-encoding normalization + examples [1], RFC 3986 on percent-encoding and reserved characters [2], RFC 3986 query validity (space not allowed) [3], OData URL Conventions (v4.02) rule that { } [ ] " must be percent-encoded in URLs [4].

Citations:
[1] (docs.oasis-open.org)
[2] (datatracker.ietf.org)
[3] (stackoverflow.com)
[4] (docs.oasis-open.org)

Citations:


🏁 Script executed:

# Search for how buildQueryString is used and where parameters come from
rg "buildQueryString" packages/better-auth/src/adapter.ts -A 3 -B 3

Repository: proofgeist/proofkit

Length of output: 1891


🏁 Script executed:

# Look for existing encoding patterns in the codebase
rg "encodeURIComponent|decodeURIComponent" packages/ -t ts -t tsx

Repository: proofgeist/proofkit

Length of output: 90


🏁 Script executed:

# Check the broader context of how filter/select parameters are created
rg "filter.*:" packages/better-auth/src/adapter.ts -A 2 -B 2

Repository: proofgeist/proofkit

Length of output: 1232


URL-encode OData query parameters to handle special characters.

The filter, orderBy, and select values must be percent-encoded per the OData specification (RFC 3986 + OData URL Conventions). Spaces, reserved characters (&, =), and special characters ({, }, [, ], ") in these parameter values will cause parsing errors or security issues if not encoded.

🛡️ Proposed fix to encode query parameters
 function buildQueryString(params: {
   top?: number;
   skip?: number;
   filter?: string;
   orderBy?: string;
   select?: string[];
 }): string {
   const parts: string[] = [];
   if (params.top !== undefined) {
     parts.push(`$top=${params.top}`);
   }
   if (params.skip !== undefined) {
     parts.push(`$skip=${params.skip}`);
   }
   if (params.filter) {
-    parts.push(`$filter=${params.filter}`);
+    parts.push(`$filter=${encodeURIComponent(params.filter)}`);
   }
   if (params.orderBy) {
-    parts.push(`$orderby=${params.orderBy}`);
+    parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
   }
   if (params.select?.length) {
-    parts.push(`$select=${params.select.join(",")}`);
+    parts.push(`$select=${encodeURIComponent(params.select.join(","))}`);
   }
   return parts.length > 0 ? `?${parts.join("&")}` : "";
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Build an OData query string from parameters.
*/
function buildQueryString(params: {
top?: number;
skip?: number;
filter?: string;
orderBy?: string;
select?: string[];
}): string {
const parts: string[] = [];
if (params.top !== undefined) {
parts.push(`$top=${params.top}`);
}
if (params.skip !== undefined) {
parts.push(`$skip=${params.skip}`);
}
if (params.filter) {
parts.push(`$filter=${params.filter}`);
}
if (params.orderBy) {
parts.push(`$orderby=${params.orderBy}`);
}
if (params.select?.length) {
parts.push(`$select=${params.select.join(",")}`);
}
return parts.length > 0 ? `?${parts.join("&")}` : "";
}
/**
* Build an OData query string from parameters.
*/
function buildQueryString(params: {
top?: number;
skip?: number;
filter?: string;
orderBy?: string;
select?: string[];
}): string {
const parts: string[] = [];
if (params.top !== undefined) {
parts.push(`$top=${params.top}`);
}
if (params.skip !== undefined) {
parts.push(`$skip=${params.skip}`);
}
if (params.filter) {
parts.push(`$filter=${encodeURIComponent(params.filter)}`);
}
if (params.orderBy) {
parts.push(`$orderby=${encodeURIComponent(params.orderBy)}`);
}
if (params.select?.length) {
parts.push(`$select=${encodeURIComponent(params.select.join(","))}`);
}
return parts.length > 0 ? `?${parts.join("&")}` : "";
}
🤖 Prompt for AI Agents
In `@packages/better-auth/src/adapter.ts` around lines 131 - 158, The
buildQueryString function must percent-encode OData parameter values: when
adding $filter and $orderby use encodeURIComponent(params.filter) and
encodeURIComponent(params.orderBy) respectively, and for $select map over
params.select and encode each identifier with encodeURIComponent before joining
with commas (e.g., params.select.map(encodeURIComponent).join(",")) so special
characters (spaces, &, =, { } [ ] ", etc.) are safely encoded; update the
buildQueryString function to apply these encodings for params.filter,
params.orderBy, and params.select.

Comment on lines 55 to 91
// Extract Database from the adapter
const configDb = (adapter as unknown as { database: Database }).database;
let db: Database = configDb;

// If CLI credential overrides are provided, construct a new connection
if (options.username && options.password) {
// biome-ignore lint/suspicious/noExplicitAny: accessing internal getter
const dbName: string = (configDb as any)._getDatabaseName;
if (!dbName) {
logger.error("Could not determine database filename from adapter config.");
process.exit(1);
}

// Build the server URL from the existing db's context
// biome-ignore lint/suspicious/noExplicitAny: accessing internal method
const baseUrl: string | undefined = (configDb as any).context?._getBaseUrl?.();

if (!baseUrl) {
logger.error(
"Could not determine server URL from adapter config. Ensure your auth.ts uses FMServerConnection.",
);
process.exit(1);
}

// Extract server origin from base URL (e.g. "https://myserver.com/fmi/odata/v4" -> "https://myserver.com")
const serverUrl = new URL(baseUrl).origin;

const connection = new FMServerConnection({
serverUrl,
auth: {
username: options.username,
password: options.password,
},
});

db = connection.database(dbName);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the file in question for context
head -100 packages/better-auth/src/cli/index.ts | tail -50

Repository: proofgeist/proofkit

Length of output: 1796


🏁 Script executed:

# Search for references to _getDatabaseName and _getBaseUrl in the codebase
rg '_getDatabaseName|_getBaseUrl' -A 2 -B 2

Repository: proofgeist/proofkit

Length of output: 4001


🏁 Script executed:

# Check the fmodata package to see if these are documented as internal or stable
fd 'package.json' | xargs grep -l '@proofkit/fmodata' 2>/dev/null

Repository: proofgeist/proofkit

Length of output: 156


🏁 Script executed:

# Look at the imports and type definitions in the file
head -30 packages/better-auth/src/cli/index.ts

Repository: proofgeist/proofkit

Length of output: 1248


Replace as any with typed unknown to align with TypeScript guidelines.

Lines 62 and 70 access internal APIs from fmodata using as any, which violates the guideline to prefer unknown over any. While these APIs are intentionally exposed as internal (marked with @internal in fmodata), the type assertion should follow the pattern already used in line 56: as unknown as TypeShape.

Consider:

const dbName: string = (configDb as unknown as { _getDatabaseName: string })._getDatabaseName;
const baseUrl: string | undefined = (configDb as unknown as { context?: { _getBaseUrl?(): string } }).context?._getBaseUrl?.();

This maintains access to the documented internal APIs while adhering to the TypeScript typing guidelines.

🤖 Prompt for AI Agents
In `@packages/better-auth/src/cli/index.ts` around lines 55 - 91, Replace the
unsafe "as any" casts when accessing internal fmodata properties on configDb by
using "as unknown as { ... }" shaped types: cast configDb to a shape that
exposes _getDatabaseName to read dbName (referenced as dbName) and cast configDb
to a shape with optional context and _getBaseUrl() to read baseUrl; keep the
rest of the logic (building serverUrl, creating new FMServerConnection and
calling connection.database(dbName)) unchanged so you still create a connection
and assign db when options.username and options.password are provided.

Comment on lines 18 to 27
export async function getMetadata(db: Database): Promise<Metadata | null> {
console.log("getting metadata...");
const result = await fetch("/$metadata", {
method: "GET",
headers: { accept: "application/json" },
output: z
.looseObject({
$Version: z.string(),
"@ServerVersion": z.string(),
})
.or(z.null())
.catch(null),
});

if (result.error) {
console.error("Failed to get metadata:", result.error);
try {
const metadata = await db.getMetadata({ format: "json" });
return metadata;
} catch {
console.error("Failed to get metadata");
return null;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Log the actual error for better debugging.

The catch block discards the error details, which makes debugging connection or metadata issues difficult.

🔧 Proposed fix to include error details
 export async function getMetadata(db: Database): Promise<Metadata | null> {
   console.log("getting metadata...");
   try {
     const metadata = await db.getMetadata({ format: "json" });
     return metadata;
-  } catch {
-    console.error("Failed to get metadata");
+  } catch (error) {
+    console.error("Failed to get metadata:", error);
     return null;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getMetadata(db: Database): Promise<Metadata | null> {
console.log("getting metadata...");
const result = await fetch("/$metadata", {
method: "GET",
headers: { accept: "application/json" },
output: z
.looseObject({
$Version: z.string(),
"@ServerVersion": z.string(),
})
.or(z.null())
.catch(null),
});
if (result.error) {
console.error("Failed to get metadata:", result.error);
try {
const metadata = await db.getMetadata({ format: "json" });
return metadata;
} catch {
console.error("Failed to get metadata");
return null;
}
}
export async function getMetadata(db: Database): Promise<Metadata | null> {
console.log("getting metadata...");
try {
const metadata = await db.getMetadata({ format: "json" });
return metadata;
} catch (error) {
console.error("Failed to get metadata:", error);
return null;
}
}
🤖 Prompt for AI Agents
In `@packages/better-auth/src/migrate.ts` around lines 18 - 27, The catch block in
getMetadata discards the thrown error; change the catch to capture the error
(e.g., catch (err)) and include the error details in the log so failures from
db.getMetadata are visible—update the console.error call in getMetadata to log a
descriptive message plus the error (and optionally the db or format context) to
aid debugging.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 3, 2026

Open in StackBlitz

@proofkit/better-auth

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/better-auth@112

@proofkit/cli

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/cli@112

create-proofkit

pnpm add https://pkg.pr.new/proofgeist/proofkit/create-proofkit@112

@proofkit/fmdapi

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/fmdapi@112

@proofkit/fmodata

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/fmodata@112

@proofkit/typegen

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/typegen@112

@proofkit/webviewer

pnpm add https://pkg.pr.new/proofgeist/proofkit/@proofkit/webviewer@112

commit: db61527

…rors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

… in CLI

- Updated the CLI to extract the Database instance from both the adapter factory and the resolved adapter.
- Improved error logging for metadata retrieval and migration operations, providing clearer feedback on failures.
- Added a new `formatError` function to standardize error messages and enhance user experience during migrations.
- Modified the `prettyPrintMigrationPlan` function to include target server URL and file name for better context during migrations.
@eluce2 eluce2 mentioned this pull request Feb 3, 2026
Copy link
Collaborator Author

eluce2 commented Feb 3, 2026

Merge activity

  • Feb 3, 10:31 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 3, 10:31 PM UTC: @eluce2 merged this pull request with Graphite.

@eluce2 eluce2 merged commit 69fd3fb into main Feb 3, 2026
11 of 13 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.

2 participants