Skip to content

Commit fa5d8d6

Browse files
authored
docs(solid-start): convex + better-auth example (#5796)
1 parent 46f786a commit fa5d8d6

33 files changed

+1748
-17
lines changed

docs/start/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@
338338
"label": "Basic + Supabase",
339339
"to": "framework/solid/examples/start-basic-supabase"
340340
},
341+
{
342+
"label": "Bare + Convex + Better Auth",
343+
"to": "framework/solid/examples/start-convex-better-auth"
344+
},
341345
{
342346
"label": "Cloudflare Vite Plugin",
343347
"to": "framework/solid/examples/start-basic-cloudflare"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Deployment used by `npx convex dev`
2+
CONVEX_DEPLOYMENT=
3+
4+
VITE_CONVEX_URL=
5+
6+
# Same as VITE_CONVEX_URL but ends in .site
7+
VITE_CONVEX_SITE_URL=
8+
9+
# Your local site URL
10+
SITE_URL=http://localhost:3000
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
.env.local
3+
/convex/_generated/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
### Guide
2+
3+
Rename .env.example to .env.local
4+
5+
Run
6+
7+
- `pnpm i`
8+
- `pnpx convex dev`
9+
- `pnpx convex env set SITE_URL http://localhost:3000/`
10+
- `pnpx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)`
11+
- `pnpx convex dev` - takes up one terminal
12+
13+
In a separate terminal run
14+
15+
- `pnpm run dev`
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Welcome to your Convex functions directory!
2+
3+
Write your Convex functions here.
4+
See https://docs.convex.dev/functions for more.
5+
6+
A query function that takes two arguments looks like:
7+
8+
```ts
9+
// functions.js
10+
import { query } from './_generated/server'
11+
import { v } from 'convex/values'
12+
13+
export const myQueryFunction = query({
14+
// Validators for arguments.
15+
args: {
16+
first: v.number(),
17+
second: v.string(),
18+
},
19+
20+
// Function implementation.
21+
handler: async (ctx, args) => {
22+
// Read the database as many times as you need here.
23+
// See https://docs.convex.dev/database/reading-data.
24+
const documents = await ctx.db.query('tablename').collect()
25+
26+
// Arguments passed from the client are properties of the args object.
27+
console.log(args.first, args.second)
28+
29+
// Write arbitrary JavaScript here: filter, aggregate, build derived data,
30+
// remove non-public properties, or create new objects.
31+
return documents
32+
},
33+
})
34+
```
35+
36+
Using this query function in a React component looks like:
37+
38+
```ts
39+
const data = useQuery(api.functions.myQueryFunction, {
40+
first: 10,
41+
second: 'hello',
42+
})
43+
```
44+
45+
A mutation function looks like:
46+
47+
```ts
48+
// functions.js
49+
import { mutation } from './_generated/server'
50+
import { v } from 'convex/values'
51+
52+
export const myMutationFunction = mutation({
53+
// Validators for arguments.
54+
args: {
55+
first: v.string(),
56+
second: v.string(),
57+
},
58+
59+
// Function implementation.
60+
handler: async (ctx, args) => {
61+
// Insert or modify documents in the database here.
62+
// Mutations can also read from the database like queries.
63+
// See https://docs.convex.dev/database/writing-data.
64+
const message = { body: args.first, author: args.second }
65+
const id = await ctx.db.insert('messages', message)
66+
67+
// Optionally, return a value from your mutation.
68+
return await ctx.db.get(id)
69+
},
70+
})
71+
```
72+
73+
Using this mutation function in a React component looks like:
74+
75+
```ts
76+
const mutation = useMutation(api.functions.myMutationFunction)
77+
function handleButtonPress() {
78+
// fire and forget, the most common way to use mutations
79+
mutation({ first: 'Hello!', second: 'me' })
80+
// OR
81+
// use the result once the mutation has completed
82+
mutation({ first: 'Hello!', second: 'me' }).then((result) =>
83+
console.log(result),
84+
)
85+
}
86+
```
87+
88+
Use the Convex CLI to push your functions to a deployment. See everything
89+
the Convex CLI can do by running `npx convex -h` in your project root
90+
directory. To learn more, launch the docs with `npx convex docs`.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default {
2+
providers: [
3+
{
4+
domain: process.env.CONVEX_SITE_URL,
5+
applicationID: 'convex',
6+
},
7+
],
8+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { createClient } from '@convex-dev/better-auth'
2+
import { convex } from '@convex-dev/better-auth/plugins'
3+
import { betterAuth } from 'better-auth'
4+
import { components } from './_generated/api'
5+
import { query } from './_generated/server'
6+
import type { GenericCtx } from '@convex-dev/better-auth'
7+
import type { DataModel } from './_generated/dataModel'
8+
9+
const siteUrl = process.env.SITE_URL!
10+
11+
// The component client has methods needed for integrating Convex with Better Auth,
12+
// as well as helper methods for general use.
13+
export const authComponent = createClient<DataModel>(components.betterAuth)
14+
15+
export const createAuth = (
16+
ctx: GenericCtx<DataModel>,
17+
{ optionsOnly } = { optionsOnly: false },
18+
) => {
19+
return betterAuth({
20+
// disable logging when createAuth is called just to generate options.
21+
// this is not required, but there's a lot of noise in logs without it.
22+
logger: {
23+
disabled: optionsOnly,
24+
},
25+
baseURL: siteUrl,
26+
database: authComponent.adapter(ctx),
27+
// Configure simple, non-verified email/password to get started
28+
emailAndPassword: {
29+
enabled: true,
30+
requireEmailVerification: false,
31+
},
32+
plugins: [
33+
// The Convex plugin is required for Convex compatibility
34+
convex({ jwtExpirationSeconds: 60 * 60 * 24 }),
35+
],
36+
})
37+
}
38+
39+
// Example function for getting the current user
40+
// Feel free to edit, omit, etc.
41+
export const getCurrentUser = query({
42+
args: {},
43+
handler: async (ctx) => {
44+
return authComponent.getAuthUser(ctx)
45+
},
46+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { defineApp } from 'convex/server'
2+
import betterAuth from '@convex-dev/better-auth/convex.config'
3+
4+
const app = defineApp()
5+
app.use(betterAuth)
6+
export default app
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { httpRouter } from 'convex/server'
2+
import { authComponent, createAuth } from './auth'
3+
4+
const http = httpRouter()
5+
authComponent.registerRoutes(http, createAuth)
6+
export default http
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { v } from 'convex/values'
2+
import { action, mutation, query } from './_generated/server'
3+
import { api } from './_generated/api'
4+
import { authComponent } from './auth'
5+
6+
// Write your Convex functions in any file inside this directory (`convex`).
7+
// See https://docs.convex.dev/functions for more.
8+
9+
// You can read data from the database via a query:
10+
export const listNumbers = query({
11+
// Validators for arguments.
12+
args: {
13+
count: v.number(),
14+
},
15+
16+
// Query implementation.
17+
handler: async (ctx, args) => {
18+
// Get the current authenticated user
19+
const authUser = await authComponent.getAuthUser(ctx)
20+
if (!authUser._id) {
21+
throw new Error('User must be authenticated to list random numbers')
22+
}
23+
24+
// Read the database as many times as you need here.
25+
// See https://docs.convex.dev/database/reading-data.
26+
const numbers = await ctx.db
27+
.query('numbers')
28+
.withIndex('userId', (q) => q.eq('userId', authUser._id))
29+
// Ordered by _creationTime, return most recent
30+
.order('desc')
31+
.take(args.count)
32+
return {
33+
viewer: (await ctx.auth.getUserIdentity())?.name ?? null,
34+
numbers: numbers.reverse().map((number) => number.value),
35+
}
36+
},
37+
})
38+
39+
// You can write data to the database via a mutation:
40+
export const addNumber = mutation({
41+
// Validators for arguments.
42+
args: {
43+
value: v.number(),
44+
},
45+
46+
// Mutation implementation.
47+
handler: async (ctx, args) => {
48+
// Get the current authenticated user
49+
const authUser = await authComponent.getAuthUser(ctx)
50+
if (!authUser._id) {
51+
throw new Error('User must be authenticated to create a random number')
52+
}
53+
54+
// Insert or modify documents in the database here.
55+
// Mutations can also read from the database like queries.
56+
// See https://docs.convex.dev/database/writing-data.
57+
58+
const id = await ctx.db.insert('numbers', {
59+
value: args.value,
60+
userId: authUser._id,
61+
})
62+
63+
console.log('Added new document with id:', id)
64+
// Optionally, return a value from your mutation.
65+
// return id;
66+
},
67+
})
68+
69+
// You can fetch data from and send data to third-party APIs via an action:
70+
export const myAction = action({
71+
// Validators for arguments.
72+
args: {
73+
first: v.number(),
74+
},
75+
76+
// Action implementation.
77+
handler: async (ctx, args) => {
78+
// // Use the browser-like `fetch` API to send HTTP requests.
79+
// // See https://docs.convex.dev/functions/actions#calling-third-party-apis-and-using-npm-packages.
80+
// const response = await ctx.fetch("https://api.thirdpartyservice.com");
81+
// const data = await response.json();
82+
83+
// // Query data by running Convex queries.
84+
const data = await ctx.runQuery(api.myFunctions.listNumbers, {
85+
count: 10,
86+
})
87+
console.log(data)
88+
89+
// // Write data by running Convex mutations.
90+
await ctx.runMutation(api.myFunctions.addNumber, {
91+
value: args.first,
92+
})
93+
},
94+
})

0 commit comments

Comments
 (0)