### Run Development Server Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Start the development server for the backend, web app, and native app simultaneously using pnpm and Turbo. ```sh pnpm dev ``` -------------------------------- ### Install Dependencies with pnpm Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Use this command to install all project dependencies within the monorepo using pnpm. ```sh pnpm install ``` -------------------------------- ### Monorepo Development and Deployment Commands Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Provides essential commands for managing the monorepo, including installing dependencies, setting up Convex, running all apps concurrently, deploying the backend with web app build, and installing workspace-specific dependencies. ```bash # Install all dependencies pnpm install # Set up Convex project (creates packages/backend/.env.local) pnpm --filter @packages/backend setup # Run all three apps concurrently via Turborepo pnpm dev # Deploy backend + build web app for Vercel cd packages/backend && pnpm exec convex deploy \ --cmd 'cd ../../apps/web && pnpm build' \ --cmd-url-env-var-name NEXT_PUBLIC_CONVEX_URL # Install a dependency in a specific workspace pnpm --filter web-app add some-package@latest pnpm --filter native-app add some-package@latest pnpm --filter @packages/backend add some-package@latest ``` -------------------------------- ### Web App Convex Provider with Clerk Integration Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Web provider setup for integrating Convex with Clerk in a Next.js application. Requires NEXT_PUBLIC_CONVEX_URL environment variable. ```typescript // apps/web/src/app/ConvexClientProvider.tsx — web provider setup "use client"; import { useAuth } from "@clerk/nextjs"; import { ConvexReactClient } from "convex/react"; import { ConvexProviderWithClerk } from "convex/react-clerk"; const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); export default function ConvexClientProvider({ children }: { children: ReactNode }) { return ( {children} ); } ``` -------------------------------- ### Add Dependencies to Specific Packages Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Install new dependencies into a specific package within the monorepo using the pnpm --filter command. Examples are provided for web-app, native-app, and the backend package. ```sh pnpm --filter web-app add mypackage@latest ``` ```sh pnpm --filter native-app add mypackage@latest ``` ```sh pnpm --filter @packages/backend add mypackage@latest ``` -------------------------------- ### Native App Convex Provider with Clerk Integration Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Native provider setup for integrating Convex with Clerk in an Expo application. Requires EXPO_PUBLIC_CONVEX_URL and EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY environment variables. ```typescript // apps/native/ConvexClientProvider.tsx import { ClerkProvider, useAuth } from "@clerk/clerk-expo"; import { tokenCache } from "@clerk/clerk-expo/token-cache"; import { ConvexReactClient } from "convex/react"; import { ConvexProviderWithClerk } from "convex/react-clerk"; const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL!); export default function ConvexClientProvider({ children }: { children: ReactNode }) { return ( {children} ); } ``` -------------------------------- ### Configure Convex Backend Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Run this command to set up the Convex backend, which includes logging into Convex, creating or connecting a project, and generating the necessary .env.local file. ```sh pnpm --filter @packages/backend setup ``` -------------------------------- ### Deploy Backend and Build Web App Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Deploy the Convex backend and build the Next.js web app from the backend directory. The --cmd-url-env-var-name flag ensures the correct Convex URL is used. ```sh cd ../../packages/backend && pnpm exec convex deploy --cmd 'cd ../../apps/web && pnpm build' --cmd-url-env-var-name NEXT_PUBLIC_CONVEX_URL ``` -------------------------------- ### Environment Configuration Variables Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Sets up environment variables for Convex and Clerk on both web and native platforms, along with backend-specific variables like Clerk JWT issuer domain and optional OpenAI API key. ```bash # apps/web/.env.local (copy from .example.env) NEXT_PUBLIC_CONVEX_URL=https://.convex.cloud NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... # apps/native/.env.local (copy from .example.env) EXPO_PUBLIC_CONVEX_URL=https://.convex.cloud EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... # Convex dashboard environment variables (packages/backend) CLERK_JWT_ISSUER_DOMAIN=https://.clerk.accounts.dev OPENAI_API_KEY=sk-... # optional — enables AI summarization ``` -------------------------------- ### Set OpenAI API Key for AI Summaries Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md If you intend to use the AI summary feature, add your OpenAI API key to your Convex environment variables. ```sh OPENAI_API_KEY=... ``` -------------------------------- ### api.notes.createNote Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Creates a new note for the authenticated user. Optionally schedules an AI summary generation if the `OPENAI_API_KEY` is configured. ```APIDOC ## api.notes.createNote — Mutation: create a new note ### Description Inserts a note for the authenticated user. If `isSummary` is `true` and `OPENAI_API_KEY` is set in Convex, schedules the `internal.openai.summary` action immediately after insertion to generate and save an AI summary. ### Arguments - **title** (string) - Required - The title of the note. - **content** (string) - Required - The content of the note. - **isSummary** (boolean) - Required - Whether to generate an AI summary for the note. ### Returns - **noteId** (Id<"notes">) - The ID of the newly created note. ### Client Usage Example ```typescript const createNote = useMutation(api.notes.createNote); await createNote({ title: "Meeting notes", content: "Discussed Q3 roadmap and milestones.", isSummary: true, }); ``` ``` -------------------------------- ### Create Note Mutation Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Inserts a new note for the authenticated user. If `isSummary` is true and `OPENAI_API_KEY` is set, it schedules an AI summary generation action. ```typescript // packages/backend/convex/notes.ts import { mutation } from "./_generated/server"; import { v } from "convex/values"; import { internal } from "./_generated/api"; export const createNote = mutation({ args: { title: v.string(), content: v.string(), isSummary: v.boolean(), }, handler: async (ctx, { title, content, isSummary }) => { const userId = await requireUserId(ctx); // throws if unauthenticated const noteId = await ctx.db.insert("notes", { userId, title, content }); if (isSummary) { // Schedules OpenAI action asynchronously; summary is written back via saveSummary await ctx.scheduler.runAfter(0, internal.openai.summary, { id: noteId, title, content, }); } return noteId; // Id<"notes"> }, }); ``` ```typescript // --- Client usage (web, in CreateNote component) --- const createNote = useMutation(api.notes.createNote); const openaiKeySet = useQuery(api.openai.openaiKeySet) ?? true; await createNote({ title: "Meeting notes", content: "Discussed Q3 roadmap and milestones.", isSummary: true, // only meaningful when openaiKeySet === true }); ``` ```typescript // --- Client usage (native, in CreateNoteScreen) --- const createNote = useMutation(api.notes.createNote); try { await createNote({ title: noteTitle, content: noteContent, isSummary: isAdvancedSummarizationEnabled, }); router.replace("/"); } catch (error) { Alert.alert("Failed to create note", String(error)); } ``` -------------------------------- ### Generate AI Summary with OpenAI Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Internal Convex action to summarize notes using OpenAI. Requires OPENAI_API_KEY environment variable. Invokes saveSummary on success or error. ```typescript // packages/backend/convex/openai.ts import OpenAI from "openai"; import { internalAction } from "./_generated/server"; import { v } from "convex/values"; import { internal } from "./_generated/api"; export const summary = internalAction({ args: { id: v.id("notes"), title: v.string(), content: v.string(), }, handler: async (ctx, { id, title, content }) => { const apiKey = process.env.OPENAI_API_API_KEY; if (!apiKey) { // Saves error message as summary so the client sees a useful state await ctx.runMutation(internal.openai.saveSummary, { id, summary: missingEnvVariableUrl("OPENAI_API_KEY", "https://platform.openai.com/account/api-keys"), }); return; } const openai = new OpenAI({ apiKey }); const output = await openai.responses.create({ model: "gpt-5.4-mini", instructions: "You summarize user notes in concise plain English. Respond with summary text only.", input: `Summarize the following note.\n\nTitle: ${title}\n\nContent:\n${content}`, }); const summaryText = output.output_text.trim(); if (!summaryText) throw new Error(`OpenAI returned an empty summary for note '${id}'`); await ctx.runMutation(internal.openai.saveSummary, { id, summary: summaryText }); }, }); // internal.openai.saveSummary — patches the note document with the summary export const saveSummary = internalMutation({ args: { id: v.id("notes"), summary: v.string() }, handler: async (ctx, { id, summary }) => { await ctx.db.patch(id, { summary }); }, }); ``` -------------------------------- ### api.notes.getNotes Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Fetches all notes for the authenticated Clerk user. Returns up to 100 notes ordered by creation date (newest first). If the user is not authenticated, an empty array is returned. ```APIDOC ## Query: api.notes.getNotes ### Description Fetches all notes for the authenticated Clerk user. Returns up to 100 notes ordered by creation date (newest first). If the user is not authenticated, an empty array is returned. ### Handler ```typescript // packages/backend/convex/notes.ts import { query } from "./_generated/server"; export const getNotes = query({ args: {}, handler: async (ctx) => { const userId = await getUserId(ctx); // reads Clerk subject from JWT if (!userId) return []; return await ctx.db .query("notes") .withIndex("by_userId", (q) => q.eq("userId", userId)) .order("desc") .take(100); }, }); ``` ### Client Usage (Web) ```typescript // packages/backend/convex/_generated/api import { useQuery } from "convex/react"; import { api } from "@packages/backend/convex/_generated/api"; function NotesList() { const notes = useQuery(api.notes.getNotes); // notes is undefined while loading, then Doc<"notes">[] if (!notes) return

