@crisog/railway-sdk
v1.2.0
Published
A lightweight TypeScript SDK and tooling around Railway's public GraphQL API. It ships a minimal client, generated operation helpers, and scripts that keep local documents aligned with the platform schema.
Readme
Railway SDK
A lightweight TypeScript SDK and tooling around Railway's public GraphQL API. It ships a minimal client, generated operation helpers, and scripts that keep local documents aligned with the platform schema.
Installation
Install the package together with your preferred package manager:
npm install @crisog/railway-sdkNote
The SDK is published as ESM-only. Import it usingimport/dynamicimport(); CommonJSrequire()is not supported.
Quick Start
Set
RAILWAY_API_TOKENin your environment before running.
import { createRailwayFromEnv } from '@crisog/railway-sdk';
const railway = createRailwayFromEnv();
const projectsResult = await railway.projects.list({
variables: {
first: 5,
includeDeleted: false,
},
});
if (projectsResult.isErr()) {
throw projectsResult.error;
}
const { projects } = projectsResult.value;
for (const project of projects) {
console.log(`${project.id} – ${project.name}`);
}
if (projects.pageInfo.hasNextPage) {
console.log('More projects available…');
}Initialization
| Method | Use When |
| ------------------------ | -------------------------------------------------------------- |
| createRailwayFromEnv() | Token lives in environment variables |
| createRailway(options) | Token is supplied programmatically (e.g., from a secret store) |
import { createRailway } from '@crisog/railway-sdk';
const railway = createRailway({
token: myToken,
});Response Handling
Every helper returns ResultAsync<FlattenGraphQLResponse<TData>, GraphQLRequestError>. Branch with .isOk() / .isErr(), unwrapOr, or .match() to handle success and failure.
The FlattenGraphQLResponse type collapses GraphQL connections (edges/node) into plain arrays while preserving metadata like pageInfo.
Examples
Browse more examples at github.com/crisog/railway-sdk/tree/main/examples.
Authentication
createRailwayFromEnv() looks for tokens in this order:
RAILWAY_API_TOKEN(account or workspace tokens)RAILWAY_PROJECT_TOKEN
If none are found, it throws MissingTokenError.
Token Types
When using createRailway(), the tokenType option determines which HTTP header is used:
| tokenType | HTTP Header |
| ----------- | ----------------------- |
| (default) | Authorization: Bearer |
| 'project' | Project-Access-Token |
[!NOTE] Account and workspace tokens use Bearer authentication by default. Only specify
tokenType: 'project'for project tokens.
| Category | Account Token | Workspace Token | Project Token | | ------------------ | -------------- | ----------------- | ------------------- | | projects/ | ✅ Full Access | ✅ Full Access | ⚠️ Get Only | | services/ | ✅ Full Access | ✅ Full Access | ✅ Full Access | | environments/ | ✅ Full Access | ✅ Full Access | ✅ Full Access | | deployments/ | ✅ Full Access | ✅ Full Access | ❌ Not Authorized | | variables/ | ✅ Full Access | ✅ Full Access | ✅ Full Access | | domains/ | ✅ Full Access | ✅ Full Access | ✅ Full Access | | templates/ | ✅ Full Access | ✅ Full Access | ⚠️ Existing Project | | networking/ | ✅ Full Access | ✅ Full Access | ✅ Full Access | | observability/ | ✅ Full Access | ✅ Full Access | ⚠️ Events Only | | workflows/ | ⚠️ Limited | ⚠️ Limited | ❌ Not Authorized | | integrations/ | ✅ Full Access | ❌ Not Authorized | ❌ Not Authorized | | volumes/ | ✅ Full Access | ✅ Full Access | ✅ Backups Only | | account/ | ✅ Full Access | ✅ Full Access | ❌ Not Authorized |
Legend: ✅ Full Access · ⚠️ Limited · ❌ Not Authorized
- Account Token: Required for GitHub integrations. Uses Bearer authentication (default).
- Workspace Token: Scoped to workspace-level operations. Uses Bearer authentication (default). Can create new projects and deploy templates.
- Project Token: Scoped to specific project/environment. Use
tokenType: 'project'. Has broad access within the scoped project but cannot list deployments or access metrics.
Supported Namespaced Methods
Both helpers return a Railway client with these namespaces. Use them to discover what each part of the API can do:
| Namespace | Summary |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| projects | Cover the full project lifecycle: CRUD, membership, invitations, feature flags, schedules, tokens, access checks, workflows, and transfers. |
| services | Manage services and deployments, including instance operations (limits, overrides), domains, feature flags, and runtimes. |
| deployments | Inspect and operate deployments: approvals, retries, logs/events, triggers, and snapshots. |
| domains | Work with custom domains attached to Railway services. |
| account | Manage the authenticated account: profile, email updates, invites, flags, passkeys, beta access, terms, and deletion. |
| environments | Manage environment lifecycle, logs, configuration patches (including staged changes), and deploy triggers. |
| networking | Administer private networks, endpoints, egress gateways, nodes, and TCP proxies. |
| templates | Discover, clone, generate, publish, or delete templates (including project sources). |
| volumes | Create, update, delete volumes, and administer backups and schedules. |
| variables | List, upsert, and manage variables, collections, and shared configurations. |
| auth | Handle login sessions, recovery codes, active sessions, and two-factor authentication flows. |
| apiTokens | Create, list, and revoke personal API tokens. |
| billing | Control customer subscriptions, referral info, usage limits, and fair use acknowledgements. |
| integrations | Configure third-party integrations (GitHub, Heroku, Vercel, providers) and their auth records. |
| observability | Access platform observability dashboards, logs, events, metrics, and usage statistics. |
| preferences | Retrieve and update user preferences plus per-resource overrides. |
| webhooks | Create, update, delete, and list project webhooks. |
| workspaces | Inspect and update workspaces, manage users and permissions, invite codes, templates, trusted domains, Slack channels, and identity providers. |
| regions | List available deployment regions. |
| misc | Miscellaneous public platform endpoints such as changelog assets, status, and platform feature flags. |
Refer to
src/api.tsfor the precise helper mapping and regenerate wrappers after schema or document changes (bun run generate:wrappers).
Generated Operations
The SDK re-exports every generated wrapper from src/generated/operations.ts. You can cherry‑pick individual helpers if you prefer tree-shaken imports:
import { createRailwayFromEnv, projects } from '@crisog/railway-sdk';
const railway = createRailwayFromEnv();
const result = await projects(railway.client, {
variables: { first: 5 },
});Each helper enforces the correct variables shape and accepts optional GraphQLDocumentRequestOptions (custom headers, alternate fetch, abort signals, retry configuration, …). Responses use the flattened representation described above so you can iterate directly over arrays instead of traversing edges/node wrappers.
Retries & Cancellation
Pass retry configuration through the creation helpers:
const railway = createRailwayFromEnv({
retry: {
maxAttempts: 3,
shouldRetry: ({ error, response }) => response?.status >= 500 || error instanceof TypeError, // network errors
delayMs: (attempt) => attempt * 250,
},
});Retries never run by default. shouldRetry must return true to trigger another attempt. Aborted requests (AbortController, signal.aborted, etc.) are never retried and bubble the original abort error back to the caller.
Error Handling
- Generated operations resolve to
ResultAsync<FlattenGraphQLResponse<TData>, GraphQLRequestError>. TheErrvariant includesGraphQLRequestError(or subclasses such asNetworkError,NotFoundError,PermissionError,ValidationError). - Inspect
error.response,error.body, orerror.rawBodyon the error instance for full context. - Missing or empty tokens still throw
MissingTokenErrorduring client construction.
Response Utilities
The SDK ships a few helpers that build on neverthrow results:
unwrapField(result, 'field')– ensures the top-level field exists before returning it.unwrapArray(result, 'field')– validates the field is an array.unwrapNested(result, ['path', 'to', 'field'])– walks nested objects safely.
All helpers preserve the original error type when the upstream result is Err, and produce a ResponseFieldError when the requested field is missing.
Code Generation Workflow
The repository includes scripts for keeping schema artifacts and wrappers in sync:
Fetch the latest schema
bun run codegen:introspectRequires a valid token; writes
schema/railway-introspection.jsonand regeneratessrc/generated/graphql.ts.Emit typed wrappers
bun run generate:wrappersProduces
src/generated/operations.ts, which powers the namespaced API surface.
Run both commands whenever the upstream schema changes or after adding/editing .graphql documents under src/graphql/.
Development
bun install
bun run lint
bun run typecheckBun 1.2+ is recommended. The project enforces strict TypeScript options; run formatting/linting before submitting changes.
