Setup your development environment
Learn how to update your docs locally and deploy them to the public.
Install Convex Client
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:
Install Clerk
npm install @clerk/nextjs
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>
)
}
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?
How do I handle file uploads?
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();
How do I implement real-time updates?
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
How do I handle pagination?
Use the pagination options in queries:const stores = await convex.query(
api.customers.explore.getStoresWithPagination,
{
paginationOpts: {
numItems: 20,
cursor: null // or previous cursor
}
}
);