Loading…

; return (
    {notes.map((note) => (
  • {note.title}
  • ))}
); } ``` ### Client Usage (Native) ```typescript // packages/backend/convex/_generated/api const allNotes = useQuery(api.notes.getNotes); const filtered = search ? allNotes?.filter( (n) => n.title.toLowerCase().includes(search.toLowerCase()) || n.content.toLowerCase().includes(search.toLowerCase()), ) : (allNotes ?? []); ``` ``` -------------------------------- ### Convex Query: Fetch All Notes for User Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Fetches up to 100 notes for the authenticated user from the Convex backend, ordered by creation date (newest first). Returns an empty array if the user is not authenticated. ```typescript import { query } from "./_generated/server"; export const getNotes = query({ args: {}, handler: async (ctx) => { const userId = await getUserId(ctx); // reads Clerk subject from JWT if (!userId) return []; return await ctx.db .query("notes") .withIndex("by_userId", (q) => q.eq("userId", userId)) .order("desc") .take(100); }, }); ``` ```typescript import { useQuery } from "convex/react"; import { api } from "@packages/backend/convex/_generated/api"; function NotesList() { const notes = useQuery(api.notes.getNotes); // notes is undefined while loading, then Doc<"notes">[] if (!notes) return

Loading…

; return (
    {notes.map((note) => (
  • {note.title}
  • ))}
); } ``` ```typescript const allNotes = useQuery(api.notes.getNotes); const filtered = search ? allNotes?.filter( (n) => n.title.toLowerCase().includes(search.toLowerCase()) || n.content.toLowerCase().includes(search.toLowerCase()), ) : (allNotes ?? []); ``` -------------------------------- ### api.openai.openaiKeySet Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Checks if the `OPENAI_API_KEY` is configured in the Convex environment. ```APIDOC ## api.openai.openaiKeySet — Query: check if OpenAI is configured ### Description Returns a boolean indicating whether `OPENAI_API_KEY` is present in the Convex environment. Used by both clients to conditionally enable or disable the AI summarization UI. ### Arguments None ### Returns - **boolean** - `true` if `OPENAI_API_KEY` is set, `false` otherwise. ### Client Usage Example ```typescript const openaiKeySet = useQuery(api.openai.openaiKeySet) ?? true; // Renders UI elements based on this value ``` ``` -------------------------------- ### Utility for Missing Environment Variables Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Generates a user-friendly error message for missing environment variables, including a direct link to the Convex dashboard settings page for easy configuration. ```typescript export function missingEnvVariableUrl(envVarName: string, whereToGet: string) { const deployment = deploymentName(); if (!deployment) return `Missing ${envVarName} in environment variables.`; return ( `\n Missing ${envVarName} in environment variables.\n\n` + ` Get it from ${whereToGet} . Paste it on the Convex dashboard: ` + ` https://dashboard.convex.dev/d/${deployment}/settings?var=${envVarName}` ); } export function deploymentName() { const url = process.env.CONVEX_CLOUD_URL; if (!url) return undefined; return /https:\/\/(.+)\.convex\.cloud/.exec(url)?.[1]; } // Example output when OPENAI_API_KEY is missing: // "\n Missing OPENAI_API_KEY in environment variables.\n\n // Get it from https://platform.openai.com/account/api-keys . // Paste it on the Convex dashboard: // https://dashboard.convex.dev/d/my-project-123/settings?var=OPENAI_API_KEY" ``` -------------------------------- ### Use a Convex Query Function in React Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/packages/backend/convex/README.md Demonstrates how to call a Convex query function from a React component using the `useQuery` hook. ```typescript const data = useQuery(api.functions.myQueryFunction, { first: 10, second: "hello", }); ``` -------------------------------- ### Check OpenAI Configuration Query Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Returns a boolean indicating if `OPENAI_API_KEY` is set in the Convex environment. This is used to conditionally enable/disable AI summarization features. ```typescript // packages/backend/convex/openai.ts import { query } from "./_generated/server"; export const openaiKeySet = query({ args: {}, handler: async () => { return Boolean(process.env.OPENAI_API_KEY); }, }); ``` ```typescript // --- Client usage (web) --- const openaiKeySet = useQuery(api.openai.openaiKeySet) ?? true; // Renders as enabled/disabled based on this value ``` ```typescript // --- Client usage (native) --- const openaiKeySet = useQuery(api.openai.openaiKeySet) ?? false; // Disables the summarization checkbox when false {isAdvancedSummarizationEnabled && } ``` -------------------------------- ### Use a Convex Mutation Function in React Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/packages/backend/convex/README.md Shows how to call a Convex mutation function from a React component using the `useMutation` hook. Mutations can be fired and forgotten or their results can be awaited. ```typescript 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), ); } ``` -------------------------------- ### api.notes.getNote Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Fetches a single note by its ID. Returns the note document if it belongs to the authenticated user, or null if the note does not exist, the ID is invalid, or the user does not own it. ```APIDOC ## Query: api.notes.getNote ### Description Fetches a single note by its ID. Returns the note document if it belongs to the authenticated user, or null if the note does not exist, the ID is invalid, or the user does not own it. ### Parameters #### Arguments - **id** (string) - Optional - The ID of the note to fetch. ### Handler ```typescript // packages/backend/convex/notes.ts import { query } from "./_generated/server"; import { v } from "convex/values"; export const getNote = query({ args: { id: v.optional(v.string()), }, handler: async (ctx, args) => { const { id } = args; if (!id) return null; const normalizedId = ctx.db.normalizeId("notes", id); // validates Id format if (!normalizedId) return null; const userId = await getUserId(ctx); if (!userId) return null; const note = await ctx.db.get(normalizedId); if (!note || note.userId !== userId) return null; // ownership check return note; // Doc<"notes"> | null }, }); ``` ### Client Usage (Web) ```typescript // packages/backend/convex/_generated/api import { useQuery } from "convex/react"; import { api } from "@packages/backend/convex/_generated/api"; import { Id } from "@packages/backend/convex/_generated/dataModel"; const NoteDetails = ({ noteId }: { noteId: Id<"notes"> }) => { const note = useQuery(api.notes.getNote, { id: noteId }); // note is undefined (loading) | null (not found/unauthed) | Doc<"notes"> return

