Skip to content

feat(railway): Railway provider#604

Open
sam-goodwin wants to merge 2 commits into
mainfrom
feat/railway
Open

feat(railway): Railway provider#604
sam-goodwin wants to merge 2 commits into
mainfrom
feat/railway

Conversation

@sam-goodwin

@sam-goodwin sam-goodwin commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Closes #637

Adds the Railway cloud provider: full resource coverage, live integration tests, and an end-to-end example.

Resources

Project, Environment, Service, Function (Effect-native runtime — bundles your Effect program, ships it through Railway's build pipeline, exposes it on a public domain), Database (template-equivalent Postgres/MySQL/Redis/Mongo with volume), Variables, Volume, TcpProxy, ServiceDomain, CustomDomain, ProjectToken, Webhook — plus the DatabaseUrl and VolumeMount bindings.

const project = yield* Railway.Project("my-project");
const db = yield* Railway.Database("Postgres", { project, kind: "postgres" });

class Api extends Railway.Function<Api>()("Api", { project }, Effect.gen(function* () {
  const databaseUrl = yield* Railway.DatabaseUrl.bind(db);
  return { fetch: /* ... */ };
}).pipe(Effect.provide(Railway.DatabaseUrlLive))) {}

Typed errors via distilled

All provider error handling goes through typed @distilled.cloud/railway errors (EnvironmentNameConflict, ServiceNameConflict, ServiceNameInvalid, TcpProxyLimitExceeded, ProjectCreateRateLimited, ...) discovered by live testing and recorded as patches — no generic UnknownRailwayError handling anywhere. Submodule bump pairs with alchemy-run/distilled#340.

Reconciler notes

  • Railway soft-deletes resources (deletedAt); read/observe treat soft-deleted as missing and name-conflict adoption skips them.
  • Create adopts same-named orphans on EnvironmentNameConflict/ServiceNameConflict (service enumeration via a hand-written environment.serviceInstances query — Railway has no top-level list-services API).
  • TcpProxy replaces delete-first (Railway allows one proxy per service instance) and treats TcpProxyOperationInProgress on an already-removed proxy as success.
  • Railway rate-limits project creation to 1/30s per workspace (failed attempts re-arm the window), so tests share one persistent project and only Project.test.ts exercises the project lifecycle.

Example

examples/railway-postgresProject + Database + Function wired through the DatabaseUrl binding; verified live (/, /health, and /db round-tripping a query through the bound Postgres) and destroyed.

Tests

All 11 Railway suites pass against the shared project (Project.test.ts excluded from the default run per rate limits). Tests are idempotent: create adopts orphans by name and every test tears down via Effect.ensuring(stack.destroy()).

…tion, Database, and friends

Adds the Railway cloud provider with resources (Project, Environment,
Service, Function, Database, Variables, Volume, TcpProxy, ServiceDomain,
CustomDomain, ProjectToken, Webhook), the DatabaseUrl/VolumeMount
bindings, live integration tests sharing a single rate-limit-friendly
project, and an end-to-end examples/railway-postgres app (Function +
Postgres + DatabaseUrl binding) verified against Railway.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sam-goodwin sam-goodwin mentioned this pull request Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Railway

1 participant