v1 SaaS Starter Kit

repository·main·Indexed 42 days ago

https://github.com/get-convex/v1

An opinionated, open-source SaaS starter kit built with Convex, Next.js, and Turborepo. It provides a production-ready stack featuring authentication, Polar billing, OpenPanel analytics, and Resend email delivery.

Tokens
18.1K
Snippets
79
Records
86
Agent score
53%

What's inside v1

  1. Install v1 using the CLI (Recommended)

    main

    The fastest way to start a new v1 project is using the official CLI. This command automates project creation, dependency installation, git initialization, Convex backend setup, authentication configuration, environment variable setup, and database seeding for Polar products.

    bun create @convex-dev/v1@latest
  2. Manual Setup of v1

    main

    If you prefer to set up the project manually, follow these steps in order:

    1. Clone the repository:
      bunx degit get-convex/v1 v1
      cd v1
    2. Install dependencies: bun install
    3. Initialize git: git init && git commit -am 'initial commit'
    4. Set up Convex backend:
      cd packages/backend
      npm run setup
      Note: This will fail after project creation due to missing environment variables; this is expected.
    5. Set up authentication: npx @convex-dev/auth (follow the prompts).
    6. Configure Environment Variables: Refer to the Detailed Service Setup Instructions.
    7. Sync Convex Environment Variables: Copy the contents of packages/backend/.env and paste them into the environment variables panel in your Convex dashboard.
    8. Initialize Polar and seed database:
      cd packages/backend
      bunx convex run init
    9. Start development server: bun dev
    bunx degit get-convex/v1 v1
    cd v1
    bun install
    git init && git commit -am 'initial commit'
    cd packages/backend
    npm run setup
    npx @convex-dev/auth
    cd packages/backend
    bunx convex run init
    cd ../..
    bun dev
  3. Deploy v1 to Vercel

    main

    The v1 repository contains two separate Next.js applications (apps/web and apps/app). You should deploy them as two separate Vercel projects.

    Required Production Environment Variables:

    • NEXT_PUBLIC_APP_URL (Optional for apps/web): The URL of your deployed app (e.g., https://your-app.vercel.app).
    • NEXT_PUBLIC_CONVEX_URL (Required for both): The URL of your production Convex instance.
  4. Configure Google Authentication

    main

    To use Google OAuth, add your credentials to packages/backend/.env and configure the redirect URI in the Google Cloud Console.

    Redirect URI: https://<your-convex-deployment>.convex.site/api/auth/callback/google

    Authorized JavaScript Origins (Local Dev):

    • http://localhost:3000
    • http://localhost:3001
    # packages/backend/.env
    AUTH_GOOGLE_ID=424242424242-foobarfoobarfoobarfoobar42.apps.googleusercontent.com
    AUTH_GOOGLE_SECRET=GOCSPX-foobarfoobarfoobarfoobar42
  5. Configure Polar billing and webhooks

    main

    Polar requires an organization token and a webhook secret. The webhook must point to your Convex deployment's specific endpoint.

    Webhook URL Pattern: https://<your-convex-deployment>.convex.site/polar/events

    # packages/backend/.env
    POLAR_ORGANIZATION_TOKEN=polar_oat_foobarfoobarfoobarfoobarfoobar42
    POLAR_WEBHOOK_SECRET=whsec_foobarfoobarfoobarfoobarfoobar42
  6. Configure Convex environment variables

    main

    To connect your Next.js applications to your Convex backend, add your Convex URL to the .env files of both the web and app workspaces.

    Required Variable:

    • NEXT_PUBLIC_CONVEX_URL: The URL from your Convex dashboard (e.g., https://example-123.convex.cloud).
    # apps/web/.env and apps/app/.env
    NEXT_PUBLIC_CONVEX_URL=https://foobar-42.convex.cloud
  7. Configure Sentry error monitoring

    main

    Add these variables to apps/app/.env to enable error tracking and monitoring.

    # apps/app/.env
    NEXT_PUBLIC_SENTRY_DSN=https://foobarfoobar42@foobar42.ingest.sentry.io/42424242
    SENTRY_AUTH_TOKEN=foobarfoobarfoobarfoobarfoobar42
    SENTRY_ORG=your-org-name
    SENTRY_PROJECT=your-project-name
  8. Configure OpenPanel analytics

    main

    OpenPanel requires configuration in both the app and the web workspace to track analytics correctly.

    # apps/app/.env
    OPENPANEL_SECRET_KEY=sec_foobarfoobarfoobarfoobar42
    
    # apps/web/.env and apps/app/.env
    NEXT_PUBLIC_OPENPANEL_CLIENT_ID=foo-bar-42-baz-qux-42
  9. Configure Resend email delivery

    main

    Add these variables to packages/backend/.env to enable email sending via Resend.

    # packages/backend/.env
    RESEND_API_KEY=re_foobarfoobarfoobarfoobarfoobar42
    RESEND_SENDER_EMAIL_AUTH=auth@yourdomain.com
  10. Use a mutation function in a React component

    main

    To trigger a mutation in a React component, use the useMutation hook. This returns a function that you can call with the required arguments. You can use it as a 'fire and forget' operation or await the returned Promise to handle the result.

    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),
      );
    }
  11. Use a query function in a React component

    main

    To consume a query function in a React component, use the useQuery hook. Pass the function reference from the generated api object and an object containing the required arguments.

    const data = useQuery(api.functions.myQueryFunction, {
      first: 10,
      second: "hello",
    });
  12. Define a Convex query function

    main

    A query function is used to read data from the database. You define it using the query function from your generated server module. You must provide an args object containing validators (using v from convex/values) to define the schema of the arguments, and a handler function that receives ctx (the context) and args.

    // 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.
        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);
    
        return documents;
      },
    });