Convex is the real-time database powering Polaris, providing instant updates across all clients.
Why Convex?
Convex offers several advantages for Polaris:
- Real-time subscriptions - UI updates instantly when data changes
- Type-safe - Full TypeScript support with generated types
- Reactive queries - React components re-render automatically
- Built-in auth - Integrates seamlessly with Clerk
- Optimistic updates - Instant UI feedback before server confirmation
Setup
Create a Convex project
- Visit convex.dev
- Create an account and new project
- Copy your deployment URL
Configure environment variables
Add to .env.local:NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
CONVEX_DEPLOYMENT=your-deployment
Start Convex development server
This watches for schema changes and keeps types in sync.
Database schema
The Polaris schema is defined in convex/schema.ts with four tables:
Projects
Stores user projects with import/export status:
projects: defineTable({
name: v.string(),
ownerId: v.string(),
updatedAt: v.number(),
importStatus: v.optional(
v.union(
v.literal("importing"),
v.literal("completed"),
v.literal("failed")
)
),
exportStatus: v.optional(
v.union(
v.literal("exporting"),
v.literal("completed"),
v.literal("failed"),
v.literal("cancelled")
)
),
exportRepoUrl: v.optional(v.string()),
settings: v.optional(
v.object({
installCommand: v.optional(v.string()),
devCommand: v.optional(v.string())
})
)
}).index("by_owner", ["ownerId"])
Files
Hierarchical file system with text and binary file support:
files: defineTable({
projectId: v.id("projects"),
parentId: v.optional(v.id("files")),
name: v.string(),
type: v.union(v.literal("file"), v.literal("folder")),
content: v.optional(v.string()),
storageId: v.optional(v.id("_storage")),
updatedAt: v.number()
})
.index("by_project", ["projectId"])
.index("by_parent", ["parentId"])
.index("by_project_parent", ["projectId", "parentId"])
Conversations
AI chat threads:
conversations: defineTable({
projectId: v.id("projects"),
title: v.string(),
updatedAt: v.number()
}).index("by_project", ["projectId"])
Messages
Chat messages with processing status:
messages: defineTable({
conversationId: v.id("conversations"),
projectId: v.id("projects"),
role: v.union(v.literal("user"), v.literal("assistant")),
content: v.string(),
status: v.optional(
v.union(
v.literal("processing"),
v.literal("completed"),
v.literal("cancelled")
)
)
})
.index("by_conversation", ["conversationId"])
.index("by_project_status", ["projectId", "status"])
Using Convex in React
Convex provides React hooks for real-time data:
Queries (read data)
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
function ProjectList() {
const projects = useQuery(api.projects.list);
if (projects === undefined) {
return <div>Loading...</div>;
}
return (
<div>
{projects.map((project) => (
<div key={project._id}>{project.name}</div>
))}
</div>
);
}
Mutations (write data)
import { useMutation } from "convex/react";
import { api } from "@/convex/_generated/api";
function CreateProject() {
const createProject = useMutation(api.projects.create);
const handleCreate = async () => {
await createProject({ name: "My Project" });
};
return <button onClick={handleCreate}>Create</button>;
}
Actions (external API calls)
Actions can call external APIs like AI providers:
import { action } from "./_generated/server";
import { api } from "./_generated/api";
export const generateSuggestion = action({
handler: async (ctx, args) => {
// Call external AI API
const response = await fetch("https://api.anthropic.com/...");
// Store result in database
await ctx.runMutation(api.files.updateFile, {
fileId: args.fileId,
content: result
});
}
});
Development workflow
- Schema changes: Edit
convex/schema.ts
- Auto-sync:
npx convex dev regenerates types
- Write functions: Create queries/mutations in
convex/ directory
- Use in React: Import from
api._generated.api
Production deployment
Convex automatically deploys when you push to your git repository:
- Link your repository in the Convex dashboard
- Configure environment variables in production
- Deploy happens automatically on push
Best practices
Use indexes for queries that filter or sort data. Polaris indexes by ownerId for fast user-specific queries.
Never store sensitive data in plain text. Use Convex environment variables for secrets.
Learn more