Skip to content

Commit f275705

Browse files
authored
Merge pull request #14 from udecode/package
2 parents e685cfe + 2800e68 commit f275705

356 files changed

Lines changed: 44236 additions & 23348 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changesets
2+
3+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4+
with multi-package repos, or single-package repos to help you version and publish your code. You can
5+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6+
7+
We have a quick list of common questions to get you started engaging with this project in
8+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

.changeset/config.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
3+
"changelog": [
4+
"@changesets/changelog-github",
5+
{ "repo": "udecode/better-convex" }
6+
],
7+
"commit": false,
8+
"fixed": [],
9+
"linked": [],
10+
"access": "public",
11+
"baseBranch": "main",
12+
"updateInternalDependencies": "patch",
13+
"ignore": ["example"]
14+
}

.changeset/first-release.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"better-convex": minor
3+
---
4+
5+
Initial release

.claude/AGENTS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
- In all interactions and commit messages, be extremely concise and sacrifice grammar for the sake of concision.
1+
- In all interactions and commit messages, be extremely concise and sacrifice grammar for the sake of concision
2+
- ALWAYS read and understand relevant files before proposing edits. Do not speculate about code you have not inspected
3+
- ALWAYS use AskUserQuestion tool when asking questions to the user
4+
- When playwriter browser requires start dev, auth or disconnects, use AskUserQuestion: (1) Connected (2) Skip browser test. Never close browser when done.
25

36
## PR Comments
47

.claude/docs/ERRORS.md

Whitespace-only changes.

