Skip to content

Files API

CRUD over project files. Source: services/api/src/routes/project-files.ts and services/api/src/routes/project-files/.

The filesystem is the source of truth (PROJECTS_ROOT/<projectId>/). The project_files table is a fast-read cache kept in sync by the API.

List

GET /projects/:projectId/files

Returns a flat list:

[
  { path: "package.json", size: 423, updatedAt: "..." },
  { path: "src/App.tsx",  size: 1820, updatedAt: "..." },
  ...
]

Add ?tree=1 for a nested tree response (for the file explorer).

Read

GET /projects/:projectId/files/<path>

Returns the raw file body with Content-Type guessed from the extension. Binary files are streamed as application/octet-stream.

Create / replace

PUT /projects/:projectId/files/<path>
Content-Type: text/plain
<body>

Creates parent directories as needed. The path is always validated to stay within the project root — ../ traversal returns 400 path_traversal.

After write:

  1. The file is staged in the project's Git repo.
  2. The project_files cache is updated.
  3. The WS service rebroadcasts the new content into the collaboration room.

Delete

DELETE /projects/:projectId/files/<path>

Bulk operations

POST /projects/:projectId/files/bulk
{
  "operations": [
    { "op": "write",  "path": "src/a.ts", "content": "..." },
    { "op": "rename", "from": "src/b.ts", "to": "src/c.ts" },
    { "op": "delete", "path": "old.ts" }
  ]
}

Atomic per file; the whole batch is committed once.

GET /projects/:projectId/files/search?q=<regex>&maxResults=50

Server-side grep confined to the project root.

Live preview integration

When a file is written, the project's Vite dev server picks up the change via filesystem watching → HMR → instant browser update. The frontend iframe listens for HMR events and surfaces build errors in a banner.

Permissions

Identical to the parent project: read-only for visibility = public, full access for workspace members.

See also