Create Ts.ED Platform Adapter
Build the adapter around Ts.ED's HTTP abstractions, not by copying an existing implementation wholesale. Preserve the target framework's lifecycle and native request/response model.
Read the adapter reference before designing or implementing. It summarizes the repository's Express, Koa, and Fastify patterns and the PlatformAdapter contract.
1. Establish feasibility and scope
- Confirm the target runtime can support Ts.ED routing, async middleware, status/headers/cookies, streams or equivalent bodies, error propagation, and a testable HTTP entry point.
- Identify whether it is Node
IncomingMessage/ServerResponse compatible. For Fetch-native runtimes, explicitly design the bridge instead of pretending Node response mutation works.
- Record framework-version support, required plugins, and unsupported Ts.ED features. Do not silently omit multipart uploads, static assets, or serverless callback support.
- Do not start or apply an OpenSpec workflow from this skill. The workflow below is the source of truth for the adapter implementation.
2. Inspect local precedents
Read these sources before creating files:
packages/platform/platform-http/src/common/services/PlatformAdapter.ts
packages/platform/platform-express/src/components/PlatformExpress.ts
packages/platform/platform-koa/src/components/PlatformKoa.ts
packages/platform/platform-fastify/src/components/PlatformFastify.ts
- Each package's
src/services/, src/utils/convertPath.ts, src/index.ts, package manifest, Vitest config, and focused specs.
packages/platform/platform-express/test/platform-express.spec.ts, packages/platform/platform-koa/test/platform-koa.spec.ts, and packages/platform/platform-fastify/test/platform-fastify.spec.ts.
docs/introduction/capabilities.md, docs/docs/configuration/{express,koa,fastify}.md, docs/docs/configuration/index.md, docs/index.md, and docs/.vitepress/config.mts.
Choose the closest precedent by execution model: Express for Connect-style middleware, Koa for composed async middleware, Fastify for plugin/route-registration APIs. Reuse intent and coverage, not framework-specific calls.
3. Create the package
Create packages/platform/platform-<framework>/ using a comparable package as a structural template. Adapt, do not blindly duplicate:
package.json: ESM exports, build/test/barrel scripts, framework peer dependency, Ts.ED peer dependencies, and only runtime dependencies required by the adapter.
tsconfig.esm.json, vitest.config.mts, .npmignore, and package readme.md.
src/index.ts: export only the supported public adapter class, settings, decorators, and helpers.
test/app/: a minimal server, framework-plugin setup, and shared integration fixtures compatible with PlatformTestSdk.
test/platform-<framework>.spec.ts: the explicit @tsed/platform-test-sdk integration capability matrix. This file is mandatory for every new platform; a route-conversion or unit-only test suite is not a substitute.
Keep the framework itself a peer dependency; put it in devDependencies at a pinned test version. The workspace glob already discovers packages/platform/*; only update other package catalogues if a real registry, docs, or release surface needs the entry.
For vitest.config.mts, use the workspace preset import with this explicit type-resolution exception:
// @ts-ignore
import {presets} from "@tsed/vitest/presets";
Add the new public package alias to both tsconfig.node.json and tsconfig.spec.json. For Fetch-native platforms with native FormData parsing, prefer that API in the platform package and document Multer decorators and storage engines as unsupported; do not create a @tsed/platform-multer/<framework> alias unless the Multer middleware contract is proven compatible.
4. Implement the adaptations
Create Platform<Framework> extends PlatformAdapter<App> with:
NAME, static create() and bootstrap() through PlatformBuilder.
createApp() returning the framework app and a valid request callback. If the framework cannot yield a Node callback, implement and document the appropriate server/runtime boundary.
useContext() that creates a PlatformContext, awaits $ctx.start(), makes it available to mapped handlers, and reliably calls $ctx.finish() when the response completes.
mapLayers() translating PlatformLayer methods, paths, wildcard parameters, statics, and handler ordering to the framework router.
mapHandler() that runs in the Ts.ED DI context, captures async errors, and preserves error-middleware and response-function semantics.
bodyParser(), statics(), server/listening hooks, and framework-specific lifecycle hooks where the base contract requires them.
adapter() bindings for the framework-specific PlatformRequest, PlatformResponse, and PlatformHandler implementations.
Do not expose a framework's raw request/response directly to controllers without adapters. Map all semantics needed by PlatformRequest and PlatformResponse: URL and query, params, protocol and host, cookies/session, status, headers, redirect, body serialization, file/download, stream, and completion state.
5. Handle framework boundaries explicitly
- Convert Ts.ED route syntax with a framework-specific
convertPath() and test named, optional, regexp, and wildcard paths.
- Preserve the framework's error path. Do not call a Connect
next(error) from a Koa/Fastify/Fetch handler unless the target framework actually supports it.
- Route errors through
PlatformExceptions.catch(error, $ctx) and route unknown paths through PlatformExceptions.resourceNotFound($ctx). Reuse the existing exception filters; do not handcraft framework-specific error or 404 response payloads.
- Register body parsing, multipart support, static serving, view rendering, and raw-body capture only when supported. Make plugin registration awaitable when required. Match
PlatformBuilder's global rawBody policy: capture the raw buffer only when rawBody is configured or @RawBodyParams() is detected, while preserving the framework-parsed body. For Fetch-native runtimes, capture it before parsing with request.clone() only when request.body exists; do not globally disable parsing with a per-route parse: "none" strategy. Fall back to body when no raw capture is active.
- Make HTTP/HTTPS creation and
listen() match the framework's ownership model. Confirm the callback works with serverless-http-style consumers if the adapter claims serverless support.
- Keep framework module augmentation and Ts.ED global declarations narrow and public.
6. Build and run the integration matrix
After the package and platform adaptations exist, add focused unit tests and create test/platform-<framework>.spec.ts with PlatformTestSdk.create({rootDir, adapter, server}). Build the spec from the Express, Koa, and Fastify precedents, then run it before documenting the platform as supported.
- Use Express and Koa as the implementation baseline.
- Use Fastify only to understand framework-specific integration differences; do not copy its skips as an escape hatch.
- Add the
test/app/ fixtures needed by the SDK instead of omitting a scenario.
The following SDK groups are mandatory for every new platform. Keep them enabled, make them pass, and do not wrap them in describe.skip:
handlers, childrenControllers, inheritanceController, response, stream, middlewares, scopeRequest, headers, acceptMime, headerParams, pathParams, queryParams, bodyParams, cookies, session, location, redirect, errors, responseFilter, routing, locals, auth, module, and cache.
view, statics, deepQueryParams, and custom404.
For Fetch-native platforms, test multipart independently with native FormData and File body parameters. Do not add the SDK Multer suite when Multer's middleware contract is incompatible; document that Multer decorators and storage engines are unavailable.
Iterate on the adapter until the mandatory suite passes. Tackle isolated SDK gaps first (for example custom 404, deep query parsing, framework plugins, and test fixtures). Defer structural Fetch-native response work—stream bridging, response finalization, and Node callback emulation—until those simpler gaps are resolved. Then add coverage for adapter-specific behavior not supplied by the SDK, including bootstrap/create, native callback behavior, route conversion, error propagation, raw body, native multipart, and runtime/server lifecycle.
Update all public documentation in the same change:
- Add the framework column and supported/unsupported states to both feature and plugin tables in
docs/introduction/capabilities.md; explain limitations below the tables when a marker needs context.
- Create
docs/docs/configuration/<framework>.md, modeled on the closest adapter page. Cover installation, framework settings/plugins, static files, and use of a custom application instance.
- Link the new page from
docs/docs/configuration/index.md and add it to the Configuration sidebar in docs/.vitepress/config.mts.
- Add the platform and configuration-page link to
docs/index.md wherever the existing Express/Koa/Fastify list appears.
Follow this order: create the package; implement the platform adaptations; add the SDK matrix and its fixtures; execute the matrix; iterate on missing framework features until it passes; then run the package tests, yarn tsc -b from the repository root, affected lint checks, and the documentation build. Do not use a package-scoped tsc --build command.
Completion checklist
- Every mandatory
PlatformTestSdk group is enabled and passing; none is skipped.
- The complete
test/platform-<framework>.spec.ts matrix, checked against the Express, Koa, and Fastify precedents, exists and has been executed successfully.
- Framework capability gaps are documented in
capabilities.md, the platform configuration page, navigation, and homepage.
PlatformAdapter lifecycle, request/response/handler bindings, and route conversion are covered.
- Public package metadata, exports, peer dependencies, docs, and integration tests are complete.
- No Express/Koa/Fastify assumptions remain in the new adapter.
1---2name: create-platform-adapter3description: Create a production-ready Ts.ED platform adapter for a new HTTP framework or runtime, such as Hono, Elysia, Bun.serve, or a Node framework. Use when adding an @tsed/platform-* package, porting controller and middleware support to another server framework, or assessing whether a framework can satisfy Ts.ED's platform contract.4---56# Create Ts.ED Platform Adapter78Build the adapter around Ts.ED's HTTP abstractions, not by copying an existing implementation wholesale. Preserve the target framework's lifecycle and native request/response model.910Read [the adapter reference](references/adapter-reference.md) before designing or implementing. It summarizes the repository's Express, Koa, and Fastify patterns and the `PlatformAdapter` contract.1112## 1. Establish feasibility and scope13141. Confirm the target runtime can support Ts.ED routing, async middleware, status/headers/cookies, streams or equivalent bodies, error propagation, and a testable HTTP entry point.152. Identify whether it is Node `IncomingMessage`/`ServerResponse` compatible. For Fetch-native runtimes, explicitly design the bridge instead of pretending Node response mutation works.163. Record framework-version support, required plugins, and unsupported Ts.ED features. Do not silently omit multipart uploads, static assets, or serverless callback support.174. Do not start or apply an OpenSpec workflow from this skill. The workflow below is the source of truth for the adapter implementation.1819## 2. Inspect local precedents2021Read these sources before creating files:2223- `packages/platform/platform-http/src/common/services/PlatformAdapter.ts`24- `packages/platform/platform-express/src/components/PlatformExpress.ts`25- `packages/platform/platform-koa/src/components/PlatformKoa.ts`26- `packages/platform/platform-fastify/src/components/PlatformFastify.ts`27- Each package's `src/services/`, `src/utils/convertPath.ts`, `src/index.ts`, package manifest, Vitest config, and focused specs.28- `packages/platform/platform-express/test/platform-express.spec.ts`, `packages/platform/platform-koa/test/platform-koa.spec.ts`, and `packages/platform/platform-fastify/test/platform-fastify.spec.ts`.29- `docs/introduction/capabilities.md`, `docs/docs/configuration/{express,koa,fastify}.md`, `docs/docs/configuration/index.md`, `docs/index.md`, and `docs/.vitepress/config.mts`.3031Choose the closest precedent by execution model: Express for Connect-style middleware, Koa for composed async middleware, Fastify for plugin/route-registration APIs. Reuse intent and coverage, not framework-specific calls.3233## 3. Create the package3435Create `packages/platform/platform-<framework>/` using a comparable package as a structural template. Adapt, do not blindly duplicate:3637- `package.json`: ESM exports, build/test/barrel scripts, framework peer dependency, Ts.ED peer dependencies, and only runtime dependencies required by the adapter.38- `tsconfig.esm.json`, `vitest.config.mts`, `.npmignore`, and package `readme.md`.39- `src/index.ts`: export only the supported public adapter class, settings, decorators, and helpers.40- `test/app/`: a minimal server, framework-plugin setup, and shared integration fixtures compatible with `PlatformTestSdk`.41- `test/platform-<framework>.spec.ts`: the explicit `@tsed/platform-test-sdk` integration capability matrix. This file is mandatory for every new platform; a route-conversion or unit-only test suite is not a substitute.4243Keep the framework itself a peer dependency; put it in `devDependencies` at a pinned test version. The workspace glob already discovers `packages/platform/*`; only update other package catalogues if a real registry, docs, or release surface needs the entry.4445For `vitest.config.mts`, use the workspace preset import with this explicit type-resolution exception:4647```typescript48// @ts-ignore49import {presets} from "@tsed/vitest/presets";50```5152Add the new public package alias to both `tsconfig.node.json` and `tsconfig.spec.json`. For Fetch-native platforms with native `FormData` parsing, prefer that API in the platform package and document Multer decorators and storage engines as unsupported; do not create a `@tsed/platform-multer/<framework>` alias unless the Multer middleware contract is proven compatible.5354## 4. Implement the adaptations5556Create `Platform<Framework> extends PlatformAdapter<App>` with:57581. `NAME`, static `create()` and `bootstrap()` through `PlatformBuilder`.592. `createApp()` returning the framework app and a valid request callback. If the framework cannot yield a Node callback, implement and document the appropriate server/runtime boundary.603. `useContext()` that creates a `PlatformContext`, awaits `$ctx.start()`, makes it available to mapped handlers, and reliably calls `$ctx.finish()` when the response completes.614. `mapLayers()` translating `PlatformLayer` methods, paths, wildcard parameters, statics, and handler ordering to the framework router.625. `mapHandler()` that runs in the Ts.ED DI context, captures async errors, and preserves error-middleware and response-function semantics.636. `bodyParser()`, `statics()`, server/listening hooks, and framework-specific lifecycle hooks where the base contract requires them.647. `adapter()` bindings for the framework-specific `PlatformRequest`, `PlatformResponse`, and `PlatformHandler` implementations.6566Do not expose a framework's raw request/response directly to controllers without adapters. Map all semantics needed by `PlatformRequest` and `PlatformResponse`: URL and query, params, protocol and host, cookies/session, status, headers, redirect, body serialization, file/download, stream, and completion state.6768## 5. Handle framework boundaries explicitly6970- Convert Ts.ED route syntax with a framework-specific `convertPath()` and test named, optional, regexp, and wildcard paths.71- Preserve the framework's error path. Do not call a Connect `next(error)` from a Koa/Fastify/Fetch handler unless the target framework actually supports it.72- Route errors through `PlatformExceptions.catch(error, $ctx)` and route unknown paths through `PlatformExceptions.resourceNotFound($ctx)`. Reuse the existing exception filters; do not handcraft framework-specific error or 404 response payloads.73- Register body parsing, multipart support, static serving, view rendering, and raw-body capture only when supported. Make plugin registration awaitable when required. Match `PlatformBuilder`'s global `rawBody` policy: capture the raw buffer only when `rawBody` is configured or `@RawBodyParams()` is detected, while preserving the framework-parsed `body`. For Fetch-native runtimes, capture it before parsing with `request.clone()` only when `request.body` exists; do not globally disable parsing with a per-route `parse: "none"` strategy. Fall back to `body` when no raw capture is active.74- Make HTTP/HTTPS creation and `listen()` match the framework's ownership model. Confirm the callback works with `serverless-http`-style consumers if the adapter claims serverless support.75- Keep framework module augmentation and Ts.ED global declarations narrow and public.7677## 6. Build and run the integration matrix7879After the package and platform adaptations exist, add focused unit tests and create `test/platform-<framework>.spec.ts` with `PlatformTestSdk.create({rootDir, adapter, server})`. Build the spec from the Express, Koa, and Fastify precedents, then run it before documenting the platform as supported.8081- Use Express and Koa as the implementation baseline.82- Use Fastify only to understand framework-specific integration differences; do not copy its skips as an escape hatch.83- Add the `test/app/` fixtures needed by the SDK instead of omitting a scenario.8485The following SDK groups are mandatory for every new platform. Keep them enabled, make them pass, and do not wrap them in `describe.skip`:8687- `handlers`, `childrenControllers`, `inheritanceController`, `response`, `stream`, `middlewares`, `scopeRequest`, `headers`, `acceptMime`, `headerParams`, `pathParams`, `queryParams`, `bodyParams`, `cookies`, `session`, `location`, `redirect`, `errors`, `responseFilter`, `routing`, `locals`, `auth`, `module`, and `cache`.88- `view`, `statics`, `deepQueryParams`, and `custom404`.8990For Fetch-native platforms, test multipart independently with native `FormData` and `File` body parameters. Do not add the SDK Multer suite when Multer's middleware contract is incompatible; document that Multer decorators and storage engines are unavailable.9192Iterate on the adapter until the mandatory suite passes. Tackle isolated SDK gaps first (for example custom 404, deep query parsing, framework plugins, and test fixtures). Defer structural Fetch-native response work—stream bridging, response finalization, and Node callback emulation—until those simpler gaps are resolved. Then add coverage for adapter-specific behavior not supplied by the SDK, including bootstrap/create, native callback behavior, route conversion, error propagation, raw body, native multipart, and runtime/server lifecycle.9394Update all public documentation in the same change:95961. Add the framework column and supported/unsupported states to both feature and plugin tables in `docs/introduction/capabilities.md`; explain limitations below the tables when a marker needs context.972. Create `docs/docs/configuration/<framework>.md`, modeled on the closest adapter page. Cover installation, framework settings/plugins, static files, and use of a custom application instance.983. Link the new page from `docs/docs/configuration/index.md` and add it to the Configuration sidebar in `docs/.vitepress/config.mts`.994. Add the platform and configuration-page link to `docs/index.md` wherever the existing Express/Koa/Fastify list appears.100101Follow this order: create the package; implement the platform adaptations; add the SDK matrix and its fixtures; execute the matrix; iterate on missing framework features until it passes; then run the package tests, `yarn tsc -b` from the repository root, affected lint checks, and the documentation build. Do not use a package-scoped `tsc --build` command.102103## Completion checklist104105- Every mandatory `PlatformTestSdk` group is enabled and passing; none is skipped.106- The complete `test/platform-<framework>.spec.ts` matrix, checked against the Express, Koa, and Fastify precedents, exists and has been executed successfully.107- Framework capability gaps are documented in `capabilities.md`, the platform configuration page, navigation, and homepage.108- `PlatformAdapter` lifecycle, request/response/handler bindings, and route conversion are covered.109- Public package metadata, exports, peer dependencies, docs, and integration tests are complete.110- No Express/Koa/Fastify assumptions remain in the new adapter.