The Projects API allows you to create, read, update, and delete projects. Each project represents a web application generated by ZapDev’s AI.
Data Model
Projects contain the following fields:
Unique identifier for the project
Display name of the project
Clerk user ID of the project owner
Framework used for the project: NEXTJS, ANGULAR, REACT, VUE, or SVELTE
Preferred AI model for code generation (optional)
Timestamp when the project was created (milliseconds)
Timestamp when the project was last updated (milliseconds)
Queries
list
Get all projects for the authenticated user with preview attachments.
import { api } from '@/convex/_generated/api';
import { useQuery } from 'convex/react';
const projects = useQuery(api.projects.list);
Arguments: None
Returns: Array of projects with preview attachments
The most recent image attachment from the latest message in the project
Authentication: Returns empty array if user is not authenticated
get
Get a single project by ID.
import { api } from '@/convex/_generated/api';
import { useQuery } from 'convex/react';
const project = useQuery(api.projects.get, {
projectId: "jh7...",
});
The project ID to retrieve
Returns: Project object
Authentication: Required. User must own the project.
Throws: "Project not found" or "Unauthorized"
listShowcase
Get public showcase projects (up to 12 projects with at least one fragment).
import { api } from '@/convex/_generated/api';
import { useQuery } from 'convex/react';
const showcaseProjects = useQuery(api.projects.listShowcase);
Arguments: None
Returns: Array of up to 12 projects sorted by creation date (descending)
Total number of messages in the project
Whether the project has at least one fragment
Authentication: Public (no authentication required)
getForUser
Get a project for a specific user (for background jobs/Inngest).
import { api } from '@/convex/_generated/api';
const project = await ctx.runQuery(api.projects.getForUser, {
userId: "user_...",
projectId: "jh7...",
});
The project ID to retrieve
Returns: Project object
Throws: "Project not found" or "Unauthorized"
getForSystem
System-level query to get any project by ID (for Inngest background jobs only). Bypasses authentication.
import { api } from '@/convex/_generated/api';
const project = await ctx.runQuery(api.projects.getForSystem, {
projectId: "jh7...",
});
The project ID to retrieve
Returns: Project object
Throws: "Project not found"
Note: This is a system-level function that bypasses authentication. Use only from trusted Inngest workflows.
Mutations
create
Create a new project.
import { api } from '@/convex/_generated/api';
import { useMutation } from 'convex/react';
const createProject = useMutation(api.projects.create);
const projectId = await createProject({
name: "My App",
framework: "NEXTJS",
});
Display name for the project
Framework to use: NEXTJS, ANGULAR, REACT, VUE, or SVELTE
Returns: Id<"projects"> - The newly created project ID
Authentication: Required
update
Update a project’s name, framework, or model preference.
import { api } from '@/convex/_generated/api';
import { useMutation } from 'convex/react';
const updateProject = useMutation(api.projects.update);
const projectId = await updateProject({
projectId: "jh7...",
name: "Updated App Name",
framework: "REACT",
modelPreference: "gpt-4",
});
New name for the project (optional)
New framework for the project (optional)
Preferred AI model (optional, set to undefined to clear)
Returns: Id<"projects"> - The updated project ID
Authentication: Required. User must own the project.
Throws: "Project not found" or "Unauthorized"
deleteProject
Delete a project and all associated data (messages, fragments, attachments).
import { api } from '@/convex/_generated/api';
import { useMutation } from 'convex/react';
const deleteProject = useMutation(api.projects.deleteProject);
const result = await deleteProject({
projectId: "jh7...",
});
Returns: { success: true }
Authentication: Required. User must own the project.
Throws: "Project not found" or "Unauthorized"
Note: This operation cascades and deletes all messages, fragments, attachments, and fragment drafts associated with the project.
getOrCreateFragmentDraft
Get or create a fragment draft for a project.
import { api } from '@/convex/_generated/api';
import { useMutation } from 'convex/react';
const getOrCreateDraft = useMutation(api.projects.getOrCreateFragmentDraft);
const draft = await getOrCreateDraft({
projectId: "jh7...",
framework: "NEXTJS",
});
Framework for the fragment draft
Returns: Fragment draft object with files, framework, createdAt, updatedAt
Authentication: Required. User must own the project.
Throws: "Unauthorized"
createForUser
Create a project with an explicit user ID (for use from actions).
import { api } from '@/convex/_generated/api';
const projectId = await ctx.runMutation(api.projects.createForUser, {
userId: "user_...",
name: "My App",
framework: "NEXTJS",
});
Display name for the project
Returns: Id<"projects"> - The newly created project ID
updateForUser
Update a project for a specific user (for use from background jobs/Inngest).
import { api } from '@/convex/_generated/api';
const projectId = await ctx.runMutation(api.projects.updateForUser, {
userId: "user_...",
projectId: "jh7...",
name: "Updated Name",
});
Preferred AI model (optional)
Returns: Id<"projects"> - The updated project ID
Throws: "Project not found" or "Unauthorized"
Actions
createWithMessage
Create a new project with an initial message. This is the primary entry point for new projects.
import { api } from '@/convex/_generated/api';
import { useAction } from 'convex/react';
const createWithMessage = useAction(api.projects.createWithMessage);
const result = await createWithMessage({
value: "Create a todo app with Next.js",
});
The initial message content (user’s request)
Returns: Project object with additional fields:
The newly created project ID
The ID of the initial message
Authentication: Required
Throws: "Unauthorized" or "You have run out of credits"
Note: This action automatically:
- Checks and consumes 1 credit
- Generates a random project name
- Creates the project with NEXTJS framework
- Creates the initial USER message
createWithMessageAndAttachments
Create a new project with an initial message and optional attachments (images, Figma files, etc.).
import { api } from '@/convex/_generated/api';
import { useAction } from 'convex/react';
const createWithAttachments = useAction(
api.projects.createWithMessageAndAttachments
);
const result = await createWithAttachments({
value: "Create an app based on this design",
attachments: [
{
url: "https://...",
size: 123456,
width: 1920,
height: 1080,
},
],
});
The initial message content
Optional array of attachment objects
Image width in pixels (optional)
Image height in pixels (optional)
Returns: Project object with id, messageId, and value
Authentication: Required
Throws: "Unauthorized" or "You have run out of credits"
Note: This action automatically checks and consumes 1 credit before creating the project.
Database Schema
The projects table has the following indexes:
by_userId: Lookup projects by user ID
by_userId_createdAt: Lookup projects by user ID and creation date (for sorting)
// convex/schema.ts
projects: defineTable({
name: v.string(),
userId: v.string(),
framework: frameworkEnum,
modelPreference: v.optional(v.string()),
createdAt: v.optional(v.number()),
updatedAt: v.optional(v.number()),
})
.index("by_userId", ["userId"])
.index("by_userId_createdAt", ["userId", "createdAt"])
Example Usage
Creating a project with the UI
import { api } from '@/convex/_generated/api';
import { useAction } from 'convex/react';
import { useState } from 'react';
function NewProjectForm() {
const [input, setInput] = useState('');
const createProject = useAction(api.projects.createWithMessage);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const result = await createProject({ value: input });
console.log('Created project:', result.id);
// Navigate to project page
} catch (error) {
console.error('Failed to create project:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Describe your app..."
/>
<button type="submit">Create Project</button>
</form>
);
}
Listing user projects
import { api } from '@/convex/_generated/api';
import { useQuery } from 'convex/react';
function ProjectList() {
const projects = useQuery(api.projects.list);
if (!projects) return <div>Loading...</div>;
return (
<div>
{projects.map((project) => (
<div key={project._id}>
<h3>{project.name}</h3>
<p>Framework: {project.framework}</p>
{project.previewAttachment && (
<img src={project.previewAttachment.url} alt="Preview" />
)}
</div>
))}
</div>
);
}
Deleting a project
import { api } from '@/convex/_generated/api';
import { useMutation } from 'convex/react';
function DeleteProjectButton({ projectId }: { projectId: string }) {
const deleteProject = useMutation(api.projects.deleteProject);
const handleDelete = async () => {
if (confirm('Are you sure you want to delete this project?')) {
await deleteProject({ projectId });
}
};
return (
<button onClick={handleDelete}>
Delete Project
</button>
);
}