TS-REST Nav
Navigate between ts-rest contracts, handlers, services, schemas, and client call sites using predictable folder naming under roots you configure.
Minimum requirements
This extension helps when your project already has something like:
- Separate trees for contracts, HTTP handlers, and service logic (or at least distinct filename patterns in shared folders)
- Names that line up across layers — same operation name on the contract, handler export, service function, and client call when possible
- ts-rest with shared contract files that declare your HTTP routes (the extension links each route to its handler, service, and callers)
Optional, but needed for the fuller hop set:
- A stable client API prefix (e.g.
getRestApi().employees.listEmployees())
- A server services namespace in handlers (e.g.
ServerServices.employees.listEmployees)
- Colocated schema files next to contracts (for the Schema lens)
If you don’t use ts-rest or your routes aren’t declared in shared contract files, this won’t add much.
⚠️ GETTING STARTED
Point the extension at your contracts, handlers, and services folders — once those roots are set, CodeLens and navigation light up. We don’t assume a default directory structure, because every repo puts these in different places.
1. Install
Install TS-REST Nav from the VS Code / Cursor marketplace, then reload the window if prompted.
2. Add workspace settings (required)
Add this to your workspace settings (.vscode/settings.json, or the "settings" block in a *.code-workspace file), then reload:
{
"tsrest.nav.contractsRoots": ["shared/api"],
"tsrest.nav.handlersRoot": "server/api",
"tsrest.nav.servicesRoot": "server/services",
"tsrest.nav.contractsGlob": "**/shared/api/**/*.contracts.ts",
"tsrest.nav.handlersGlob": "**/server/api/**/*.{ts,js}",
"tsrest.nav.clientGlobs": [
"**/web/**/*.{ts,tsx}",
"**/mobile/**/*.{ts,tsx}"
],
"tsrest.nav.clientApiExpressions": ["getRestApi()"],
"tsrest.nav.serverServicesExpression": "ServerServices"
}
*Root paths are relative to the repo root (the directory that contains those folders). Include a packages/ prefix only if your folders actually live under packages/.
- Globs are normal VS Code workspace globs.
- Adjust paths/expressions to match your repo.
- Optional: override filename templates with
contractFilePattern, handlerFilePattern, and serviceFilePattern (see below).
3. Confirm it works
Open a *.contracts.ts route key (e.g. listEmployees:). You should see CodeLens: API · Service · Schema · Call sites.
If nothing appears, see Troubleshooting.
What it does
| Location |
CodeLens |
Contract route key listEmployees: |
API · Service · Schema · Call sites |
API listEmployeesApi |
Contract · Service · Call sites |
Service export async function listEmployees |
Contract · API · Call sites |
Client getRestApi().employees.listEmployees() |
Service · API · Contract |
Cmd/Ctrl-click and TS-REST Nav: Go to Related work from the same places.
Prefer matching names across layers (listEmployees everywhere). If a handler calls a differently named service method, the extension warns (Service ⚠ + Problems panel).
More detail: docs/directory-structure.md. Naming across layers: docs/naming.md.
Default filename templates (configurable):
| Layer |
Setting |
Default |
| Contract |
tsrest.nav.contractFilePattern |
{area}.contracts.ts |
| Handler |
tsrest.nav.handlerFilePattern |
{op}.api.ts |
| Service |
tsrest.nav.serviceFilePattern |
{area}.service.ts |
Placeholders: {area} (kebab area id), {op} (kebab operation id). A .js twin is also tried when the pattern ends in .ts.
Flat area (folder name = router area):
{contractsRoot}/[audience]/<area>/<area>.contracts.ts
{handlersRoot}/[audience]/<area>/<op-kebab>.api.ts
{servicesRoot}/<area>/<area>.service.ts
Nested domain group (router key from the contracts file stem):
{contractsRoot}/…/<domain>/get-<area>/<area>.contracts.ts
{handlersRoot}/…/<domain>/<op-kebab>.api.ts
{servicesRoot}/<domain>/<area>.service.ts
[audience] is optional — see tsrest.nav.audiences below. Router key = camelCase of the area (employees → employees). Handlers and services may share one directory if filenames differ.
Settings reference
| Setting |
Default |
Description |
tsrest.nav.contractsRoots |
[] |
Required. Contract tree roots |
tsrest.nav.handlersRoot |
"" |
Required. Handler tree root |
tsrest.nav.servicesRoot |
"" |
Required. Service tree root |
tsrest.nav.contractsGlob |
**/*.contracts.ts |
Contract search fallback |
tsrest.nav.handlersGlob |
**/*.api.ts |
Handler search fallback |
tsrest.nav.clientGlobs |
[] |
Client call-site globs |
tsrest.nav.clientApiExpressions |
getRestApi(), … |
Client call prefixes |
tsrest.nav.serverServicesExpression |
ServerServices |
Handler→service call namespace |
tsrest.nav.audiences |
private/public/internal |
Optional top-level API-surface folders (not feature areas). Set [] if you don’t use them. |
tsrest.nav.contractFilePattern |
{area}.contracts.ts |
Contract filename template |
tsrest.nav.handlerFilePattern |
{op}.api.ts |
Handler filename template |
tsrest.nav.serviceFilePattern |
{area}.service.ts |
Service filename template |
tsrest.nav.warnNameMismatch |
true |
Warn on service/route name mismatch |
tsrest.nav.enabled |
true |
Master switch |
tsrest.nav.showCodeLens |
true |
CodeLens on route keys |
Troubleshooting
CodeLens never appears / status bar says configure roots
- Confirm
tsrest.nav.contractsRoots, handlersRoot, and servicesRoot are set and non-empty.
- Reload the window after changing settings.
Multi-root workspace
- Put
tsrest.nav.* in the *.code-workspace "settings" block.
- A folder’s
.vscode/settings.json only applies to that folder — not sibling roots.
- A workspace file does not inherit root
.vscode/settings.json.
Gitignored *.local.code-workspace
- If you copy a shared workspace file into a personal
*.local.code-workspace, copy the tsrest.nav settings block too. Otherwise lenses disappear with no error.
Develop (extension authors)
npm install
npm test
npm run install-extension # symlinks into ~/.cursor/extensions
Then Developer: Reload Window.
License
MIT