feat: self-hosted serve command and admin API URL fix - #5374
Merged
Conversation
…pi-infra-aws Phase 1 of the api-infra composition layer (see plans/api-infra-composition-layer.md). The GraphQL handler's composition root — ~30 feature registrations in a specific order — lived in the scaffolding template `_templates/appTemplates/api/graphql/src/index.ts`, exercised only by full e2e deploy. That's why wiring/fresh-install bugs (e.g. "No registration found for FileModel") surface only in CI e2e, never in a test. Move it verbatim (order preserved) into a real package, @webiny/api-infra-aws, behind `createWebinyApiHandler(config)`. The only project-specific input is `extensions`; everything else is standard AWS/env wiring owned by the package. The template collapses to a 3-line shim. This makes the comp-root a real, integration-testable unit (Phase 2 adds the fresh-install test). No behavior change. Build + adio + lint green; ESM imports validated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The WebSocket API Gateway (ApiWebsocket.ts) routes $connect/$disconnect/$default straight to the GraphQL Lambda (graphql.functions.graphql), and the GraphQL handler already dispatches WS events (registers WebSocketEventType + WebSocketLambdaHandler). No pulumi module deploys a separate websockets function, and nothing references appTemplates/api/websockets — it's a leftover from before WS was consolidated into the graphql handler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Precondition for Phase 2 integration testing: createWebinyApiHandler defaulted to @webiny/aws-sdk's getDocumentClient() + process.env.DB_TABLE, which can't be pointed at a test DynamoDB. Add optional config.documentClient / config.dbTable (defaulting to the AWS client + env) so an integration test can boot the full handler against a local DynamoDB. No behavior change for the app (template passes neither → defaults preserved). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…variants Carve @webiny/api-infra-aws into a storage-agnostic base plus two thin storage-variant packages, mirroring the api-headless-cms-ddb / -ddb-es convention (and giving each variant real dependency isolation — the DDB path never pulls OpenSearch, and vice versa). - @webiny/api-infra-aws (base): transport + all storage-agnostic API features + engine, in the order-sensitive sequence, with two injected hooks — registerRootStorage / registerRequestStorage — for the variant's storage. - @webiny/api-infra-aws-ddb: supplies DynamoDB storage ops (core/CMS/audit-logs/ ACO/websockets). No OpenSearch deps. - @webiny/api-infra-aws-ddb-os: OpenSearch core + HeadlessCmsDdbEs CMS storage + DbRegistry (per-request, before CMS), DDB for the rest. OpenSearch client injectable for tests. Templates shim to their variant: the default graphql handler → -ddb; the OpenSearch extension handler → -ddb-os. Single source of truth for the wiring; the two variants can't drift. Builds + adio + lint green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The first test that boots the entire API composition root — createWebinyApiHandler
with every feature registered — against an in-process (dynalite) DynamoDB and
drives a real GraphQL request. Previously the comp root lived only in an app
template, exercised solely by full e2e deploy.
Asserts the base /graphql schema builds and resolves (`{ __typename }` → "Query")
on a fresh/empty DB, with no DI wiring failure. This catches a whole class of
comp-root bugs — a feature that fails to register, a broken base schema, a
registration-order regression — before deploy.
NOTE: this does NOT yet reproduce the CI "No registration found for FileModel"
install failure — the base schema builds fine here because FileManagerAppFeature
register()s FileModel and FmGraphQLSchema is on the base schema. That failure is
on a different path (likely the install-wizard state / a route this query doesn't
build), and is a follow-up to target specifically.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s to api-infra-aws Phase 3: make event-handler-aws a pure, domain-free transport package. The three loader decorators (ApiGatewayIdentityLoaderDecorator, ApiGatewayTenantLoaderDecorator, S3TenantLoaderDecorator) were the only thing in event-handler-aws importing @webiny/api-core (its requestContext RequestIdentityLoader/RequestTenantLoader). - Move the three decorators (+ the S3 decorator's test) into @webiny/api-infra-aws, the composition layer that legitimately depends on both transport and domain. - ApiGatewayFeature / S3Feature become transport-only (event type + router); the base createWebinyApiHandler now registers the ApiGateway loader decorators after ApiGatewayFeature (same order: tenant inner, identity outer). - Drop @webiny/api-core from event-handler-aws entirely (prod + test). event-handler-aws now has zero api-* dependency. Builds + adio + lint green; event-handler-aws 4 files/18 tests, api-infra-aws base S3 decorator test 3/3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The standard Webiny packages (event-handler-core, event-handler-aws, api-core) have no root index.js — in-repo imports resolve via tsconfig paths (→ src) and at publish "webiny.publishFrom": "dist" makes dist/ the package root, so main: "index.js" resolves to the built dist/index.js. The hand-written `export * from "./dist/index.js"` shim is never used; it was mistakenly copied from event-handler-server (which inherited it from the old event-handler-node). Remove it from the three api-infra packages. Builds + the -ddb integration test pass without it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-2026-06-15 # Conflicts: # packages/cli-core/package.json # packages/cli/files/references.json # yarn.lock
…/webiny/webiny-js into adrian/project-server-2026-06-15
`controller.response`/`.state`/`.logger`/`.runtime` are added to api-core's TaskController via a `declare module` augmentation that lives in @webiny/background-tasks. Consumers importing TaskDefinition from `webiny/api/tasks` (e.g. task extensions) only got the bare api-core controller and failed to typecheck with "Property 'response' does not exist". Previously the app handler imported @webiny/background-tasks directly, which incidentally pulled the augmentation into the compile program; the api-infra handler shim no longer does. Load it explicitly via a side-effect import so the full controller interface is always available to `webiny/api/tasks` consumers. The import is preserved in the built .d.ts, so the augmentation propagates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rror the extension instead packages/webiny/src/api/tasks.ts is GENERATED by `webiny generate-webiny-package` (source: packages/api-core/src/exports/api/tasks.ts) — a hand-edit there is overwritten on regen, so it's the wrong place. And moving the augmentation import into api-core/src/exports would create an api-core → background-tasks cycle (background-tasks already depends on api-core). Both need a team decision. For now: revert the generated-file edit and mark the failing `controller.response` usages in the SelfCleaningTask extension with @ts-expect-error (self-cleaning — they'll flag once the augmentation is properly fixed). The underlying issue (TaskController's response/state/logger/runtime live in a background-tasks `declare module` augmentation that must be in the compile program) is pre-existing and to be fixed at the source. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…026-07-03 # Conflicts: # packages/cli/files/references.json
Rename the API composition packages to parallel the event-handler-* transport layer they build on: - @webiny/api-infra-aws → @webiny/api-event-handler-aws - @webiny/api-infra-aws-ddb → @webiny/api-event-handler-aws-ddb - @webiny/api-infra-aws-ddb-os → @webiny/api-event-handler-aws-ddb-os Pure rename: dir moves + package names + all imports + template shims + comments; regenerated tsconfigs/references/lockfile. All three build. (api-event-handler-core extraction + template-dep prune follow in separate commits.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…om api template The graphql handler no longer imports the individual @webiny/api-* packages — it goes through @webiny/api-event-handler-aws-ddb / -ddb-os, which pull the full feature + storage set transitively; extensions use the `webiny` meta-package. Remove the 17 storage/transport-ADAPTER deps (pure wiring, never imported by extension code, now fully encapsulated by the handler): api-*-ddb / -ddb-es / -sql / -os storage adapters, api-*-aws transport, api-opensearch, api-dynamodb-to-elasticsearch, api-background-tasks-*, api-websockets-server. Kept: the two handler packages, infra (aws-sdk/build-tools/db-dynamodb/handler-*), and the domain-feature packages (api-core, api-headless-cms, api-aco, etc.) as a safety margin since extensions may import their types directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…equestStack Pull the per-request API feature composition out of api-event-handler-aws into a new transport-agnostic package, @webiny/api-event-handler-core, exposing `registerApiRequestStack(container, config)`. It registers every domain feature + the GraphQL engine in the exact (order-sensitive) sequence, ONE place so the feature set + ordering can't drift between transports. The three transport-/storage-specific interleave points are injected as hooks at their exact positions: registerRequestStorage (before CMS), registerRealtimeTransport (after WebsocketsFeature), registerSchedulerTransport (after SchedulerFeature). api-event-handler-aws now keeps only the ROOT AWS transport wiring (API Gateway + auth/tenant loaders, bg-task + WebSocket Lambda invocations, DynamoDB, Cognito, storage) and delegates its request phase to registerApiRequestStack, passing the AWS WebSockets + scheduler hooks. 18 domain-feature deps moved from aws → core. This is what the upcoming @webiny/api-event-handler-server will reuse. Verified: the aws-ddb dynalite integration test (full handler boot → core) passes unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…over shared core Add @webiny/api-event-handler-server: a Node HTTP server transport composition that reuses @webiny/api-event-handler-core's registerApiRequestStack — proving the request stack is genuinely transport-agnostic. Notably it calls registerApiRequestStack with NEITHER the realtime nor the scheduler hook (both are AWS-specific: API Gateway WebSockets / EventBridge Scheduler), validating that those interleave hooks are correctly optional. EXPERIMENTAL / not deployable yet — the Node transport in @webiny/event-handler-server is missing two primitives (documented in the handler header): (1) a routing terminal that dispatches the translated IHttpRequest through HttpRouter and translates the response back (AWS has ApiGatewayHttpRouterHandler; Node has only the translator), and (2) Node auth/tenant loader decorators. Both belong in event-handler-server, not in this composition layer. No template wires it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…' into adrian/server-flavour-2026-07-03 # Conflicts: # packages/cli/files/references.json # yarn.lock
…server api template Add storage variants over the experimental Node server transport base, mirroring the AWS side: - @webiny/api-event-handler-server-ddb — DynamoDB storage (uniform DI Features; the base bakes in no DB, so the variant registers DbFeature itself). - @webiny/api-event-handler-server-sql — SQL storage; caller supplies the Knex client. Unlike DDB, the SQL storage layer is a MIX (ApiCoreSql/WebsocketsSql are DI Features; CMS/ACO/audit-logs SQL are legacy RegisterExtensionPlugins applied via registerExtensions), composed to mirror the api-headless-cms-sql test setup. Wire the server-flavour api graphql template to the SQL variant as a thin shim (replacing the old plugin-based createHandler placeholder that stalled this branch).⚠️ Both variants are BUILD-VERIFIED ONLY. They inherit the server transport's not-yet-deployable status (missing Node routing terminal + auth/tenant loaders), and no SQL-backed API handler has been runtime-assembled before — this is a scaffold/starting point. Builds + adio + lint green across all. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Not needed for now — the server flavour targets SQL. Remove @webiny/api-event-handler-server-sql's DDB sibling and the stale comment references to it. The base + sql variant are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…webiny/webiny-js into adrian/self-hosted-2-2026-07-07
Member
Author
|
/vitest |
|
Vitest tests have been initiated (for more information, click here). ✨
❌ Failed packagesDDB+OS
|
adrians5j
marked this pull request as ready for review
July 10, 2026 11:16
…2026-07-07 # Conflicts: # yarn.lock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The self-hosted (server) flavour gained a real local-dev and production workflow:
webiny-server serve— runs built apps as long-running servers:serve apiboots the built API HTTP handler,serve adminstatically serves the admin SPA (with client-side-routing fallback and correct cache headers), andserve(no app) runs both. Apps must be built first.webiny-server watch api— now boots the API automatically alongside the build watchers, with cleaned-up output: no startup crash before the first build, Node--watchchatter suppressed, and clearapi ✔ listening/api ↻ reloadedlines. Port honours a host-injectedPORT(e.g. portless), thenWEBINY_API_PORT, then a default.Admin.ApiUrl/EnvVar. The server flavour now registers them via<ProjectServer />.@webiny/project-servernow exposes theApi,Admin,Cli,Infra, andProjectnamespaces (minus AWS/Pulumi/deploy/environments concepts, which don't apply), so server projects import config helpers from one place.OnEntryBeforeCreateCMS config extension.webiny-server watchwith no app is temporarily disabled (run apps separately) while the combined output is refined.Changelog
Serve and watch for self-hosted projects, plus admin API URL fix
Self-hosted projects previously couldn't run their built apps and, when watching, the admin area talked to the wrong address. You can now serve the API and admin locally or in production, and the admin reliably uses the API URL you configure.
Squash Merge Commit