{note?.content ?? "Loading…"}

; }; ``` ### Client Usage (Native) ```typescript // packages/backend/convex/_generated/api const { noteId } = useLocalSearchParams<{ noteId: string }>(); const note = useQuery(api.notes.getNote, { id: noteId ?? undefined }); const displayText = activeTab === "original" ? note?.content : note?.summary ?? "No summary available"; ``` ``` -------------------------------- ### Convex Query: Fetch Single Note by ID Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Fetches a single note by its ID from the Convex backend. It validates the ID format, checks user ownership, and returns the note document or null if not found, invalid, or unauthorized. ```typescript import { query } from "./_generated/server"; import { v } from "convex/values"; export const getNote = query({ args: { id: v.optional(v.string()), }, handler: async (ctx, args) => { const { id } = args; if (!id) return null; const normalizedId = ctx.db.normalizeId("notes", id); // validates Id format if (!normalizedId) return null; const userId = await getUserId(ctx); if (!userId) return null; const note = await ctx.db.get(normalizedId); if (!note || note.userId !== userId) return null; // ownership check return note; // Doc<"notes"> | null }, }); ``` ```typescript import { useQuery } from "convex/react"; import { api } from "@packages/backend/convex/_generated/api"; import { Id } from "@packages/backend/convex/_generated/dataModel"; const NoteDetails = ({ noteId }: { noteId: Id<"notes"> }) => { const note = useQuery(api.notes.getNote, { id: noteId }); // note is undefined (loading) | null (not found/unauthed) | Doc<"notes"> return

