-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
docs(solid-start): convex + better-auth example #5796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3f8802b
9b861a5
43f6686
320ad62
2cbe033
60b687f
086e49f
5113692
63d696f
1d2fdda
c799b2c
4fd9338
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Deployment used by `npx convex dev` | ||
| CONVEX_DEPLOYMENT= | ||
|
|
||
| VITE_CONVEX_URL= | ||
|
|
||
| # Same as VITE_CONVEX_URL but ends in .site | ||
| VITE_CONVEX_SITE_URL= | ||
|
|
||
| # Your local site URL | ||
| SITE_URL=http://localhost:3000 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
|
|
||
| .env.local | ||
| /convex/_generated/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| ### Guide | ||
|
|
||
| Rename .env.example to .env.local | ||
|
|
||
| Run | ||
|
|
||
| - `pnpm i` | ||
| - `pnpx convex dev` | ||
| - `pnpx convex env set SITE_URL http://localhost:3000/` | ||
| - `pnpx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)` | ||
| - `pnpx convex dev` - takes up one terminal | ||
|
|
||
| In a separate terminal run | ||
|
|
||
| - `pnpm run dev` |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,90 @@ | ||||||
| # Welcome to your Convex functions directory! | ||||||
|
|
||||||
| Write your Convex functions here. | ||||||
| See https://docs.convex.dev/functions for more. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap the URL in angle brackets for proper Markdown formatting. The bare URL should be wrapped in angle brackets to comply with Markdown best practices. Apply this diff: -See https://docs.convex.dev/functions for more.
+See <https://docs.convex.dev/functions> for more.📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.18.1)4-4: Bare URL used (MD034, no-bare-urls) 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| A query function that takes two arguments looks like: | ||||||
|
|
||||||
| ```ts | ||||||
| // functions.js | ||||||
| import { query } from './_generated/server' | ||||||
| import { v } from 'convex/values' | ||||||
|
|
||||||
| export const myQueryFunction = query({ | ||||||
| // Validators for arguments. | ||||||
| args: { | ||||||
| first: v.number(), | ||||||
| second: v.string(), | ||||||
| }, | ||||||
|
|
||||||
| // Function implementation. | ||||||
| handler: async (ctx, args) => { | ||||||
| // Read the database as many times as you need here. | ||||||
| // See https://docs.convex.dev/database/reading-data. | ||||||
| const documents = await ctx.db.query('tablename').collect() | ||||||
|
|
||||||
| // Arguments passed from the client are properties of the args object. | ||||||
| console.log(args.first, args.second) | ||||||
|
|
||||||
| // Write arbitrary JavaScript here: filter, aggregate, build derived data, | ||||||
| // remove non-public properties, or create new objects. | ||||||
| return documents | ||||||
| }, | ||||||
| }) | ||||||
| ``` | ||||||
|
|
||||||
| Using this query function in a React component looks like: | ||||||
|
|
||||||
| ```ts | ||||||
| const data = useQuery(api.functions.myQueryFunction, { | ||||||
| first: 10, | ||||||
| second: 'hello', | ||||||
| }) | ||||||
| ``` | ||||||
|
|
||||||
| A mutation function looks like: | ||||||
|
|
||||||
| ```ts | ||||||
| // functions.js | ||||||
| import { mutation } from './_generated/server' | ||||||
| import { v } from 'convex/values' | ||||||
|
|
||||||
| export const myMutationFunction = mutation({ | ||||||
| // Validators for arguments. | ||||||
| args: { | ||||||
| first: v.string(), | ||||||
| second: v.string(), | ||||||
| }, | ||||||
|
|
||||||
| // Function implementation. | ||||||
| handler: async (ctx, args) => { | ||||||
| // Insert or modify documents in the database here. | ||||||
| // Mutations can also read from the database like queries. | ||||||
| // See https://docs.convex.dev/database/writing-data. | ||||||
| const message = { body: args.first, author: args.second } | ||||||
| const id = await ctx.db.insert('messages', message) | ||||||
|
|
||||||
| // Optionally, return a value from your mutation. | ||||||
| return await ctx.db.get(id) | ||||||
| }, | ||||||
| }) | ||||||
| ``` | ||||||
|
|
||||||
| Using this mutation function in a React component looks like: | ||||||
|
|
||||||
| ```ts | ||||||
| const mutation = useMutation(api.functions.myMutationFunction) | ||||||
| function handleButtonPress() { | ||||||
| // fire and forget, the most common way to use mutations | ||||||
| mutation({ first: 'Hello!', second: 'me' }) | ||||||
| // OR | ||||||
| // use the result once the mutation has completed | ||||||
| mutation({ first: 'Hello!', second: 'me' }).then((result) => | ||||||
| console.log(result), | ||||||
| ) | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Use the Convex CLI to push your functions to a deployment. See everything | ||||||
| the Convex CLI can do by running `npx convex -h` in your project root | ||||||
| directory. To learn more, launch the docs with `npx convex docs`. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export default { | ||
| providers: [ | ||
| { | ||
| domain: process.env.CONVEX_SITE_URL, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainVerify environment variable and handle undefined domain. The Verify the correct environment variable name and consider adding validation: 🏁 Script executed: #!/bin/bash
# Check if CONVEX_SITE_URL or SITE_URL is referenced in Convex auth setup
rg -n 'CONVEX_SITE_URL|SITE_URL' --type ts examples/solid/start-convex-better-auth/convex/Length of output: 201 Length of output: 0 🏁 Script executed: #!/bin/bash
# View auth.config.ts with context around line 4
cat examples/solid/start-convex-better-auth/convex/auth.config.tsLength of output: 125 🏁 Script executed: #!/bin/bash
# View auth.ts with context around line 9
cat examples/solid/start-convex-better-auth/convex/auth.tsLength of output: 1516 🏁 Script executed: #!/bin/bash
# Check if there are any .env files in the root or example directory
fd '\.env' examples/solid/start-convex-better-auth/ -type fLength of output: 170 🏁 Script executed: #!/bin/bash
# Search for CONVEX_SITE_URL or SITE_URL in entire example directory
rg -n 'CONVEX_SITE_URL|SITE_URL' examples/solid/start-convex-better-auth/Length of output: 201 🏁 Script executed: #!/bin/bash
# Search for .env.example at multiple levels
find examples/solid/start-convex-better-auth -name '.env*' -o -name '.env.example' 2>/dev/null | head -20Length of output: 52 🏁 Script executed: #!/bin/bash
# Check for README or setup files that document environment variables
fd 'README|setup|ENV|\.env' examples/solid/start-convex-better-auth/ --max-depth 2Length of output: 56 🏁 Script executed: #!/bin/bash
# Search entire repository for .env.example to understand naming pattern
fd -H '\.env\.example' . --max-depth 4Length of output: 208 🏁 Script executed: #!/bin/bash
# Check all references to SITE_URL and CONVEX_SITE_URL across entire examples/solid directory
rg 'SITE_URL|CONVEX_SITE_URL' examples/solid/ -nLength of output: 201 🏁 Script executed: #!/bin/bash
# View .env.example to see which variables are defined
cat examples/solid/start-convex-better-auth/.env.exampleLength of output: 195 🏁 Script executed: #!/bin/bash
# View convex README for documentation on env vars
cat examples/solid/start-convex-better-auth/convex/README.mdLength of output: 2485 Fix environment variable mismatch in auth.config.ts. The 🤖 Prompt for AI Agents |
||
| applicationID: 'convex', | ||
| }, | ||
| ], | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { createClient } from '@convex-dev/better-auth' | ||
| import { convex } from '@convex-dev/better-auth/plugins' | ||
| import { betterAuth } from 'better-auth' | ||
| import { components } from './_generated/api' | ||
| import { query } from './_generated/server' | ||
| import type { GenericCtx } from '@convex-dev/better-auth' | ||
| import type { DataModel } from './_generated/dataModel' | ||
|
|
||
| const siteUrl = process.env.SITE_URL! | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate the required environment variable. Using a non-null assertion on Apply this diff to add validation: -const siteUrl = process.env.SITE_URL!
+const siteUrl = process.env.SITE_URL
+if (!siteUrl) {
+ throw new Error('SITE_URL environment variable is required')
+}🤖 Prompt for AI Agents |
||
|
|
||
| // The component client has methods needed for integrating Convex with Better Auth, | ||
| // as well as helper methods for general use. | ||
| export const authComponent = createClient<DataModel>(components.betterAuth) | ||
|
|
||
| export const createAuth = ( | ||
| ctx: GenericCtx<DataModel>, | ||
| { optionsOnly } = { optionsOnly: false }, | ||
| ) => { | ||
| return betterAuth({ | ||
| // disable logging when createAuth is called just to generate options. | ||
| // this is not required, but there's a lot of noise in logs without it. | ||
| logger: { | ||
| disabled: optionsOnly, | ||
| }, | ||
| baseURL: siteUrl, | ||
| database: authComponent.adapter(ctx), | ||
| // Configure simple, non-verified email/password to get started | ||
| emailAndPassword: { | ||
| enabled: true, | ||
| requireEmailVerification: false, | ||
| }, | ||
| plugins: [ | ||
| // The Convex plugin is required for Convex compatibility | ||
| convex({ jwtExpirationSeconds: 60 * 60 * 24 }), | ||
| ], | ||
| }) | ||
| } | ||
|
|
||
| // Example function for getting the current user | ||
| // Feel free to edit, omit, etc. | ||
| export const getCurrentUser = query({ | ||
| args: {}, | ||
| handler: async (ctx) => { | ||
| return authComponent.getAuthUser(ctx) | ||
| }, | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { defineApp } from 'convex/server' | ||
| import betterAuth from '@convex-dev/better-auth/convex.config' | ||
|
|
||
| const app = defineApp() | ||
| app.use(betterAuth) | ||
| export default app |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { httpRouter } from 'convex/server' | ||
| import { authComponent, createAuth } from './auth' | ||
|
|
||
| const http = httpRouter() | ||
| authComponent.registerRoutes(http, createAuth) | ||
| export default http |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { v } from 'convex/values' | ||
| import { action, mutation, query } from './_generated/server' | ||
| import { api } from './_generated/api' | ||
| import { authComponent } from './auth' | ||
|
|
||
| // Write your Convex functions in any file inside this directory (`convex`). | ||
| // See https://docs.convex.dev/functions for more. | ||
|
|
||
| // You can read data from the database via a query: | ||
| export const listNumbers = query({ | ||
| // Validators for arguments. | ||
| args: { | ||
| count: v.number(), | ||
| }, | ||
|
|
||
| // Query implementation. | ||
| handler: async (ctx, args) => { | ||
| // Get the current authenticated user | ||
| const authUser = await authComponent.getAuthUser(ctx) | ||
| if (!authUser._id) { | ||
| throw new Error('User must be authenticated to list random numbers') | ||
| } | ||
|
|
||
| // Read the database as many times as you need here. | ||
| // See https://docs.convex.dev/database/reading-data. | ||
| const numbers = await ctx.db | ||
| .query('numbers') | ||
| .withIndex('userId', (q) => q.eq('userId', authUser._id)) | ||
| // Ordered by _creationTime, return most recent | ||
| .order('desc') | ||
| .take(args.count) | ||
| return { | ||
| viewer: (await ctx.auth.getUserIdentity())?.name ?? null, | ||
| numbers: numbers.reverse().map((number) => number.value), | ||
| } | ||
| }, | ||
| }) | ||
|
|
||
| // You can write data to the database via a mutation: | ||
| export const addNumber = mutation({ | ||
| // Validators for arguments. | ||
| args: { | ||
| value: v.number(), | ||
| }, | ||
|
|
||
| // Mutation implementation. | ||
| handler: async (ctx, args) => { | ||
| // Get the current authenticated user | ||
| const authUser = await authComponent.getAuthUser(ctx) | ||
| if (!authUser._id) { | ||
| throw new Error('User must be authenticated to create a random number') | ||
| } | ||
|
|
||
| // Insert or modify documents in the database here. | ||
| // Mutations can also read from the database like queries. | ||
| // See https://docs.convex.dev/database/writing-data. | ||
|
|
||
| const id = await ctx.db.insert('numbers', { | ||
| value: args.value, | ||
| userId: authUser._id, | ||
| }) | ||
|
|
||
| console.log('Added new document with id:', id) | ||
| // Optionally, return a value from your mutation. | ||
| // return id; | ||
| }, | ||
| }) | ||
|
|
||
| // You can fetch data from and send data to third-party APIs via an action: | ||
| export const myAction = action({ | ||
| // Validators for arguments. | ||
| args: { | ||
| first: v.number(), | ||
| }, | ||
|
|
||
| // Action implementation. | ||
| handler: async (ctx, args) => { | ||
| // // Use the browser-like `fetch` API to send HTTP requests. | ||
| // // See https://docs.convex.dev/functions/actions#calling-third-party-apis-and-using-npm-packages. | ||
| // const response = await ctx.fetch("https://api.thirdpartyservice.com"); | ||
| // const data = await response.json(); | ||
|
|
||
| // // Query data by running Convex queries. | ||
| const data = await ctx.runQuery(api.myFunctions.listNumbers, { | ||
| count: 10, | ||
| }) | ||
| console.log(data) | ||
|
|
||
| // // Write data by running Convex mutations. | ||
| await ctx.runMutation(api.myFunctions.addNumber, { | ||
| value: args.first, | ||
| }) | ||
| }, | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { defineSchema, defineTable } from 'convex/server' | ||
| import { v } from 'convex/values' | ||
|
|
||
| // The schema is entirely optional. | ||
| // You can delete this file (schema.ts) and the | ||
| // app will continue to work. | ||
| // The schema provides more precise TypeScript types. | ||
| export default defineSchema({ | ||
| numbers: defineTable({ | ||
| value: v.number(), | ||
| userId: v.string(), | ||
| }).index('userId', ['userId']), | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| /* This TypeScript project config describes the environment that | ||
| * Convex functions run in and is used to typecheck them. | ||
| * You can modify it, but some settings are required to use Convex. | ||
| */ | ||
| "compilerOptions": { | ||
| /* These settings are not required by Convex and can be modified. */ | ||
| "allowJs": true, | ||
| "strict": true, | ||
| "moduleResolution": "Bundler", | ||
| "jsx": "preserve", | ||
| "jsxImportSource": "solid-js", | ||
| "skipLibCheck": true, | ||
| "allowSyntheticDefaultImports": true, | ||
| /* These compiler options are required by Convex */ | ||
| "target": "ESNext", | ||
| "lib": ["ES2021", "dom"], | ||
| "forceConsistentCasingInFileNames": true, | ||
| "module": "ESNext", | ||
| "isolatedModules": true, | ||
| "noEmit": true | ||
| }, | ||
| "include": ["./**/*"], | ||
| "exclude": ["./_generated"] | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||||||
| { | ||||||||||||||
| "name": "tanstack-solid-start-example-convex-better-auth", | ||||||||||||||
| "private": true, | ||||||||||||||
| "sideEffects": false, | ||||||||||||||
| "type": "module", | ||||||||||||||
| "scripts": { | ||||||||||||||
| "dev": "vite dev", | ||||||||||||||
| "convex:dev": "convex dev", | ||||||||||||||
| "build": "vite build && tsc --noEmit", | ||||||||||||||
| "start": "vite start" | ||||||||||||||
| }, | ||||||||||||||
| "dependencies": { | ||||||||||||||
| "@convex-dev/better-auth": "^0.9.7", | ||||||||||||||
| "@tailwindcss/vite": "^4.1.17", | ||||||||||||||
| "@tanstack/solid-router": "^1.134.16", | ||||||||||||||
| "@tanstack/solid-router-devtools": "^1.134.16", | ||||||||||||||
| "@tanstack/solid-start": "^1.134.16", | ||||||||||||||
|
Comment on lines
+15
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use workspace protocol for internal TanStack dependencies. Per the coding guidelines, internal dependencies should use the Apply this diff: - "@tanstack/solid-router": "^1.134.16",
- "@tanstack/solid-router-devtools": "^1.134.16",
- "@tanstack/solid-start": "^1.134.16",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-devtools": "workspace:*",
+ "@tanstack/solid-start": "workspace:*",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| "better-auth": "^1.3.27", | ||||||||||||||
| "clsx": "^2.1.1", | ||||||||||||||
| "convex": "^1.28.2", | ||||||||||||||
| "convex-solidjs": "^0.0.3", | ||||||||||||||
| "redaxios": "^0.5.1", | ||||||||||||||
| "solid-js": "^1.9.10", | ||||||||||||||
| "tailwind-merge": "^2.6.0", | ||||||||||||||
| "tailwindcss": "^4.1.17", | ||||||||||||||
| "zod": "^3.24.2" | ||||||||||||||
| }, | ||||||||||||||
| "devDependencies": { | ||||||||||||||
| "@types/node": "^22.10.2", | ||||||||||||||
| "combinate": "^1.1.11", | ||||||||||||||
| "typescript": "^5.7.2", | ||||||||||||||
| "vite": "^7.1.7", | ||||||||||||||
| "vite-plugin-solid": "^2.11.10", | ||||||||||||||
| "vite-tsconfig-paths": "^5.1.4" | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add CONVEX_SITE_URL for backend auth configuration.
The
convex/auth.config.tsfile referencesprocess.env.CONVEX_SITE_URL, but this environment variable is not defined in.env.example. This may cause the auth configuration to have an undefined domain.Apply this diff to add the missing variable:
🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 10-10: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🤖 Prompt for AI Agents