Skip to content

feat: add createTypedAdapter for model-inferred doc types - #401

Open
FatahChan wants to merge 11 commits into
get-convex:mainfrom
FatahChan:feat/typed-adapter-wrapper
Open

feat: add createTypedAdapter for model-inferred doc types#401
FatahChan wants to merge 11 commits into
get-convex:mainfrom
FatahChan:feat/typed-adapter-wrapper

Conversation

@FatahChan

@FatahChan FatahChan commented Jul 5, 2026

Copy link
Copy Markdown

Summary

  • Adds createTypedAdapter(schema, adapterRefs) — typed wrappers around the Better Auth adapter API (findOne, findMany, create, updateOne, updateMany, deleteOne, deleteMany)
  • Exposes authComponent.typedAdapter on createClient as the recommended entry point for app code (uses local schema when configured)
  • Infers Doc<table> from the model literal at the call site (e.g. model: "user"Doc<"user"> | null)
  • Model-aware query options (with concrete schema + local install):
    • AdapterWhere<S, T>field and value narrow with the table
    • select on findOne / findMany — return type becomes Pick<Doc<table>, …>
    • SortBy<S, T> on findManyfield must be a document key
  • Falls back to validator-shaped types when the schema type param is still generic (e.g. inside createClient)
  • Exports TypedAdapter, AdapterFunctions, AdapterWhere, SortBy, and BatchResult
  • Adds optional @convex-dev/better-auth/typed-adapter subpath export (main entry also exports everything)
  • Documents usage in local-install, component-client, and type-utilities guides
  • Adds vitest type tests and includes *.types.test.ts in the typecheck glob

Problem

createApi uses queryGeneric / mutationGeneric, so Convex codegen types adapter returns as any. Even with discriminated-union args, FunctionReturnType is fixed on the reference and does not narrow from call-site model.

Calling ctx.runQuery(components.betterAuth.adapter.findOne, …) directly has the same limitation — where, select, and sortBy accept any field: string with no value or return narrowing.

Usage

Recommended — via createClient (local install with schema):

export const authComponent = createClient<DataModel, typeof authSchema>(
  components.betterAuth,
  { local: { schema: authSchema } }
);

const user = await authComponent.typedAdapter.findOne(ctx, {
  model: "user",
  where: [{ field: "email", value: email }],
  select: ["email", "name"],
});
// Pick<Doc<"user">, "email" | "name"> | null

const page = await authComponent.typedAdapter.findMany(ctx, {
  model: "user",
  sortBy: { field: "email", direction: "asc" },
  paginationOpts: { cursor: null, numItems: 20 },
});
// page.page: Array<Doc<"user">>

Lower-levelcreateTypedAdapter directly:

import { createTypedAdapter } from "@convex-dev/better-auth";
// or: import { createTypedAdapter } from "@convex-dev/better-auth/typed-adapter";

export const adapter = createTypedAdapter(schema, components.betterAuth.adapter);

The cast lives inside the library at the Convex runQuery/runMutation boundary.

Internal changes

  • createClient uses typedAdapter internally for safeGetAuthUser, getHeaders, and getAnyUserById (removes manual casts)

Test plan

  • npm test (70 tests, vitest --typecheck)
  • npm run build
  • Type tests cover doc inference, model-aware where, select, and sortBy

Ahmad Fathallah and others added 2 commits July 5, 2026 23:10
Convex function references use a fixed return type, so adapter findOne/create
results stay `any` even when model is a literal. createTypedAdapter wraps the
adapter refs and infers Doc<table> from the model argument at the call site.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stub adapter fns with real FunctionReference values so tsc accepts
the test file, and align model literals with the current schema.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