{note?.content ?? "Loading…"}

; }; ``` ```typescript const { noteId } = useLocalSearchParams<{ noteId: string }>(); const note = useQuery(api.notes.getNote, { id: noteId ?? undefined }); const displayText = activeTab === "original" ? note?.content : note?.summary ?? "No summary available"; ``` -------------------------------- ### Configure Clerk JWT Provider for Convex Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Configuration for Clerk as a JWT provider in Convex. Requires CLERK_JWT_ISSUER_DOMAIN environment variable. ```typescript // packages/backend/convex/auth.config.ts — configure Clerk as JWT provider import { type AuthConfig } from "convex/server"; export default { providers: [ { domain: process.env.CLERK_JWT_ISSUER_DOMAIN, // e.g. https://xxx.clerk.accounts.dev applicationID: "convex", }, ], } satisfies AuthConfig; ``` -------------------------------- ### Convex Database Schema Definition Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Defines the 'notes' table schema for the Convex backend, including fields for userId, title, content, and an optional summary. It includes an index for efficient per-user queries. ```typescript import { defineSchema, defineTable, } from "convex/server"; import { v } from "convex/values"; export default defineSchema({ notes: defineTable({ userId: v.string(), // Clerk subject identifier title: v.string(), content: v.string(), summary: v.optional(v.string()), // Populated by OpenAI action }).index("by_userId", ["userId"]), }); ``` -------------------------------- ### Native OAuth Sign-In with Clerk Expo Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Initiates OAuth flows for Google and Apple using Clerk's useSSO hook in an Expo app. Redirects to the main app route upon successful session activation. ```typescript import { useSSO } from "@clerk/clerk-expo"; import { useRouter } from "expo-router"; type OAuthStrategy = "oauth_google" | "oauth_apple"; const LoginScreen = () => { const { startSSOFlow } = useSSO(); const router = useRouter(); const onPress = async ({ strategy }: { strategy: OAuthStrategy }) => { try { const { createdSessionId, setActive } = await startSSOFlow({ strategy }); if (!createdSessionId || !setActive) return; await setActive({ session: createdSessionId }); router.replace("/"); // navigate to authenticated dashboard } catch (err) { const message = String(err); if (message.includes("already signed in")) return; console.error("OAuth error", err); } }; return ( <> onPress({ strategy: "oauth_google" })}> Continue with Google onPress({ strategy: "oauth_apple" })}> Continue with Apple ); }; ``` -------------------------------- ### Set Convex JWT Issuer Domain Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/README.md Configure the Clerk JWT issuer domain in your Convex environment variables. This is crucial for authentication between Clerk and Convex. ```sh CLERK_JWT_ISSUER_DOMAIN=https://your-frontend-api.clerk.accounts.dev ``` -------------------------------- ### api.notes.deleteNote Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Permanently deletes a note after verifying ownership. Throws errors if the note is not found or not owned by the user. ```APIDOC ## api.notes.deleteNote — Mutation: delete a note ### Description Permanently deletes a note after verifying it exists and is owned by the calling user. Throws descriptive errors if the note is not found or if the user does not own it. ### Arguments - **noteId** (Id<"notes">) - Required - The ID of the note to delete. ### Client Usage Example ```typescript const deleteNote = useMutation(api.notes.deleteNote); await deleteNote({ noteId: "someNoteId" }); ``` ``` -------------------------------- ### Delete Note Mutation Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Permanently deletes a note after verifying ownership. Throws errors if the note is not found or not owned by the user. ```typescript // packages/backend/convex/notes.ts import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const deleteNote = mutation({ args: { noteId: v.id("notes"), // strongly-typed Convex ID }, handler: async (ctx, { noteId }) => { const userId = await requireUserId(ctx); const note = await ctx.db.get(noteId); if (!note) throw new Error(`Note '${noteId}' could not be found`); if (note.userId !== userId) throw new Error(`User '${userId}' cannot delete note '${noteId}'`); await ctx.db.delete(noteId); }, }); ``` ```typescript // --- Client usage (web, in Notes component) --- const deleteNote = useMutation(api.notes.deleteNote); // Passed down to NoteItem → DeleteNote dialog deleteNote({ noteId: note._id })} /> ``` ```typescript // --- Client usage (native) --- // Triggered from a swipe-to-delete or button press await deleteNote({ noteId: someNoteId }); ``` -------------------------------- ### Define a Convex Mutation Function Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/packages/backend/convex/README.md Use this to define a mutation function that writes data to the database. It requires argument validators and an async handler. ```typescript 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); }, }); ``` -------------------------------- ### Define a Convex Query Function Source: https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/blob/main/packages/backend/convex/README.md Use this to define a query function that reads data from the database. It requires argument validators and an async handler. ```typescript 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; }, }); ``` -------------------------------- ### Native Route Guard with Expo Router Source: https://context7.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/llms.txt Protects the '(app)' route group in Expo Router by redirecting unauthenticated users to the '/sign-in' route. Ensures Clerk is loaded before checking authentication status. ```typescript import { useAuth } from "@clerk/clerk-expo"; import { Redirect, Stack } from "expo-router"; export default function AppLayout() { const { isLoaded, isSignedIn } = useAuth(); if (!isLoaded) return null; // wait for Clerk to initialize if (!isSignedIn) return ; return ; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.