Overview
The Files API allows you to manage files and folders within Polaris IDE projects. File operations are performed through Convex mutations and queries.
File operations are primarily handled through the Convex database directly. The API routes mainly handle project-level operations. For direct file manipulation, use the Convex client with proper authentication.
File Schema
Files in Polaris IDE follow this structure:
files : {
_id : Id < "files" > ,
projectId : Id < "projects" > ,
parentId ?: Id < "files" > , // Parent folder (undefined for root files)
name : string , // File or folder name
type : "file" | "folder" ,
content ?: string , // Text content (for files only)
storageId ?: Id < "_storage" > , // Binary file reference (for non-text files)
updatedAt : number // Timestamp
}
File Types
Text Files
Text files store content directly in the content field:
JavaScript/TypeScript (.js, .ts, .jsx, .tsx)
CSS/SCSS (.css, .scss)
HTML (.html)
JSON (.json)
Markdown (.md)
Python (.py)
Plain text (.txt)
Binary Files
Binary files use Convex storage and store a reference in storageId:
Images (.png, .jpg, .svg, etc.)
Fonts (.ttf, .woff, etc.)
Other binary formats
File Operations
Create File
Create a new file using Convex mutation:
import { useMutation } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function MyComponent () {
const createFile = useMutation ( api . files . create );
const handleCreateFile = async () => {
const fileId = await createFile ({
projectId: 'k17a8b9c0d1e2f3g4h5i6j7' ,
parentId: undefined , // or parent folder ID
name: 'index.js' ,
type: 'file' ,
content: 'console.log("Hello World");'
});
};
}
Read File
Query files by project:
import { useQuery } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function MyComponent ({ projectId }) {
const files = useQuery ( api . files . getFiles , { projectId });
return (
< ul >
{ files ?. map ( file => (
< li key = { file . _id } >
{ file . type === 'folder' ? '📁' : '📄' } { file . name }
</ li >
)) }
</ ul >
);
}
Update File Content
Update file content using mutation:
import { useMutation } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function MyComponent () {
const updateFile = useMutation ( api . files . update );
const handleSave = async ( fileId , newContent ) => {
await updateFile ({
id: fileId ,
content: newContent
});
};
}
Delete File
Delete a file or folder (recursively deletes contents):
import { useMutation } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function MyComponent () {
const deleteFile = useMutation ( api . files . deleteFile );
const handleDelete = async ( fileId ) => {
await deleteFile ({ id: fileId });
};
}
Rename File
Rename a file or folder:
import { useMutation } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function MyComponent () {
const renameFile = useMutation ( api . files . update );
const handleRename = async ( fileId , newName ) => {
await renameFile ({
id: fileId ,
name: newName
});
};
}
Internal API (System Routes)
The system API requires POLARIS_CONVEX_INTERNAL_KEY and is used by background jobs. Not intended for external use.
Write File by Path
Used by GitHub import and project generation:
await convex . mutation ( api . system . writeFileByPath , {
internalKey: process . env . POLARIS_CONVEX_INTERNAL_KEY ,
projectId: 'k17a8b9c0d1e2f3g4h5i6j7' ,
path: 'src/components/App.jsx' ,
content: 'export default function App() { ... }'
});
This automatically:
Creates parent directories if they don’t exist
Creates or updates the file
Maintains proper folder hierarchy
Get All Project Files
Retrieve all files in a project (used for export):
const allFiles = await convex . query ( api . system . getAllProjectFiles , {
internalKey: process . env . POLARIS_CONVEX_INTERNAL_KEY ,
projectId: 'k17a8b9c0d1e2f3g4h5i6j7'
});
// Returns array of:
// [{ path: 'src/App.js', content: '...', type: 'file' }, ...]
File Path Resolution
The system API handles path-based operations:
// Path: "src/components/Button.jsx"
// Resolves to:
{
folders : [ 'src' , 'components' ],
fileName : 'Button.jsx' ,
fileId : Id < "files" >
}
GitHub Integration
Import Repository Files
When importing from GitHub via POST /api/github/import:
Repository files are fetched via GitHub API
Each file is created using writeFileByPath
Directory structure is preserved
Binary files are skipped or stored in _storage
Export Project Files
When exporting to GitHub via POST /api/github/export:
All project files are retrieved via getAllProjectFiles
Files are pushed to GitHub via GitHub API
Commit is created with specified message
File Explorer UI
The file explorer in Polaris IDE provides:
Tree view of files and folders
Drag-and-drop file organization
Context menu for file operations
VSCode-style file icons
Auto-save with debouncing
Storage Limits
Polaris IDE uses Convex for file storage. Default Convex limits apply:
Document size: 1MB per document
Storage: Varies by Convex plan
For large files, consider using storageId with Convex storage API
Real-time Updates
All file operations are real-time via Convex:
import { useQuery } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
function FileExplorer ({ projectId }) {
// Automatically re-renders when files change
const files = useQuery ( api . files . getFiles , { projectId });
return < div > { /* Render files */ } </ div > ;
}
Changes are immediately reflected across all connected clients.
Example: Complete File Workflow
import { useMutation , useQuery } from 'convex/react' ;
import { api } from './convex/_generated/api' ;
import { useState } from 'react' ;
function FileManager ({ projectId }) {
const files = useQuery ( api . files . getFiles , { projectId });
const createFile = useMutation ( api . files . create );
const updateFile = useMutation ( api . files . update );
const deleteFile = useMutation ( api . files . deleteFile );
const [ selectedFile , setSelectedFile ] = useState ( null );
const [ content , setContent ] = useState ( '' );
// Create new file
const handleCreate = async () => {
const fileId = await createFile ({
projectId ,
name: 'new-file.js' ,
type: 'file' ,
content: '// Start coding...'
});
setSelectedFile ( fileId );
};
// Save file content
const handleSave = async () => {
if ( selectedFile ) {
await updateFile ({
id: selectedFile ,
content
});
}
};
// Delete file
const handleDelete = async ( fileId ) => {
await deleteFile ({ id: fileId });
if ( selectedFile === fileId ) {
setSelectedFile ( null );
}
};
return (
< div >
< button onClick = { handleCreate } > New File </ button >
{ /* File list */ }
< ul >
{ files ?. map ( file => (
< li key = { file . _id } >
< span onClick = { () => setSelectedFile ( file . _id ) } >
{ file . name }
</ span >
< button onClick = { () => handleDelete ( file . _id ) } > Delete </ button >
</ li >
)) }
</ ul >
{ /* Editor */ }
{ selectedFile && (
< div >
< textarea
value = { content }
onChange = { ( e ) => setContent ( e . target . value ) }
/>
< button onClick = { handleSave } > Save </ button >
</ div >
) }
</ div >
);
}
Next Steps
Projects API Learn about project operations
Messages API Add AI assistance to your files