Skip to content
Merged

init #14

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
14 changes: 14 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "udecode/better-convex" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["example"]
}
5 changes: 5 additions & 0 deletions .changeset/first-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"better-convex": minor
---

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

## PR Comments

Expand Down
Empty file added .claude/docs/ERRORS.md
Empty file.
88 changes: 88 additions & 0 deletions .claude/docs/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Better-Convex Documentation Test

## Objective

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.

## Rules

### Knowledge Boundaries

1. **Only use `better-convex/content/docs/`** as reference
2. **Convex native knowledge allowed** (official Convex patterns)
3. **When stuck, document the gap** - don't guess
4. Do not read anything outside `better-convex` folder.

### Error Tracking

Create `ERRORS.md` and log every issue:

```md
### Error N: [Title]

- **Location**: Doc page, step
- **Expected**: What docs said
- **Actual**: What happened
- **Resolution**: Workaround used
- **Doc Fix**: Suggested improvement
```

Log: missing steps, wrong code, missing imports, type errors, runtime errors, missing deps, broken links

### Process

1. Read docs first, then act
2. Follow docs literally - copy code exactly
3. Log confusion before proceeding
4. No silent fixes - log every doc gap

## Test Scope

Build a complete app following the docs. Exclude auth (separate test).

## Combination Strategy

Test one combo thoroughly first, then expand. Based on index.mdx "For AI Agents" options.

### Combo 1 (Current): Default Stack

| Choice | Value |
|--------|-------|
| Approach | Top-down (Templates) |
| Framework | Next.js App Router |
| Database | ctx.table (Ents) |
| Auth | None (excluded) |
| SSR/RSC | Yes |
| Triggers | Yes |

### Future Combos (after Combo 1 passes)

| # | Approach | Framework | DB | Auth | Notes |
|---|----------|-----------|-----|------|-------|
| 2 | Top-down | Next.js | ctx.db | None | Vanilla DB |
| 3 | Bottom-up | Vite | ctx.table | None | Non-Next.js |
| 4 | Bottom-up | Next.js | ctx.table | Better Auth | Auth test |
| 5 | Top-down | Vite | ctx.db | None | Minimal stack |

## Verification

- App runs
- Features work
- Real-time updates

## Success Criteria

### Combo 1
- [x] TypeScript passes
- [ ] App runs without errors
- [ ] Real-time updates work
- [ ] ERRORS.md documents all gaps

### Overall
- [ ] All combos tested
- [ ] Doc fixes applied

## Output

1. `ERRORS.md` - Issues found
2. Summary - Doc quality assessment
92 changes: 92 additions & 0 deletions .claude/docs/references/better-auth/authorization.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: Authorization
description: Authorization with Better Auth
---

### Showing UI based on authentication state

You can control which UI is shown when the user is signed in or signed out using
Convex's `<Authenticated>`, `<Unauthenticated>` and `<AuthLoading>` helper
components. These components are powered by Convex's `useConvexAuth()` hook,
which provides `isAuthenticated` and `isLoading` flags. This hook can be used
directly if preferred.

It's important to use Convex's authentication state components or the
`useConvexAuth()` hook instead of Better Auth's `getSession()` or `useSession()`
when you need to check whether the user is logged in or not. Better Auth will
reflect an authenticated user before Convex does, as the Convex client must
subsequently validate the token provided by Better Auth. Convex functions that
require authentication can throw if called before Convex has validated the
token.

In the following example, the `<Content />` component is a child of
`<Authenticated>`, so its content and any of its child components are guaranteed
to have an authenticated user, and Convex queries can require authentication.

```tsx title="src/App.tsx"
import {
Authenticated,
Unauthenticated,
AuthLoading,
useQuery,
} from "convex/react";
import { api } from "../convex/_generated/api";

function App() {
return (
<main>
<Unauthenticated>Logged out</Unauthenticated>
<Authenticated>Logged in</Authenticated>
<AuthLoading>Loading...</AuthLoading>
</main>
);
}

const Content = () => {
const messages = useQuery(api.messages.getForCurrentUser);
return <div>Authenticated content: {messages?.length}</div>;
};

export default App;
```

### Authentication state in Convex functions

