Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ _.log

/.agents/
/.claude/
CLAUDE.local.md
CLAUDE.local.md

.env
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ CodeLens links appear above HTTP client calls like `client.get('/items')`, letti
| Setting | Description | Default |
|---------|-------------|---------|
| `fastapi.entryPoint` | Path to the main FastAPI application file (e.g., `src/main.py`). If not set, the extension searches common locations: `main.py`, `app/main.py`, `api/main.py`, `src/main.py`, `backend/app/main.py`. | `""` (auto-detect) |
| `fastapi.showTestCodeLenses` | Show CodeLens links above test client calls (e.g., `client.get('/items')`) to navigate to the corresponding route definition. | `true` |
| `fastapi.codeLens.enabled` | Show CodeLens links above test client calls (e.g., `client.get('/items')`) to navigate to the corresponding route definition. | `true` |
| `fastapi.telemetry.enabled` | Send anonymous usage data to help improve the extension. See [TELEMETRY.md](TELEMETRY.md) for details on what is collected. | `true` |

**Note:** Currently the extension discovers one FastAPI app per workspace folder. If you have multiple apps, use separate workspace folders or configure `fastapi.entryPoint` to point to your primary app.

## Data and telemetry

The FastAPI extension collects anonymous usage data and sends it to FastAPI to help improve the extension. You can disable telemetry by setting `fastapi.telemetry.enabled` to `false`. Read our [TELEMETRY.md](TELEMETRY.md) for details on what we collect and what we don't.

## License
MIT

MIT
61 changes: 61 additions & 0 deletions TELEMETRY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Telemetry

The FastAPI VS Code extension collects anonymous usage data to help us understand how the extension is used and how we can improve it. This document describes exactly what data is collected. No personally identifiable information is collected. No information is shared with third parties.

## How to disable telemetry

You can disable telemetry in two ways:

### Option 1: Disable all VS Code telemetry

1. Open VS Code Settings (File > Preferences > Settings, or `Cmd+,` on macOS)
2. Search for `telemetry.telemetryLevel`
3. Set it to `off`

This disables telemetry for VS Code and all extensions that respect this setting, including FastAPI.

### Option 2: Disable only FastAPI telemetry

1. Open VS Code Settings
2. Search for `fastapi.telemetry.enabled`
3. Uncheck the box (set to `false`)

This disables only the FastAPI extension's telemetry while leaving other telemetry unchanged.

**Note:** Telemetry is only sent when *both* VS Code's global telemetry (`telemetry.telemetryLevel`) is enabled *and* the extension setting (`fastapi.telemetry.enabled`) is `true`. Disabling either one will stop all telemetry collection.

## What we collect

We collect anonymous usage metrics to improve the extension. We do **not** collect:
- File paths or file contents
- Route paths or endpoint names
- Any code from your project
- IP addresses (geo-IP is disabled)

**Note:** All events include contextual information: client type (VS Code, Cursor, etc.), OS platform, CPU architecture, extension version, and if available, the installed Python version and versions of related packages (FastAPI, Pydantic, Starlette, Typer, FastAPI CLI, FastAPI Cloud CLI) from your active interpreter.

### Events

| Event | Data | Why |
|-------|------|-----|
| Extension activated | Activation duration, success/failure, number of routes/routers/apps discovered, workspace folder count | Helps us understand startup performance, project sizes, and environment compatibility |
| Extension deactivated | Session duration (time from activation to deactivation) | Helps us understand how long users keep VS Code open with the extension active |
| Activation failed | Error category (e.g., "parse_error", "wasm_load_error"), failure stage | Helps us debug issues users encounter |
| Entrypoint detected | Detection duration, method used (config/pyproject/heuristic), success/failure, routes and routers count | Helps us understand which detection methods work best |
| Tree view visible | _(none)_ | Know if users see the endpoint explorer |
| Search executed | Number of results, whether user selected a result | Helps us understand search usage |
| CodeLens provided | Number of test calls found, number matched to routes | Helps us understand CodeLens effectiveness |
| Routes navigated | Count of navigations (cumulative) | Helps us understand feature usage depth |
| Routes copied | Count of copies (cumulative) | Helps us understand feature usage depth |
| CodeLens clicked | Count of clicks (cumulative) | Helps us understand feature usage depth |

### Identifiers

- A random UUID is generated and stored locally in VS Code's extension storage
- This ID is used solely to count unique users and is not linked to any personal information

## Source code

