---
title: Pulse Routing | Vibecodr Docs
description: Map server files to same-origin /api routes and learn how routing decisions influence deployment and invocation behavior.
canonical: https://vibecodr.space/docs/pulse-routing
---

# Pulse Routing

Pulse routing defines how source files map to same-origin /api routes and how those routes are exposed to runtime callers, embeds, and automation surfaces.

Marker: Route contract reference for mapping Pulse files to API endpoints.

## Implementation focus

Use this page when creating or refactoring server routes so call paths and deployment behavior remain predictable.

## Expected outcomes

- Map source layout to stable /api route contracts.
- Avoid route collisions during iterative releases.
- Preserve clear ownership across handler files and surfaces.

## The src/server/ (and server/) Convention

Vibecodr uses a file-based routing convention. TypeScript and JavaScript files inside `src/server/` (preferred) or `server/` publish as API endpoints when the project ships as a Pulse or Combo.

Mental model: `src/server/` tells Vibecodr what should run as backend code, while `/api/*` is the URL surface people and browsers actually call.

The same convention applies to GitHub and ZIP imports. Framework apps like Vite + Svelte can keep their frontend source and add `src/server/` endpoints. Frontend previews in Studio; server files deploy as `/api/*` routes when you publish. Built-only archives such as `dist/` do not contain enough source to create backend routes.

### How to deploy a Pulse (serverless compute)

1. Add backend function files to `src/server/` (preferred) or `server/`.

2. Verify file-to-route mapping (`src/server/foo.ts -> /api/foo`).

3. Publish from [Studio](/studio); Vibecodr bundles and deploys the Pulse runtime automatically.

4. Blueprint starters open in Studio as Pulse source with setup tasks, not as browser preview apps. Finish the checklist, publish, then test the deployed endpoint.

5. PulseDescriptor normalizes Pulse setup metadata internally; simple one-file handlers remain simple, and setup UI, examples, local replay, API/OpenAPI, MCP, CLI, and deployment validation read the normalized descriptor projection.

6. For standalone automations and webhooks, use the [Pulse Operating Center](/pulses?tab=automations) or go straight to [Pulse creation](/pulses/new).

### Vibes (Browser Code)

Files outside `src/server/` run in the browser.

src/App.tsx, index.html

### Pulses (Server Code)

Files inside `src/server/` (or `server/`) become API endpoints.

src/server/weather.ts -> /api/weather

## File Path to Route Mapping

Your file path determines your API route. Here's how the transformation works:

File Path

API Route

Notes

src/server/weather.ts

/api/weather

Basic route

src/server/index.ts

/api

Root endpoint

src/server/users/index.ts

/api/users

index.ts = folder route

src/server/users/[id].ts

/api/users/:id

Dynamic parameter

src/server/webhooks/stripe.ts

/api/webhooks/stripe

Nested paths

server/weather.ts

/api/weather

Legacy path (also supported)

Supported extensions: `.ts`, `.js`, `.tsx`, `.jsx`. Other files (like `.json` or `.md`) in `src/server/` (or `server/`) are ignored. PHP and WordPress are not supported inside vibes or Pulses; `.php` files, WordPress themes/plugins, and WordPress admin routes are treated as unsupported source or scanner traffic.

## Dynamic Route Parameters

Use square brackets `[param]` in filenames to create dynamic route segments. These become `:param` in the URL and are accessible in your handler.

src/server/users/[id].ts -> /api/users/:id

```typescript
// File: src/server/users/[id].ts
// Route: /api/users/:id
import { definePulse } from "@vibecodr/pulse";

export default definePulse(async (input, env) => {
  // Access the dynamic parameter from the request URL
  const url = new URL(env.request.url);
  const id = url.pathname.split("/").pop();  // "123" from /api/users/123

  return { userId: id, message: "Found user" };
});
```

`[id].ts` matches `/api/users/123`, `/api/users/abc`, etc.

`[...slug].ts` catch-all routes are not currently supported

## Project Types

Vibecodr automatically detects your project type based on file structure:

V

### Vibe

Browser-only. No `src/server/` (or `server/`) files.

Slot cost: 0 (unlimited)

P

### Pulse

Server-only. Has `src/server/` (or `server/`) files, no frontend entry.

Slot cost: 1 deployment slot

C

### Combo

Full-stack. Frontend entry + `src/server/` (or `server/`) files.

Slot cost: 1 deployment slot

Deployment slots limit how many pulse/combo projects you can have deployed at once. Free: 3 slots, Creator: 15 slots, Pro: 50 slots.

## Creating Pulse Files

In Studio, create endpoints by adding a file under `src/server/` (or `server/`). Use Configure → API → New endpoint, or right-click the folder in the file tree.

### New API endpoint

This creates a new endpoint file with boilerplate code and shows the derived `/api/*` route.

Default pulse template

```typescript
// src/server/my-endpoint.ts
// Route: /api/my-endpoint
import { definePulse } from "@vibecodr/pulse";

export default definePulse(async (input, env) => {
  // Your logic here
  return { message: "Hello from my-endpoint!" };
});
```

Advanced SQL compatibility is exposed as `env.db` on eligible plans. Use that surface only when you truly need SQL, treat it as advanced compatibility rather than the default Pulse backend model, and do not assume automatic Pulse State migration.