.claude/docs/plan.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Better-Convex Documentation Test
2+
3+
## Objective
4+
5+
Test the better-convex documentation by building a working Next.js 15 app from scratch. Simulate a new developer with no prior better-convex knowledge.
6+
7+
## Rules
8+
9+
### Knowledge Boundaries
10+
11+
1. **Only use `better-convex/content/docs/`** as reference
12+
2. **Convex native knowledge allowed** (official Convex patterns)
13+
3. **When stuck, document the gap** - don't guess
14+
4. Do not read anything outside `better-convex` folder.
15+
16+
### Error Tracking
17+
18+
Create `ERRORS.md` and log every issue:
19+
20+
```md
21+
### Error N: [Title]
22+
23+
- **Location**: Doc page, step
24+
- **Expected**: What docs said
25+
- **Actual**: What happened
26+
- **Resolution**: Workaround used
27+
- **Doc Fix**: Suggested improvement
28+
```
29+
30+
Log: missing steps, wrong code, missing imports, type errors, runtime errors, missing deps, broken links
31+
32+
### Process
33+
34+
1. Read docs first, then act
35+
2. Follow docs literally - copy code exactly
36+
3. Log confusion before proceeding
37+
4. No silent fixes - log every doc gap
38+
39+
## Test Scope
40+
41+
Build a complete app following the docs. Exclude auth (separate test).
42+
43+
## Combination Strategy
44+
45+
Test one combo thoroughly first, then expand. Based on index.mdx "For AI Agents" options.
46+
47+
### Combo 1 (Current): Default Stack
48+
49+
| Choice | Value |
50+
|--------|-------|
51+
| Approach | Top-down (Templates) |
52+
| Framework | Next.js App Router |
53+
| Database | ctx.table (Ents) |
54+
| Auth | None (excluded) |
55+
| SSR/RSC | Yes |
56+
| Triggers | Yes |
57+
58+
### Future Combos (after Combo 1 passes)
59+
60+
| # | Approach | Framework | DB | Auth | Notes |
61+
|---|----------|-----------|-----|------|-------|
62+
| 2 | Top-down | Next.js | ctx.db | None | Vanilla DB |
63+
| 3 | Bottom-up | Vite | ctx.table | None | Non-Next.js |
64+
| 4 | Bottom-up | Next.js | ctx.table | Better Auth | Auth test |
65+
| 5 | Top-down | Vite | ctx.db | None | Minimal stack |
66+
67+
## Verification
68+
69+
- App runs
70+
- Features work
71+
- Real-time updates
72+
73+
## Success Criteria
74+
75+
### Combo 1
76+
- [x] TypeScript passes
77+
- [ ] App runs without errors
78+
- [ ] Real-time updates work
79+
- [ ] ERRORS.md documents all gaps
80+
81+
### Overall
82+
- [ ] All combos tested
83+
- [ ] Doc fixes applied
84+
85+
## Output
86+
87+
1. `ERRORS.md` - Issues found
88+
2. Summary - Doc quality assessment
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
title: Authorization
3+
description: Authorization with Better Auth
4+
---
5+
6+
### Showing UI based on authentication state
7+
8+
You can control which UI is shown when the user is signed in or signed out using
9+
Convex's `<Authenticated>`, `<Unauthenticated>` and `<AuthLoading>` helper
10+
components. These components are powered by Convex's `useConvexAuth()` hook,
11+
which provides `isAuthenticated` and `isLoading` flags. This hook can be used
12+
directly if preferred.
13+
14+
It's important to use Convex's authentication state components or the
15+
`useConvexAuth()` hook instead of Better Auth's `getSession()` or `useSession()`
16+
when you need to check whether the user is logged in or not. Better Auth will
17+
reflect an authenticated user before Convex does, as the Convex client must
18+
subsequently validate the token provided by Better Auth. Convex functions that
19+
require authentication can throw if called before Convex has validated the
20+
token.
21+
22+
In the following example, the `<Content />` component is a child of
23+
`<Authenticated>`, so its content and any of its child components are guaranteed
24+
to have an authenticated user, and Convex queries can require authentication.
25+
26+
```tsx title="src/App.tsx"
27+
import {
28+
Authenticated,
29+
Unauthenticated,
30+
AuthLoading,
31+
useQuery,
32+
} from "convex/react";
33+
import { api } from "../convex/_generated/api";
34+
35+
function App() {
36+
return (
37+
<main>
38+
<Unauthenticated>Logged out</Unauthenticated>
39+
<Authenticated>Logged in</Authenticated>
40+
<AuthLoading>Loading...</AuthLoading>
41+
</main>
42+
);
43+
}
44+
45+
const Content = () => {
46+
const messages = useQuery(api.messages.getForCurrentUser);
47+
return <div>Authenticated content: {messages?.length}</div>;
48+
};
49+
50+
export default App;
51+
```
52+
53+
### Authentication state in Convex functions
54+
55+
If the client is authenticated, you can access the information stored in the JWT
56+
via `ctx.auth.getUserIdentity`.
57+
58+
If the client is **not** authenticated, `ctx.auth.getUserIdentity` will return
59+
null.
60+
61+
Make sure that the component calling this query is a child of `<Authenticated>`
62+
from `convex/react`, or that `isAuthenticated` from `useConvexAuth()` is `true`.
63+
Otherwise, it will throw on page load.
64+
65+
```ts title="convex/messages.ts"
66+
import { query } from "./_generated/server";
67+
68+
// You can get the current user from the auth component with session validation.
69+
export const getCurrentUser = query({
70+
args: {},
71+
handler: async (ctx) => {
72+
return await authComponent.getAuthUser(ctx);
73+
},
74+
});
75+
76+
// You can also just get the authenticated user id as you
77+
// normally would from ctx.auth.getUserIdentity. Note that
78+
// this does not validate the session.
79+
export const getForCurrentUser = query({
80+
args: {},
81+
handler: async (ctx) => {
82+
const identity = await ctx.auth.getUserIdentity();
83+
if (identity === null) {
84+
throw new Error("Not authenticated");
85+
}
86+
return await ctx.db
87+
.query("messages")
88+
.filter((q) => q.eq(q.field("author"), identity.email))
89+
.collect();
90+
},
91+
});
92+
```
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
title: Basic Usage
3+
description: Using Better Auth with Convex
4+
---
5+
6+
## Better Auth guide
7+
8+
Better Auth's [basic usage guide](https://www.better-auth.com/docs/basic-usage)
9+
applies to Convex as well. It covers signing in and out, social providers,
10+
plugins, and more. You will be using Better Auth directly in your project, so
11+
their guides are a primary reference.
12+
13+
### Exceptions
14+
15+
There are a few areas in the Better Auth basic usage guide that work differently
16+
in Convex.
17+
18+
- #### Server side authentication
19+
20+
Better Auth supports signing users in and out through server side functions.
21+
Because Convex functions run over websockets and don't return HTTP responses
22+
or set cookies, signing up/in/out must be done from the client via
23+
`authClient.signIn.*` methods.
24+
25+
- #### Schemas and migrations
26+
27+
The basic usage guide includes information on database schema generation and
28+
migrations via the Better Auth CLI. This only applies for
29+
[local installs](/local-install), which support generating schemas. For
30+
projects not using local install, the default schema provided with the Better
31+
Auth component (preconfigured with the
32+
[supported plugins](/supported-plugins)) is used, and cannot be altered.
33+
34+
## Using server methods with `auth.api`
35+
36+
Better Auth's server side `auth.api` methods can be used with your `createAuth`
37+
function and the component `headers` method. Here's an example implementing the
38+
[`changePassword` server method](https://www.better-auth.com/docs/concepts/users-accounts#api-method-change-password).
39+
40+
```ts
41+
export const updateUserPassword = mutation({
42+
args: {
43+
currentPassword: v.string(),
44+
newPassword: v.string(),
45+
},
46+
handler: async (ctx, args) => {
47+
// Many Better Auth server methods require a currently authenticated
48+
// user, so request headers have to be passed in so session cookies
49+
// can be parsed and validated. The `getAuth` method provides both the
50+
// auth object and headers for convenience.
51+
const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
52+
await auth.api.changePassword({
53+
body: {
54+
currentPassword: args.currentPassword,
55+
newPassword: args.newPassword,
56+
},
57+
headers,
58+
});
59+
},
60+
});
61+
```
62+
63+
## Using Convex ctx in Better Auth config
64+
65+
The `ctx` param passed in to the `createAuth` function is the Convex context
66+
object. This can be used to access the Convex database or Convex functions in
67+
your Better Auth config. It can be a
68+
[query](https://docs.convex.dev/functions/query-functions#query-context),
69+
[mutation](https://docs.convex.dev/functions/mutation-functions#mutation-context),
70+
or [action](https://docs.convex.dev/functions/actions#action-context) context.
71+
72+
A common use case is sending emails for verification or password resets with the
73+
[Resend component](https://www.convex.dev/components/resend). `resend.sendEmail`
74+
will produce a type error because the ctx object could be a query ctx. The
75+
component provides type guards for this.
76+
77+
```ts
78+
import { requireActionCtx } from "@convex-dev/better-auth/utils";
79+
import { type GenericCtx } from "@convex-dev/better-auth";
80+
import { Resend } from "@convex-dev/resend";
81+
import { components } from "./_generated/api";
82+
import { type DataModel } from "./_generated/dataModel";
83+
84+
export const resend = new Resend(components.resend);
85+
86+
export const createAuthOptions = (ctx: GenericCtx<DataModel>) => ({
87+
baseURL: siteUrl,
88+
sendVerificationEmail: async ({ user, url }) => {
89+
// This function only requires a `runMutation` property on the ctx object,
90+
// but we'll make sure we have an action ctx because we know a network
91+
// request is being made, which requires an action ctx.
92+
await resend.sendEmail(requireActionCtx(ctx), {
93+
to: user.email,
94+
subject: "Verify your email",
95+
html: `<p>Click <a href="${url}">here</a> to verify your email</p>`,
96+
});
97+
},
98+
});
99+
```

0 commit comments

Comments
 (0)