#better auth and convex prod vs local

50 messages · Page 1 of 1 (latest)

empty nimbus
#

i currently have auth set up with better auth and working just fine locally, but am having problems in prod, specifically with the lib/auth.ts file

const createOptions = (ctx: GenericCtx) =>
  ({
    baseURL: "http://localhost:3000",
    trustedOrigins: [
      "http://localhost:3000",
    ],
    ...

i currently have it set up like this which works just fine locally, but in prod i'd have to use an environment variable to set the base url/trusted origins

so i use something like process.env.NEXT_PUBLIC_THIS_SITE_URL, and this is where i get a bit confused
it seems like i cannot set this environment locally at all, and i'd have to set this inside the convex dashboard? i think this is because auth.ts is being imported into convex/http.ts so they cannot be set locally using .env files?

so i was just wondering how do i set that variable dynamically based on whether im working locally vs in prod

i did try something like

const thisSiteUrl =
  process.env.NODE_ENV === "development"
    ? "http://localhost:3000"
    : process.env.NEXT_PUBLIC_THIS_SITE_URL;
const createOptions = (ctx: GenericCtx) =>
  ({
    baseURL: thisSiteUrl,

but it doesn't work because NODE_ENV will always be prod because it picks up the environment of the cloud-hosted convex instance?

so as of now, i have to either set baseURL/trustedOrigins to either my localhost or either my prod URL for it to work

thanks in advance!

minor carbonBOT
#

Hi @empty nimbus. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

You're correct in your understanding: when Convex code (like your lib/auth.ts or convex/http.ts) runs, it does not have access to environment variables from your local .env files. Instead, Convex functions—including anything imported into them—only have access to environment variables set in the Convex deployment itself (via the dashboard or CLI). This is true for both local and production deployments.

Key points:

  • Local .env files (like .env.local) are only read by your frontend dev server (Vite, Next.js, etc.) and the Convex CLI for project info, not by the Convex backend runtime.
  • Convex backend code (including anything in convex/ or imported by it) only sees environment variables set in the Convex dashboard or via npx convex env set ... for each deployment (dev and prod) see: Environment Variables.
  • NODE_ENV is always "development" in Convex environments, regardless of deployment, due to how Convex builds and runs code. You cannot use NODE_ENV to distinguish between dev and prod in Convex functions see: Discord thread.

How to handle different URLs for dev and prod:

  1. Set environment variables in each Convex deployment:
    • For local dev:
      npx convex env set THIS_SITE_URL <http://localhost:3000>
      
    • For prod:
      npx convex env set THIS_SITE_URL <https://your-production-url.com>
      
    • Do this in the Convex dashboard or via CLI for each deployment.
#
  1. Access the variable in your Convex code:
    const thisSiteUrl = process.env.THIS_SITE_URL!;
    const createOptions = (ctx: GenericCtx) => ({
      baseURL: thisSiteUrl,
      trustedOrigins: [thisSiteUrl],
      // ...
    });
    
    This way, the correct value is injected per deployment, and you don't need to branch on NODE_ENV or rely on .env files.

Summary:
You must set environment variables for each Convex deployment (dev and prod) in the dashboard or via CLI. Do not rely on .env files or NODE_ENV in Convex backend code. Use process.env.YOUR_VAR and set the value appropriately per deployment see: Environment Variables, Discord thread.

If you need to use the same codebase for both environments, always reference process.env.THIS_SITE_URL and set it per deployment. This is the recommended and supported approach.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon oh...
so i just create two instances? and it'd be fine right?

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, that's correct! You should create two Convex deployments—one for development and one for production. Each deployment can have its own set of environment variables (such as your site URL), which you set via the Convex dashboard or CLI. Your Convex backend code will then use the appropriate value for each environment, and you don't need to change your code or rely on .env files for this.

This is the standard approach for handling environment-specific configuration in Convex. Just make sure to set the environment variables for each deployment as needed, and your code will work correctly in both dev and prod environments see: Environment Variables.

If you need to migrate data or environment variables between deployments, you can use the Convex CLI to export/import data and set environment variables as needed see: Discord support thread.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon
how do i handle deployment to prod in this case

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To handle deployment to production with Convex in your scenario:

  1. Set up your production deployment:

    • Make sure you have a production deployment created for your Convex project. This is separate from your local/dev deployment. Each project in Convex has one production deployment and one dev deployment per team member see: Deploying Your App to Production.
  2. Set environment variables for production:

    • In the Convex dashboard, switch to your production deployment and set the required environment variables (like THIS_SITE_URL) to your production values (e.g., <https://your-production-url.com>).
    • You can also use the CLI:
      npx convex env --prod set THIS_SITE_URL <https://your-production-url.com>
      
    • Make sure the same variable is set in your dev deployment with the dev value (e.g., <http://localhost:3000>) see: Environment Variables.
  3. Deploy your backend code to production:

    • Run the deploy command from your project directory:
      npx convex deploy
      
    • This will push your backend code to the production deployment. If you’re using CI/CD (like Vercel or Netlify), you can automate this step and set the CONVEX_DEPLOY_KEY as an environment variable in your CI provider see: CLI Deploy, Vercel Hosting, Netlify Hosting.
  4. Frontend configuration:

    • Make sure your frontend is configured to use the production Convex URL and any other production-specific settings. This is typically handled by environment variables in your frontend build process.
#

Summary:

  • Set environment variables per deployment (dev/prod) in the Convex dashboard or via CLI.
  • Deploy your backend code to production with npx convex deploy.
  • Ensure your frontend is configured for production.

This approach ensures your app uses the correct configuration in each environment and is the recommended workflow for Convex see: Typical team development workflow.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon
give me a more detailed step-by-step breakdown on how do push my staging/dev convex instance to my prod isntance
note that i only want to deploy my schema changes, not the data

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Certainly! Here’s a detailed, step-by-step breakdown for pushing your staging/dev Convex instance (including data and environment variables) to your production instance:


1. Export Data from Dev/Staging

From your local machine, export the data from your dev deployment:

npx convex export

This will create a snapshot of your dev database.


2. Export Environment Variables from Dev/Staging

List and save your environment variables from your dev deployment:

npx convex env list

Copy these values somewhere safe, as you’ll need to set them in production.


3. Import Data into Production

Import the previously exported data into your production deployment:

npx convex import --prod --replace-all

This will overwrite all data in your production deployment with the data from your dev export. Be careful: this is a destructive operation for your prod data.


4. Set Environment Variables in Production

Set each environment variable in your production deployment, either via the Convex dashboard or CLI:

npx convex env --prod set VAR_NAME value

Repeat for each variable you exported from dev.
Alternatively, you can set them in the Convex dashboard.


5. Deploy Your Backend Code to Production

Push your backend code to the production deployment:

npx convex deploy

This will deploy your Convex functions, indexes, and schema to production.


6. Update Frontend Configuration (if needed)

If your frontend references the Convex deployment URL or other environment-specific values, make sure it’s configured to use the production values.


7. Verify Everything Works

#
  • Test your production deployment to ensure data, environment variables, and backend code are all correct.
  • Make sure your frontend is communicating with the production backend.

References:

If you have a lot of data, be aware that exports/imports can take time and may cause downtime. For most use cases, the above process is the recommended and supported way to migrate from dev/staging to production in Convex.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon
give me a more detailed step-by-step breakdown on how do push my staging/dev convex instance to my prod isntance
note that i only want to deploy my schema changes, not the data

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To push only your schema changes (and backend code) from your staging/dev Convex instance to your production instance—without migrating data—follow these steps:


1. Test Your Changes Locally

  • Make your schema and backend code changes in your local/dev environment.
  • Ensure everything works as expected in your dev deployment.

2. Deploy to Production

  • Run the following command from your project directory:

    npx convex deploy
    

    This command will:

    • Typecheck your Convex functions.
    • Regenerate the generated code in convex/_generated.
    • Bundle your Convex functions and their dependencies.
    • Push your functions, indexes, and schema to production.
    • Your production data will remain unchanged; only the schema and code are updated.

    The target deployment is determined by your environment variables (typically, CONVEX_DEPLOY_KEY in CI or CONVEX_DEPLOYMENT locally). You can also deploy manually from your local machine, and Convex will prompt for confirmation before deploying to production see: CLI Deploy, Project Configuration.


3. Handle Schema/Data Mismatches

#
  • Important: Convex enforces that your schema matches the data already in production. If your schema changes are not compatible with the existing data (e.g., making a field required when it’s missing in some documents), the deploy will fail.
  • To avoid this, follow safe schema migration practices:

4. Verify Production

  • After deploying, check your production deployment to ensure the schema and backend code changes are live and functioning as expected.

Summary:
You do not need to export/import data to update your schema and backend code. Just use npx convex deploy to push code and schema changes to production. Make sure your schema changes are compatible with your existing production data, or the deploy will be blocked until you resolve the mismatch.

If you need to update data to match a new schema, do so with migrations or manual edits before making breaking schema changes.


If you need more details on safe schema evolution or handling schema/data mismatches, let me know!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon
during the early stages of development (e.g. MVP), i don't need to consider deploying changes that much
is there a way for me to speed up the process of having the schema of the dev envirnment match that of the prod environment
tbh, i would have just used the same convex instance for both prod and dev for now (just for creating an MVP)

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, during the early stages of development (such as building an MVP), it’s common and acceptable to use a single Convex deployment for both development and production to simplify your workflow and avoid the overhead of managing multiple environments. This approach is especially useful if you don’t need strict separation between dev and prod and want to iterate quickly.

Convex does not impose fundamental limitations on dev deployments compared to prod ones, so you can use a dev deployment for your prototype if you wish. The main differences are around error reporting, logging, and some production-only features like custom domains, but for MVPs and early development, these are usually not blockers (see Discord support thread).

Tips for fast iteration:

  • Use the "deploy on save" behavior of the dev environment for rapid feedback.
  • If you need to reset your data or schema, you can delete dev data liberally and maintain a seed script to re-initialize your database, which is much faster than writing migrations at this stage (see: YOLO: Get to an MVP fast).
  • You can always export your data before making big changes, so you can restore it if needed (see: Use snapshot export).
#

Summary:
For MVPs, it’s perfectly fine to use a single Convex instance for both dev and prod. This will keep your schema and data in sync automatically and speed up your development process. You can introduce separate environments later as your app matures and your needs change.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon how do i get the type for the user table from the betterAuth schema (i.e. separate from the app schema containing most of the tables)

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I'm sorry, but the knowledge sources do not provide explicit details on how to extract or reference the TypeScript type for the Better Auth user table (the internal user table managed by Better Auth, separate from your application's users table). The documentation and examples focus on how to define and use your own users table for your application, and how to interact with Better Auth's hooks and metadata, but they do not describe how to import or infer the type for the Better Auth internal user table directly in your code see: Better Auth users table separation.

If you need to type the user object returned by Better Auth hooks (such as in onCreateUser), you may need to refer to the Better Auth package's own type definitions, but this is not covered in the provided knowledge sources. If you have access to the Better Auth package source, you might find relevant types exported there, but this is outside the scope of the current documentation.

If you need the type for your own application's users table, you can use the generated Doc<"users"> type from your Convex data model see: TypeScript types from schema.

If you need more specific guidance, please provide additional context or refer to the Better Auth package documentation directly.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon what is the best way to handle an isAuthenticated state, and get the user info with convex + better auth

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To handle isAuthenticated state and get user info with Convex + Better Auth, you should use the authentication state components and hooks provided by convex/react and the Better Auth component.

1. Authentication State in React

Use the <Authenticated>, <Unauthenticated>, and <AuthLoading> components from convex/react to control your UI based on authentication state. This ensures your app only renders protected content when the user is authenticated:

import { Authenticated, Unauthenticated, AuthLoading } from "convex/react";

export default function App() {
  return (
    <>
      <AuthLoading>
        <div>Loading...</div>
      </AuthLoading>
      <Authenticated>
        <Dashboard />
      </Authenticated>
      <Unauthenticated>
        <SignIn />
      </Unauthenticated>
    </>
  );
}

See: Basic Usage - Authorization

2. Getting User Info

Inside your Convex functions (queries, mutations, actions), you can get the authenticated user's info using the Better Auth component:

import { betterAuthComponent } from "./auth";
import { Id } from "./_generated/dataModel";

export const myFunction = query({
  args: {},
  handler: async (ctx) => {
    // Get the user id from Convex auth
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      return null;
    }
    const userId = await betterAuthComponent.getAuthUserId(ctx);
    if (!userId) {
      return null;
    }
    // Get user data from your application's users table
    const user = await ctx.db.get(userId as Id<"users">);

    // Get user metadata from Better Auth
    const userMetadata = await betterAuthComponent.getAuthUser(ctx);
#

// Combine and return
return { ...userMetadata, ...user };
}
});

[See: Basic Usage - Authorization](<https://convex-better-auth.netlify.app/>)

### **Summary**

- Use `<Authenticated>`, `<Unauthenticated>`, and `<AuthLoading>` for UI state.
- Use `betterAuthComponent.getAuthUserId(ctx)` and `betterAuthComponent.getAuthUser(ctx)` in Convex functions to get user info and metadata.

This approach ensures you have a reliable `isAuthenticated` state and access to user information in both your frontend and backend with Convex + Better Auth.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Convex's useQuery hook does provide client-side caching, but it works differently from React Query (TanStack Query). With Convex, when you use useQuery, the client automatically subscribes to the query result and keeps it up-to-date in real time. The cache is managed by Convex: if multiple components use the same query (with the same arguments), they share a single subscription and cached result. When the underlying data changes, all subscribers are updated simultaneously, ensuring consistency and eliminating stale data see: Realtime, useQuery sharing.

Unlike React Query, which can serve stale data from its cache and requires manual invalidation or refetching, Convex's cache is always live and consistent with the backend. However, this also means that Convex does not keep "stale" data around for instant display after unmount/remount; it always reflects the current state of the backend.

In summary:

  • Yes, Convex's useQuery provides client-side caching and sharing across components.
  • It is always live and consistent, not stale.
  • It is not identical to React Query's cache, which can serve stale data and has more manual cache control optionssee: Differences from using fetch with TanStack Query.
#

If you need more React Query-like caching (e.g., keeping stale data for instant display after navigation), you may want to look into the convex-helpers query caching or use Convex's TanStack Query adapter, but the default Convex useQuery is always live and up-to-date.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon if i use tanstack query with convex, do i get caching? for example i see cached results when switching betwen pages in nextjs?

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, if you use TanStack Query with Convex (via the @convex-dev/react-query adapter), you do get client-side caching. When you navigate between pages in Next.js, TanStack Query will serve cached results if the query is still in memory, which means you can see instant results without a loading state, as long as the subscription is still active.

Convex queries in React Query continue to receive updates after the last component subscribed to the query unmounts. By default, the subscription stays alive for 5 minutes (gcTime), so if you navigate away and back within that window, you'll see cached (and live-updating) results instantly. This behavior is similar to what you expect from TanStack Query with other backends, but with the added benefit that Convex keeps the data live and consistent with the backend—no manual invalidation is needed. You can also configure the gcTime to control how long the cache persists after unmounting see: TanStack Start - Staying subscribed to queries, Convex with TanStack Query - Differences from using fetch.

So, yes, you get caching and instant results when switching between pages in Next.js with TanStack Query and Convex, as long as the query subscription is still active.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@empty nimbus Kapa.ai is still learning and improving, please let me know how I did by reacting below.

empty nimbus
#

@minor carbon and in addition to that i still get reactive updates right? so both caching and reactive updates? or wrong?

minor carbonBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, that's correct! When you use TanStack Query with Convex (via the @convex-dev/react-query adapter), you get both client-side caching and reactive updates:

  • Caching: TanStack Query will serve cached results instantly when you navigate between pages, as long as the subscription is still active (by default, for 5 minutes after the last component unmounts, configurable via gcTime).
  • Reactive updates: The data remains live and will automatically update in real time if the underlying data in Convex changes, so your UI always reflects the latest state without manual refetching or invalidation.

This means you get the best of both worlds: instant cached results on navigation and always up-to-date data thanks to Convex's reactivity see: TanStack Start - Staying subscribed to queries, Convex with TanStack Query - Differences from using fetch, Convex with TanStack Query.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: