Skip to main content

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:
JavaScript
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:
JavaScript
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:
JavaScript
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):
JavaScript
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:
JavaScript
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:
  1. Repository files are fetched via GitHub API
  2. Each file is created using writeFileByPath
  3. Directory structure is preserved
  4. Binary files are skipped or stored in _storage

Export Project Files

When exporting to GitHub via POST /api/github/export:
  1. All project files are retrieved via getAllProjectFiles
  2. Files are pushed to GitHub via GitHub API
  3. 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:
JavaScript
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

Complete Example
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

Build docs developers (and LLMs) love