Skip to main content

Setup your development environment

Learn how to update your docs locally and deploy them to the public.

Install Convex Client

npm install convex

Configure Environment

Create a .env.local file in your project root:
NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
Never commit your secret keys to version control. Use environment variables for all sensitive configuration.

Initialize Convex Client

// convex/client.ts
import { ConvexReactClient } from "convex/react";

const convex = new ConvexReactClient(
  process.env.NEXT_PUBLIC_CONVEX_URL!
);

export default convex;

Make your first request

Let’s start by fetching all available categories:
import { api } from "./convex/_generated/api";
import convex from "./convex/client";

async function getCategories() {
  try {
    const categories = await convex.query(
      api.shared.categories.getParents
    );
    
    console.log("Available categories:", categories);
    return categories;
  } catch (error) {
    console.error("Error fetching categories:", error);
  }
}

// Call the function
getCategories();
Expected response:
[
  {
    "_id": "k123456789",
    "name": "Food & Restaurants",
    "parent": null,
    "isActive": true,
    "_creationTime": 1640995200000
  },
  {
    "_id": "k987654321", 
    "name": "Electronics",
    "parent": null,
    "isActive": true,
    "_creationTime": 1640995200000
  }
]

Authentication

Most API endpoints require authentication. Here’s how to set it up:
1

Install Clerk

npm install @clerk/nextjs
2

Configure Clerk Provider

// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
import { ConvexProviderWithClerk } from "convex/react-clerk";
import convex from "./convex/client";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <ClerkProvider>
      <ConvexProviderWithClerk client={convex}>
        <html lang="en">
          <body>{children}</body>
        </html>
      </ConvexProviderWithClerk>
    </ClerkProvider>
  )
}
3

Use Authenticated Queries

import { useQuery } from "convex/react";
import { api } from "./convex/_generated/api";

function MyComponent() {
  const store = useQuery(
    api.stores.queries.getStoreByAuthenticatedOwner
  );
  
  if (store === undefined) return <div>Loading...</div>;
  if (store === null) return <div>No store found</div>;
  
  return <div>Store: {store.name}</div>;
}

Common Use Cases

1. Register a Customer

import { api } from "./convex/_generated/api";

const customerId = await convex.mutation(
  api.customers.account.registerCustomer,
  {
    preferredLanguage: "en"
  }
);

console.log("Customer registered:", customerId);

2. Create a Store

const storeId = await convex.mutation(
  api.stores.operations.registerStore,
  {
    name: "Mario's Pizza",
    primaryCategory: "k123456789", // Food category
    logoId: "s123456789", // Upload logo first
    tradeLicenseId: "s987654321" // Upload license first
  }
);

3. Add Products to Cart

await convex.mutation(api.customers.cart.addToCart, {
  customerId: "c123456789",
  storeId: "j123456789", 
  productId: "p123456789",
  quantity: 2
});

4. Create an Order

const order = await convex.mutation(
  api.customers.cartToOrder.createOrderFromCart,
  {
    customerId: "c123456789",
    storeId: "j123456789",
    deliveryAddress: {
      fullAddress: "123 Main St, Apt 4B",
      city: "ct123456789",
      area: "ar123456789",
      phoneNumber: "+971501234567"
    },
    deliveryType: "delivery",
    deliveryFee: 5.00,
    paymentMethod: "card",
    paymentIntentId: "pi_123456789"
  }
);

Error Handling

Always implement proper error handling:
import { ConvexError } from "convex/values";

try {
  const result = await convex.mutation(
    api.shared.products.createProduct,
    {
      name: "New Product",
      // ... other fields
    }
  );
} catch (error) {
  if (error instanceof ConvexError) {
    // Handle Convex-specific errors
    console.error("Convex error:", error.data);
  } else {
    // Handle other errors
    console.error("Unexpected error:", error);
  }
}

Rate Limiting

The API implements rate limiting to ensure fair usage:
  • Queries: 1000 requests per minute
  • Mutations: 100 requests per minute
  • Actions: 50 requests per minute
Use the useQuery hook for real-time data that updates automatically, and useMutation for user actions.

Next Steps

Need Help?

Use the generateUploadUrl function to get a secure upload URL:
const uploadUrl = await convex.mutation(
  api.shared.utils.generateUploadUrl
);

// Upload file to the URL
const response = await fetch(uploadUrl, {
  method: "POST",
  body: file
});

const { storageId } = await response.json();
Use the useQuery hook which automatically updates when data changes:
const orders = useQuery(api.stores.queries.getStoreOrders, {
  storeId: "j123456789",
  status: "pending"
});

// Component re-renders automatically when orders change
Use the pagination options in queries:
const stores = await convex.query(
  api.customers.explore.getStoresWithPagination,
  {
    paginationOpts: { 
      numItems: 20, 
      cursor: null // or previous cursor
    }
  }
);