Skip to content

File-based Routing ​

Every file under apiDir is a route. The first directory level is the HTTP method, and the file path is the URL; there is no central file where routes are registered.

text
src/api/
  index.ts                 generated by align
  get/
    index.ts               generated by align
    health.ts              GET /health
    user/
      [id].ts              GET /user/:id
  post/
    index.ts               generated by align
    user/
      login.ts             POST /user/login
  • [id] denotes a route parameter, written as :id at runtime.
  • Handler files must export default.
  • types/handler.d.ts currently provides type projections for four methods: get, post, patch and delete. The runtime itself only expands the keys of a method directory, so directories for other methods are still registered.

Generated Barrel Files ​

Each method directory has an index.*, and the API root has another one that aggregates all methods:

ts
// src/api/get/index.ts
// @generated by tealina — do not edit.
// Rebuild it with: tealina align

export default {
  health: import('./health.js'),
  'user/:id': import('./user/[id].js'),
}
ts
// src/api/index.ts
export default {
  get: import('./get/index.js'),
  post: import('./post/index.js'),
}

The keys are logical paths: no leading slash. @tealina/server prepends / when registering routes.

These index.* files are generated artifacts; never edit them by hand. The // @generated by tealina — do not edit. line at the top of the file is the marker. After adding, renaming or deleting a handler, run align once.

Commands ​

create ​

bash
tealina create post/user       # writes src/api/post/user.ts
tealina create user/login      # defaults to post when no method is given
tealina create post/user/:id   # writes [id].ts

create also updates the barrel files touched by this run. A route segment may be written as :id or [id]; on disk it is normalized to [id].

delete ​

bash
tealina delete post/user/login
tealina delete post/user/login --with-test

Deletes the handler and rewrites the barrel files. --with-test also deletes the generated integration test file.

align ​

Rebuilds every generated index.* and the fallback types/api.d.ts from the files actually present on disk. After moving or renaming files by hand, this is the command you must run.

bash
tealina align
tealina align --check          # compare only, write nothing; exit code 1 on differences
tealina align --check --json   # write a JSON object to stdout

The shape of --json:

json
{
  "ok": false,
  "changed": [{ "action": "update", "file": "src/api/get/index.ts" }]
}

--check is suitable for CI; it runs the same generation logic as a normal run and only stops before writing.

Excluding Files ​

Patterns in exclude are relative to apiDir and matched with POSIX path rules. align already skips index files and dotfiles; this option is for other files you want to keep next to the routes without turning them into endpoints:

ts
export default defineConfig({
  apiDir: 'src/api',
  apiBaseURL: '/api/v1',
  typesDir: 'types',
  exclude: ['_shared/**', '**/*.test.ts'],
})

URL Prefix ​

apiBaseURL is the prefix under which the routes answer; the CLI never infers it from directory names. In the scaffold it comes from API_BASE_URL in version.js, and at least four places read it:

  • tealina.config.ts, which passes it to gdoc;
  • src/mount.ts, which decides where routes are mounted;
  • src/doc.ts, which decides the URL of the documentation JSON file;
  • the frontend's api/client.ts, which decides request URLs and the Vite proxy.

When changing the prefix, change version.js; do not change all four places separately.

Exit Codes ​

align and gdoc exit 0 on success; --check exits 1 when a generated artifact is stale, without writing files. Error messages go to stderr, and the --json object goes to stdout, so it can safely be piped into other tools.