The telemetry implementation is fully open source. See:
- [src/utils/telemetry/](src/utils/telemetry/) - All telemetry code
- [src/utils/telemetry/events.ts](src/utils/telemetry/events.ts) - Event definitions
5 changes: 5 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import esbuild from "esbuild"
const production = process.argv.includes("--production")
const watch = process.argv.includes("--watch")

const POSTHOG_API_KEY = "phc_s0Qx8NxueJvnqe4YE7NEKYNosJr8aZ81tIByuzm464X"

function copyWasmFiles() {
const wasmDestDir = path.join(import.meta.dirname, "dist", "wasm")
mkdirSync(wasmDestDir, { recursive: true })
Expand Down Expand Up @@ -44,6 +46,9 @@ async function main() {
logLevel: "info",
define: {
"process.env.NODE_ENV": production ? '"production"' : '"development"',
"process.env.POSTHOG_API_KEY": production
? JSON.stringify(POSTHOG_API_KEY)
: '""',
__DIST_ROOT__: JSON.stringify(path.join(import.meta.dirname, "dist")),
},
}
Expand Down Expand Up @@ -74,7 +79,16 @@ async function main() {
},
// vscode is provided by the runtime; web-tree-sitter is bundled but
// internally references these Node.js modules for environment detection
external: ["vscode", "fs/promises", "module"],
// posthog-node uses Node.js APIs, so telemetry is disabled in browser
// util and child_process are used for version detection but not in browser
external: [
"vscode",
"fs/promises",
"module",
"posthog-node",
"util",
"child_process",
],
})

if (watch) {
Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,17 @@
"scope": "resource",
"description": "Path to the main FastAPI application file (e.g., 'src/main.py'). If not set, the extension will search common locations."
},
"fastapi.showTestCodeLenses": {
"fastapi.codeLens.enabled": {
"type": "boolean",
"default": true,
"scope": "resource",
"description": "Show CodeLens links above test client calls (e.g., client.get('/items')) to navigate to the corresponding route definition."
},
"fastapi.telemetry.enabled": {
"type": "boolean",
"default": true,
"scope": "machine",
"description": "Enable telemetry to help improve the extension. No personal data is collected."
}
}
}
Expand Down Expand Up @@ -238,6 +244,7 @@
"typescript": "^5.0.0"
},
"dependencies": {
"posthog-node": "^5.24.1",
"toml": "^3.0.0",
"web-tree-sitter": "^0.26.3"
},
Expand Down
24 changes: 23 additions & 1 deletion src/appDiscovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { routerNodeToAppDefinition } from "./core/transformer"
import type { AppDefinition } from "./core/types"
import { vscodeFileSystem } from "./providers/vscodeFileSystem"
import { log } from "./utils/logger"
import {
countRouters,
countRoutes,
createTimer,
trackEntrypointDetected,
} from "./utils/telemetry"

export type { EntryPoint }

Expand Down Expand Up @@ -105,6 +111,9 @@ export async function discoverFastAPIApps(
const apps: AppDefinition[] = []

for (const folder of workspaceFolders) {
const folderTimer = createTimer()
let detectionMethod: "config" | "pyproject" | "heuristic" = "heuristic"
const folderApps: AppDefinition[] = []
const config = vscode.workspace.getConfiguration("fastapi", folder.uri)
const customEntryPoint = config.get<string>("entryPoint")

Expand All @@ -126,14 +135,17 @@ export async function discoverFastAPIApps(

log(`Using custom entry point: ${customEntryPoint}`)
candidates = [{ filePath: entryUri.toString() }]
detectionMethod = "config"
} else {
// Otherwise, check pyproject.toml or auto-detect
const pyprojectEntry = await parsePyprojectForEntryPoint(folder.uri)
if (pyprojectEntry) {
candidates = [pyprojectEntry]
detectionMethod = "pyproject"
} else {
const detected = await automaticDetectEntryPoints(folder)
candidates = detected.map((filePath) => ({ filePath }))
detectionMethod = "heuristic"
log(
`Found ${candidates.length} candidate entry file(s) in ${folder.name}`,
)
Expand Down Expand Up @@ -174,10 +186,20 @@ export async function discoverFastAPIApps(
log(
`Found FastAPI app "${app.name}" with ${totalRoutes} route(s) in ${app.routers.length} router(s)`,
)
folderApps.push(app)
apps.push(app)
break
break // TODO: Only use first successful app per workspace folder, for now
}
}

// Track entrypoint detection per workspace folder
trackEntrypointDetected({
duration_ms: folderTimer(),
method: detectionMethod,
success: folderApps.length > 0,
routes_count: countRoutes(folderApps),
routers_count: countRouters(folderApps),
})
}

if (apps.length === 0) {
Expand Down
Loading