If the client is authenticated, you can access the information stored in the JWT
via `ctx.auth.getUserIdentity`.

If the client is **not** authenticated, `ctx.auth.getUserIdentity` will return
null.

Make sure that the component calling this query is a child of `<Authenticated>`
from `convex/react`, or that `isAuthenticated` from `useConvexAuth()` is `true`.
Otherwise, it will throw on page load.

```ts title="convex/messages.ts"
import { query } from "./_generated/server";

// You can get the current user from the auth component with session validation.
export const getCurrentUser = query({
args: {},
handler: async (ctx) => {
return await authComponent.getAuthUser(ctx);
},
});

// You can also just get the authenticated user id as you
// normally would from ctx.auth.getUserIdentity. Note that
// this does not validate the session.
export const getForCurrentUser = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Not authenticated");
}
return await ctx.db
.query("messages")
.filter((q) => q.eq(q.field("author"), identity.email))
.collect();
},
});
```
99 changes: 99 additions & 0 deletions .claude/docs/references/better-auth/basic-usage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Basic Usage
description: Using Better Auth with Convex
---

## Better Auth guide

Better Auth's [basic usage guide](https://www.better-auth.com/docs/basic-usage)
applies to Convex as well. It covers signing in and out, social providers,
plugins, and more. You will be using Better Auth directly in your project, so
their guides are a primary reference.

### Exceptions

There are a few areas in the Better Auth basic usage guide that work differently
in Convex.

- #### Server side authentication

Better Auth supports signing users in and out through server side functions.
Because Convex functions run over websockets and don't return HTTP responses
or set cookies, signing up/in/out must be done from the client via
`authClient.signIn.*` methods.

- #### Schemas and migrations

The basic usage guide includes information on database schema generation and
migrations via the Better Auth CLI. This only applies for
[local installs](/local-install), which support generating schemas. For
projects not using local install, the default schema provided with the Better
Auth component (preconfigured with the
[supported plugins](/supported-plugins)) is used, and cannot be altered.

## Using server methods with `auth.api`

Better Auth's server side `auth.api` methods can be used with your `createAuth`
function and the component `headers` method. Here's an example implementing the
[`changePassword` server method](https://www.better-auth.com/docs/concepts/users-accounts#api-method-change-password).

```ts
export const updateUserPassword = mutation({
args: {
currentPassword: v.string(),
newPassword: v.string(),
},
handler: async (ctx, args) => {
// Many Better Auth server methods require a currently authenticated
// user, so request headers have to be passed in so session cookies
// can be parsed and validated. The `getAuth` method provides both the
// auth object and headers for convenience.
const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
await auth.api.changePassword({
body: {
currentPassword: args.currentPassword,
newPassword: args.newPassword,
},
headers,
});
},
});
```

## Using Convex ctx in Better Auth config

The `ctx` param passed in to the `createAuth` function is the Convex context
object. This can be used to access the Convex database or Convex functions in
your Better Auth config. It can be a
[query](https://docs.convex.dev/functions/query-functions#query-context),
[mutation](https://docs.convex.dev/functions/mutation-functions#mutation-context),
or [action](https://docs.convex.dev/functions/actions#action-context) context.

A common use case is sending emails for verification or password resets with the
[Resend component](https://www.convex.dev/components/resend). `resend.sendEmail`
will produce a type error because the ctx object could be a query ctx. The
component provides type guards for this.

```ts
import { requireActionCtx } from "@convex-dev/better-auth/utils";
import { type GenericCtx } from "@convex-dev/better-auth";
import { Resend } from "@convex-dev/resend";
import { components } from "./_generated/api";
import { type DataModel } from "./_generated/dataModel";

export const resend = new Resend(components.resend);

export const createAuthOptions = (ctx: GenericCtx<DataModel>) => ({
baseURL: siteUrl,
sendVerificationEmail: async ({ user, url }) => {
// This function only requires a `runMutation` property on the ctx object,
// but we'll make sure we have an action ctx because we know a network
// request is being made, which requires an action ctx.
await resend.sendEmail(requireActionCtx(ctx), {
to: user.email,
subject: "Verify your email",
html: `<p>Click <a href="${url}">here</a> to verify your email</p>`,
});
},
});
```
Loading