ScaleRocket/Web

Convex Setup

Create a Convex project, configure auth providers, and connect your app.

Convex Setup

Convex is the backend for ScaleRocket. It handles your database, authentication, real-time subscriptions, and serverless functions — all in TypeScript.

Create a Convex Account

  1. Go to convex.dev
  2. Click Sign up and create your account
  3. You can sign up with GitHub or Google

Initialize Your Project

From the root of your project, run:

npx convex dev

On first run, this will:

  • Prompt you to log in (if you haven't already)
  • Create a new Convex project (or link to an existing one)
  • Deploy your schema and functions
  • Start watching for changes and hot-reloading

Note: npx convex dev is the main command you'll use during development. It watches your convex/ directory and auto-deploys changes in real time.

Get Your Deployment URL

  1. Go to dashboard.convex.dev
  2. Select your project
  3. Copy the Deployment URL (looks like https://your-project-123.convex.cloud)
  4. Add it to your environment file:
# apps/app/.env.local
VITE_CONVEX_URL=https://your-project-123.convex.cloud

Configure Auth Providers

ScaleRocket supports email/password, Google, and GitHub login out of the box.

Set your environment variables via the Convex CLI:

# Site URL (your app's public URL)
npx convex env set SITE_URL https://your-app.com

# GitHub OAuth
npx convex env set AUTH_GITHUB_ID your-github-client-id
npx convex env set AUTH_GITHUB_SECRET your-github-client-secret

# Google OAuth
npx convex env set AUTH_GOOGLE_ID your-google-client-id
npx convex env set AUTH_GOOGLE_SECRET your-google-client-secret

GitHub OAuth

  1. Go to GitHub Developer Settings
  2. Click New OAuth App
  3. Set the authorization callback URL to your SITE_URL
  4. Copy the Client ID and Client Secret
  5. Set them with npx convex env set as shown above

Google OAuth

  1. Go to Google Cloud Console
  2. Create a new project (or use an existing one)
  3. Go to APIs & Services > Credentials
  4. Create an OAuth 2.0 Client ID (Web application)
  5. Add your SITE_URL as an authorized redirect URI
  6. Copy the Client ID and Client Secret
  7. Set them with npx convex env set as shown above

Schema Overview

With Convex, your schema is defined entirely in TypeScript:

// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  profiles: defineTable({
    userId: v.string(),
    name: v.string(),
    email: v.string(),
    avatarUrl: v.optional(v.string()),
  }).index("by_userId", ["userId"]),

  subscriptions: defineTable({
    userId: v.string(),
    stripeSubscriptionId: v.string(),
    status: v.string(),
  }).index("by_userId", ["userId"]),
});

The schema is automatically deployed when you run npx convex dev. There are no migrations to manage — Convex handles schema changes for you.

Note: Convex validates all data against your schema at runtime. If a mutation tries to insert data that doesn't match, it will throw an error.

Convex Functions

Convex provides four types of functions:

TypePurposeExample use case
QueriesRead data (cached & reactive)Fetching a user's profile
MutationsWrite data (transactional)Updating a subscription status
ActionsCall external APIs (non-transactional)Sending an email, calling Stripe
HTTP ActionsHandle incoming webhooksStripe webhook endpoint

Query Example

// convex/profiles.ts
import { query } from "./_generated/server";
import { v } from "convex/values";

export const getProfile = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("profiles")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .unique();
  },
});

Mutation Example

// convex/profiles.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";

export const updateProfile = mutation({
  args: { userId: v.string(), name: v.string() },
  handler: async (ctx, args) => {
    const profile = await ctx.db
      .query("profiles")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .unique();

    if (!profile) throw new Error("Profile not found");

    await ctx.db.patch(profile._id, { name: args.name });
  },
});

Action Example

// convex/emails.ts
import { action } from "./_generated/server";
import { v } from "convex/values";

export const sendWelcomeEmail = action({
  args: { email: v.string(), name: v.string() },
  handler: async (ctx, args) => {
    // Call an external email API
    await fetch("https://api.your-email-service.com/send", {
      method: "POST",
      body: JSON.stringify({
        to: args.email,
        subject: `Welcome, ${args.name}!`,
      }),
    });
  },
});

Real-time

All useQuery hooks in Convex are reactive by default. When the underlying data changes, your components re-render automatically — no extra setup needed.

// apps/app/src/components/Profile.tsx
import { useQuery } from "convex/react";
import { api } from "../../convex/_generated/api";

export function Profile({ userId }: { userId: string }) {
  const profile = useQuery(api.profiles.getProfile, { userId });

  if (!profile) return <div>Loading...</div>;

  return <div>{profile.name}</div>;
}

Note: There is no need for polling, WebSocket setup, or cache invalidation. Convex handles all of this for you. When any mutation updates data that a query depends on, all subscribed clients receive the update instantly.

Convex Dashboard

Convex comes with a built-in dashboard at dashboard.convex.dev where you can:

  • Browse and edit your data tables
  • View and run your functions
  • Monitor logs and errors
  • Manage environment variables
  • View deployment history

Note: The dashboard is a great tool for debugging during development. You can inspect your data, run queries manually, and see real-time logs.

Next Steps

Done reading? Mark this page as complete.

On this page