Skip to content

Integrating into an Existing Project ​

The scaffold produces a standalone project that starts as-is. When the target is a repository that already has a server, a directory structure and a workspace, the more common approach is not to move the entire generated tree in, but to take only the parts you need.

--integrate exists for exactly this:

bash
pnpm create tealina my-app --integrate

It writes INTEGRATION.md at the project root. That file is not part of the runtime and nothing imports it; it is a merge playbook written for AI assistants, and you can delete it once the merge is done. The rest of this page describes the two shapes it covers and a few easy-to-miss details.

Two Shapes ​

ShapeCopyLeave in Place
A. Mounting onto an Existing Servicebackend/core, backend/api-v1backend/server, packages/web, root scripts
B. Merging into an Existing Monorepobackend/core, backend/api-v1, backend/serverpackages/web (unless needed)

A. Mounting onto an Existing Service ​

Use this when the target repository already has an Express/Fastify/Koa application, its own entry point and its own deployment. Copy core and api-v1, and call mount once on the existing app:

ts
import { mount } from '<api>/mount'

const source = await mount(app)

mount installs that version's routes, body parsers and JSON documentation route; the returned TealinaVdocSource is only needed by the documentation page. The documentation page is optional: copy the generated project's server/src/doc.ts and public/ over together, or skip it entirely and keep only <API_BASE_URL>/_doc/api.json.

backend/server is not something to copy. It shows how a host should wire things up; just borrow the approach from its app.ts.

B. Merging into an Existing Monorepo ​

Copy the three backend packages into the target repository, placing them under backend/ or apps/ according to existing convention. Then:

  • Add the directories to the workspace globs, and merge allowBuilds, catalog and overrides from pnpm-workspace.yaml rather than replacing the whole file.
  • Merge the root scripts rather than overwriting them. dev must build core first and then api-v1: the host imports their dist/.
  • Only if the target repository already uses Vite+, merge the fmt/lint configuration from the root vite.config.ts.
  • Do not overwrite the target repository's README.md or AGENTS.md with the scaffold's same-named files; only extract the api-v1 rules into them.

If the target is not Express/Fastify/Koa, stop and reconsider. core/types/alias.d.ts binds the request/response types of those three frameworks; switching frameworks means adding a new alias, not a mechanical migration.

Renaming ​

core, api-v1 and server are generic names, and the target repository usually already uses them. When renaming, beyond package.json and bare imports in the source, there are two places where type checking cannot see the problem:

  1. handlerImport in tealina.config.ts.
  2. The strings returned by generateTsCode / generateJsCode in the same file. They determine which package newly written handlers import after create; if you miss them, the next endpoint you add breaks, with no error in between.

The fixed artifact names do not change with the rename: types/api.d.ts, exports["./api"], apiDir.

Builds and Generated Artifacts ​

The import conditions of core's exports["./access"] and api-v1's exports["./mount"] point at dist/, so the host must build these two packages before importing them (in the order core → api-v1).

Do not merge generated artifacts by hand:

FileGenerated by
api-v1/src/api/index.*, api-v1/src/api/<method>/index.*align
api-v1/docs/api.jsongdoc

Both commands support --check (compare only, exit code 1 when stale) and --json. Wire --check into the target repository's CI; otherwise, when the docs go stale the page still renders, just with old content.

Verification Checklist ​

At the target repository root, replace <core> and <api> with the renamed package names:

bash
pnpm install
pnpm -F <core> build
pnpm -F <api> build

pnpm -F <api> align --check
pnpm -F <api> gdoc --check
# if either exits 1
pnpm -F <api> align
pnpm -F <api> gdoc

Then start the target service and confirm, in order:

  1. curl -i <API_BASE_URL>/health returns 200.
  2. A protected endpoint returns 401 without Authorization — the scaffold's guard is a placeholder; replace core/src/access/verifyToken.ts.
  3. curl <API_BASE_URL>/_doc/api.json returns the documentation.
  4. If the documentation page is wired up, /api-doc/index.html opens.
  5. The frontend still type-checks against <api>/api.

The scaffold's get/health, post/login and post/article are examples. Once step 1 passes, delete the endpoints you do not need and rerun align and gdoc.

Where It Fails Silently ​

  • Copied the packages but did not build them. core/access and api-v1/mount resolve to dist/ at runtime; when missing, you get ERR_MODULE_NOT_FOUND, while type checking stays green.
  • handlerImport or the stub strings still say the old core. Type checking passes, and the next create writes a broken import.
  • Edited a generated barrel by hand. The next align silently overwrites it.
  • Changed a handler but did not run gdoc. The documentation page renders as usual, just with stale content.
  • Two routes claim the same prefix. Requests land on the one registered first, and the other returns 404, while both sides type-check.
  • The target framework's major version differs from the template's. alias.d.ts may still compile, but it describes a different set of req/res; compare the installed types, not the version number.
  • Node < 22. Failures usually show up in a dependency's build or at runtime, not in type checking.

The full step-by-step playbook (copy scope for both shapes, rename search commands, mount examples for each framework) lives in the generated project's INTEGRATION.md, ready to hand to the AI assistant performing the merge.