@FatahChan is attempting to deploy a commit to the Convex Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds createTypedAdapter, a schema-aware wrapper around Better Auth adapter function references. It introduces exported adapter types, derives document and payload types from the provided Convex schema, and exposes typed adapter methods. createClient now builds and returns a typedAdapter, internal auth lookups use it, the new entry point is exported from the package, and docs and type tests were added for the new API.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant createClient
  participant createTypedAdapter
  participant QueryRunner
  participant MutationRunner

  Caller->>createClient: createClient(config)
  createClient->>createTypedAdapter: build typedAdapter(schema, adapter fns)
  Caller->>createClient: getAuthUser / getHeaders / getAnyUserById
  createClient->>createTypedAdapter: findOne(model, where)
  createTypedAdapter->>QueryRunner: runQuery(...)
  QueryRunner-->>createTypedAdapter: typed document or null
  createTypedAdapter-->>createClient: typed result
  createClient-->>Caller: auth data / headers / user

  Caller->>createTypedAdapter: create(model, data)
  createTypedAdapter->>MutationRunner: runMutation(...)
  MutationRunner-->>createTypedAdapter: created document
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding createTypedAdapter with model-inferred document types.
Description check ✅ Passed The description is directly related to the code changes and accurately summarizes the new typed adapter, exports, docs, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/create-typed-adapter.ts`:
- Around line 58-67: The helper types SortBy and BatchResult are part of the
public API shape used by findMany, updateMany, and deleteMany, but they are
currently internal-only. Export these type aliases from create-typed-adapter.ts
so consumers can import and reuse them directly, and make sure any related
public signatures still reference the exported symbols consistently across the
typed adapter methods.

In `@src/client/create-typed-adapter.types.test.ts`:
- Around line 34-38: The type test is referencing models that are not defined by
the imported schema, so the `member` and `invitation` checks in
`create-typed-adapter.types.test` must be moved to the plugin-table schema test
setup or removed. Update the assertions to live alongside
`src/component/testProfiles/schema.profile-plugin-table.ts` (or switch the test
to that schema) so `findOne` is validated against the schema that actually
declares `member` and `invitation`.
- Around line 1-63: The type-only Vitest checks in createTypedAdapter are
currently in a filename that the default typecheck glob will not pick up. Rename
this test to match Vitest’s typecheck pattern used by `vitest run --typecheck`,
or add a custom `typecheck.include` so `createTypedAdapter`, `expectTypeOf`, and
the `findOne`/`create`/`findMany` assertions are actually type-checked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c9320782-7c62-4e4b-be58-e0bfd6408e9c

📥 Commits

Reviewing files that changed from the base of the PR and between c628916 and 17e9e64.

📒 Files selected for processing (3)
  • src/client/create-typed-adapter.ts
  • src/client/create-typed-adapter.types.test.ts
  • src/client/index.ts

Comment thread src/client/create-typed-adapter.ts Outdated
Comment thread src/client/create-typed-adapter.types.test.ts
Comment thread src/client/create-typed-adapter.types.test.ts Outdated
FatahChan and others added 2 commits July 5, 2026 23:17
…syntax

Changed type annotations for 'where' and 'select' parameters in createTypedAdapter to use Array<T> syntax for consistency and clarity.
Export SortBy and BatchResult for consumers, include *.types.test.ts
in Vitest typecheck, and allow generated Convex files in test tsconfig.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tsconfig.test.json (1)

10-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Broad _generated inclusion.

Removing **/_generated entirely (rather than narrowing it, e.g. !src/component/_generated) makes the test tsconfig pull in all generated Convex artifacts under src/**, example/src/**, and example/convex/**. This is needed for the new type test's _generated/dataModel import, but broadens the typecheck surface to any other generated code in those trees.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tsconfig.test.json` at line 10, The test tsconfig is now pulling in too many
generated Convex artifacts, so narrow the `tsconfig.test.json` exclude/include
rules instead of broadly removing `**/_generated`; keep the new
`_generated/dataModel` import working, but scope the exception to the specific
test fixture or generated path rather than all `src/**`, `example/src/**`, and
`example/convex/**`. Update the config around `exclude` so only the needed
generated files are typechecked while unrelated generated code stays out of the
test surface.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tsconfig.test.json`:
- Line 10: The test tsconfig is now pulling in too many generated Convex
artifacts, so narrow the `tsconfig.test.json` exclude/include rules instead of
broadly removing `**/_generated`; keep the new `_generated/dataModel` import
working, but scope the exception to the specific test fixture or generated path
rather than all `src/**`, `example/src/**`, and `example/convex/**`. Update the
config around `exclude` so only the needed generated files are typechecked while
unrelated generated code stays out of the test surface.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 88db16f6-b537-4c22-bb59-abf701a2034d

📥 Commits

Reviewing files that changed from the base of the PR and between 5b87f72 and 563eef2.

📒 Files selected for processing (4)
  • src/client/create-typed-adapter.ts
  • src/client/index.ts
  • tsconfig.test.json
  • vitest.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/client/index.ts
  • src/client/create-typed-adapter.ts

FatahChan and others added 4 commits July 5, 2026 23:32
Wire createTypedAdapter into the component client and local-install API
so adapter calls infer doc types from model, and document the pattern.

Co-authored-by: Cursor <cursoragent@cursor.com>
Remove the unused typed export from createApi and add a full local-install
example for authComponent.typedAdapter as the single typed entry point.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep generated files out of the test project root while still allowing
type tests to import component _generated types via composite: false.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/create-client.ts`:
- Around line 17-21: The import in create-client.ts mixes value and inline type
specifiers, which triggers the consistent-type-specifier-style rule. Update the
createTypedAdapter import so the type-only names AdapterFunctions and
TypedAdapter are moved into a top-level type-only import, while keeping the
runtime createTypedAdapter import separate or otherwise using a consistent
top-level type import style. Use the existing import block at the top of the
file to make the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 80754f43-225a-4b56-893b-3eb2caeb6222

📥 Commits

Reviewing files that changed from the base of the PR and between 563eef2 and cad1465.

📒 Files selected for processing (6)
  • docs/content/docs/api/component-client.mdx
  • docs/content/docs/api/type-utilities.mdx
  • docs/content/docs/features/local-install.mdx
  • package.json
  • src/client/create-client.ts
  • tsconfig.test.json
✅ Files skipped from review due to trivial changes (3)
  • docs/content/docs/api/component-client.mdx
  • docs/content/docs/features/local-install.mdx
  • docs/content/docs/api/type-utilities.mdx

Comment thread src/client/create-client.ts Outdated
FatahChan and others added 3 commits July 6, 2026 10:38
Narrow where-clause field and value types from the model argument, with a
fallback to the validator shape when the schema type is still generic.

Co-authored-by: Cursor <cursoragent@cursor.com>
Narrow findOne/findMany returns from select, validate sortBy.field per model,
and document where/select/sortBy narrowing in guides.

Co-authored-by: Cursor <cursoragent@cursor.com>
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