Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/start/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@
"label": "Basic + Supabase",
"to": "framework/solid/examples/start-basic-supabase"
},
{
"label": "Bare + Convex + Better Auth",
"to": "framework/solid/examples/start-convex-better-auth"
},
{
"label": "Cloudflare Vite Plugin",
"to": "framework/solid/examples/start-basic-cloudflare"
Expand Down
10 changes: 10 additions & 0 deletions examples/solid/start-convex-better-auth/.env.example
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
Comment on lines +1 to +10
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add CONVEX_SITE_URL for backend auth configuration.

The convex/auth.config.ts file references process.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:

 # Deployment used by `npx convex dev`
 CONVEX_DEPLOYMENT=
 
 VITE_CONVEX_URL=
 
 # Same as VITE_CONVEX_URL but ends in .site
 VITE_CONVEX_SITE_URL=
 
+# Backend site URL for Convex auth (same as SITE_URL)
+CONVEX_SITE_URL=http://localhost:3000
+
 # Your local site URL
 SITE_URL=http://localhost:3000
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 10-10: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
In examples/solid/start-convex-better-auth/.env.example around lines 1 to 10,
add a CONVEX_SITE_URL entry so the backend auth config can read
process.env.CONVEX_SITE_URL; update the file by inserting a new line like
CONVEX_SITE_URL= (or a sensible default such as http://localhost:3000) alongside
the other CONVEX/VITE variables so the auth configuration never receives an
undefined domain.

3 changes: 3 additions & 0 deletions examples/solid/start-convex-better-auth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

.env.local
/convex/_generated/
15 changes: 15 additions & 0 deletions examples/solid/start-convex-better-auth/README.md
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`
90 changes: 90 additions & 0 deletions examples/solid/start-convex-better-auth/convex/README.md
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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

‼️ 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
See https://docs.convex.dev/functions for more.
See <https://docs.convex.dev/functions> for more.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

4-4: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In examples/solid/start-convex-better-auth/convex/README.md around line 4, the
bare URL "https://docs.convex.dev/functions" should be wrapped in angle brackets
for proper Markdown formatting; update the line to use
<https://docs.convex.dev/functions> so the URL renders correctly and doesn't get
parsed as plain text.


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`.
8 changes: 8 additions & 0 deletions examples/solid/start-convex-better-auth/convex/auth.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
providers: [
{
domain: process.env.CONVEX_SITE_URL,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify environment variable and handle undefined domain.

The domain field uses process.env.CONVEX_SITE_URL, but the .env.example file defines SITE_URL (not CONVEX_SITE_URL). Additionally, if the environment variable is not set, the domain will be undefined, which may cause runtime issues.

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



---

🏁 Script executed:

```shell
#!/bin/bash
# Find and display .env.example file
fd '.env.example' examples/solid/start-convex-better-auth/

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.ts

Length of output: 125


🏁 Script executed:

#!/bin/bash
# View auth.ts with context around line 9
cat examples/solid/start-convex-better-auth/convex/auth.ts

Length 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 f

Length 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 -20

Length 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 2

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Search entire repository for .env.example to understand naming pattern
fd -H '\.env\.example' . --max-depth 4

Length 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/ -n

Length of output: 201


🏁 Script executed:

#!/bin/bash
# View .env.example to see which variables are defined
cat examples/solid/start-convex-better-auth/.env.example

Length of output: 195


🏁 Script executed:

#!/bin/bash
# View convex README for documentation on env vars
cat examples/solid/start-convex-better-auth/convex/README.md

Length of output: 2485


Fix environment variable mismatch in auth.config.ts.

The .env.example file defines SITE_URL and VITE_CONVEX_SITE_URL, but auth.config.ts references CONVEX_SITE_URL (which does not exist). This creates an inconsistency where the domain field will be undefined at runtime. Either correct the variable name to match what's defined in .env.example or add validation to handle the undefined case.

🤖 Prompt for AI Agents
In examples/solid/start-convex-better-auth/convex/auth.config.ts around line 4,
the code uses process.env.CONVEX_SITE_URL which doesn't exist in .env.example;
update the reference to read from the correct env vars (prefer client-safe
VITE_CONVEX_SITE_URL, falling back to SITE_URL) and add a runtime validation
that throws or logs a clear error if the resolved domain is undefined so domain
is never silently undefined at runtime.

applicationID: 'convex',
},
],
}
46 changes: 46 additions & 0 deletions examples/solid/start-convex-better-auth/convex/auth.ts
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!
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate the required environment variable.

Using a non-null assertion on process.env.SITE_URL! will cause a runtime error if the variable is undefined. Consider providing a fallback or validation with a clear error message.

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
In examples/solid/start-convex-better-auth/convex/auth.ts around line 9, the
code uses a non-null assertion on process.env.SITE_URL! which can throw at
runtime if the env var is missing; replace the assertion with an explicit
validation: read process.env.SITE_URL into a const, check if it's undefined or
empty, and if so throw a clear Error explaining SITE_URL is required (or
alternatively assign a safe default URL if appropriate), otherwise use the
validated value for siteUrl.


// 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
6 changes: 6 additions & 0 deletions examples/solid/start-convex-better-auth/convex/http.ts
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
94 changes: 94 additions & 0 deletions examples/solid/start-convex-better-auth/convex/myFunctions.ts
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,
})
},
})
13 changes: 13 additions & 0 deletions examples/solid/start-convex-better-auth/convex/schema.ts
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']),
})
25 changes: 25 additions & 0 deletions examples/solid/start-convex-better-auth/convex/tsconfig.json
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"]
}
36 changes: 36 additions & 0 deletions examples/solid/start-convex-better-auth/package.json
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
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use workspace protocol for internal TanStack dependencies.

Per the coding guidelines, internal dependencies should use the workspace:* protocol instead of fixed versions. This ensures the example uses the local workspace versions of TanStack packages.

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

‼️ 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
"@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:*",
🤖 Prompt for AI Agents
In examples/solid/start-convex-better-auth/package.json around lines 15 to 17,
the TanStack internal dependencies are pinned to fixed versions; change each of
the three entries ("@tanstack/solid-router", "@tanstack/solid-router-devtools",
"@tanstack/solid-start") to use the workspace protocol by replacing their
version strings with "workspace:*" so the example resolves to local workspace
packages.

"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"
}
}
Binary file not shown.
Loading
Loading