## Handler Styles (Recommended)

We recommend using the `definePulse` helper to explicitly set your handler style. This ensures your code runs exactly as expected, even if you use default parameters or specialized bundlers.

### Enhanced Style

Receives parsed input and Pulse backend capabilities. Best for business logic, policy fetch, connected accounts, and structured logs.

Enhanced handler

```typescript
import { definePulse } from "@vibecodr/pulse";

export default definePulse(async (input, env) => {
  env.log.info("pulse.received", {
    method: env.request.method,
    hasName: typeof input?.name === "string",
  });

  return {
    greeting: `Hello ${typeof input?.name === "string" ? input.name : "there"}`,
    region: env.pulse.DEFAULT_REGION ?? "global",
  };
});
```

### Web-Standard Style

Receives a standard Request and returns a Response. Best for proxying, streaming, or full control over HTTP headers.

Web-standard handler

```typescript
import { defineWebPulse } from "@vibecodr/pulse";

export default defineWebPulse(async (req) => {
  const body = await req.json();
  return Response.json({ echo: body });
});
```

## Legacy: Auto-detection

If you don't use the `definePulse` helper, the runtime guesses the style based on your function's argument count. This works for basic cases but can be brittle.

```typescript
// Enhanced style: 2 arguments (input, env)
export default async function(input, env) { ... }

// Web-standard style: 1 argument (request)
export default async function(request) { ... }
```

Warning: Footguns

- Default parameters (e.g., `(input, env =)`) change the argument count and may cause your handler to run in the wrong mode.

- Rest parameters (e.g., `(...args)`) report 0 arguments and will default to "web" style.

- Wrappers (e.g., `withAuth(handler)`) can hide the true argument count.

Fix: Always use `definePulse()` or `defineWebPulse()` to avoid these issues.

## Deployment Flow

When you publish a project with pulse files, here's what happens:

1

Project type detection

Vibecodr scans for `src/server/` (or `server/`) files and classifies as Vibe, Pulse, or Combo.

2

Slot check

If pulse/combo, checks if you have available deployment slots.

3

Bundle & deploy

Your code is bundled with the Vibecodr runtime and deployed automatically by Vibecodr (no manual deploy wiring).

4

Verification

Vibecodr verifies the deployed endpoint responds before marking the Pulse active.

5

Status update

Pulse status set to "active" (success) or "failed" (error).

## Troubleshooting

### Pulse not detected

- Make sure your file is inside `src/server/` (or `server/`)

- Check file extension is `.ts`, `.js`, `.tsx`, or `.jsx`

- Verify you have a default export function

### Deployment failed

- Check Studio console for error messages

- Verify no syntax errors in your pulse code

- Ensure you haven't exceeded your deployment slot limit

### Route not working

- Check the pulse status is "active" in Studio

- Verify the route matches your file path (see mapping table above)

- Try re-publishing the project

## Best Practices

### Routing Guidelines

- Use `index.ts` for collection endpoints (`/api/users`)

- Use `[id].ts` for item endpoints (`/api/users/:id`)

- Group related endpoints in folders (`src/server/webhooks/`)

- Keep file names lowercase with hyphens (`process-order.ts`)

- Avoid deeply nested routes - 2-3 levels is usually enough

- Use the Enhanced handler style for access to `env.pulse`, `env.secrets`, `env.connections`, `env.fetch`, `env.log`, and `env.request`.

## Structured route body

### File-to-route mapping

Pulse routing maps server-side source files to same-origin /api endpoints. The route shape should be predictable enough that vibe code, embeds, docs, and automation can call it without guessing where behavior lives.

Route collisions and unclear ownership become production bugs quickly. Name endpoints around behavior and keep handler files close to the capability they own.

- Keep Pulse routes stable once public callers depend on them.
- Avoid overlapping route names with different auth expectations.
- Use `.ts`, `.js`, `.tsx`, or `.jsx` for Pulse route files; PHP and WordPress are not supported inside vibes or Pulses.
- Document payload shape and error behavior for each public endpoint.

### Exposure model

A deployed Pulse endpoint can be public HTTP while the source remains private. Those are different visibility planes. Do not rely on source privacy as endpoint access control.

If a route should be owner-only, signed, or session-gated, enforce that in the handler.

- Public callable does not mean public source.
- Public source does not mean safe to expose secrets.
- Runtime routing should not leak platform capabilities, deployment details, or logs.

### Example and read next

Example: server/contact.ts should become a stable same-origin route such as /api/contact. Keep route names intentional, avoid collisions, and update callers when the route contract changes.

Use these related pages when you need the next layer of guidance. They point to the most likely follow-up tasks, not every page that happens to touch the same system.

- Read next: [Pulse SDK & Handlers](/docs/handlers)
- Read next: [Calling Pulses](/docs/calling-pulses)
- Read next: [Vibes & Pulses](/docs/vibes-pulses)
- Read next: [How-To Guides](/docs/how-to)

## Related documentation

- [/docs/handlers](https://vibecodr.space/docs/handlers/index.md)
- [/docs/calling-pulses](https://vibecodr.space/docs/calling-pulses/index.md)
- [/docs/vibes-pulses](https://vibecodr.space/docs/vibes-pulses/index.md)
- [/docs/how-to](https://vibecodr.space/docs/how-to/index.md)