1---
2title: "Part 1: Your First Project"
3description: Install Alchemy, create a Stack with a Railway Project, and deploy it.
4sidebar:
5 order: 1
6---
7
8import Terminal from "../../../../components/Terminal.astro";
9import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
10import { effectVersion } from "../../../../versions";
11
12export const pkgs = `"alchemy@next" "effect@${effectVersion}" "@effect/platform-bun@${effectVersion}" "@effect/platform-node@${effectVersion}"`;
13
14Install Alchemy and Effect, create a Stack with a Railway Project, and
15deploy it. A Project is the parent for Services, Environments,
16Variables, and disks.
17
18## Prerequisites
19
20- [Bun](https://bun.sh) or Node.js 22+
21- A Railway workspace and an account API token — see
22 [Setup](/railway/setup) if you haven't created those yet
23
24:::note[Costs]
25A Railway Project is free. Compute (Services) is billed starting in
26[Part 2](/railway/tutorial/part-2). `alchemy destroy` tears
27everything down when you're done.
28:::
29
30## Create a project
31
32Start with an empty directory and initialize a `package.json`:
33
34<Tabs syncKey="pkgManager">
35 <TabItem label="bun" icon="bun">
36 <Code code="mkdir my-app && cd my-app && bun init -y" lang="sh" />
37 </TabItem>
38 <TabItem label="npm" icon="npm">
39 <Code code="mkdir my-app && cd my-app && npm init -y" lang="sh" />
40 </TabItem>
41 <TabItem label="pnpm" icon="pnpm">
42 <Code code="mkdir my-app && cd my-app && pnpm init" lang="sh" />
43 </TabItem>
44 <TabItem label="yarn" icon="seti:yarn">
45 <Code code="mkdir my-app && cd my-app && yarn init -y" lang="sh" />
46 </TabItem>
47</Tabs>
48
49## Install dependencies
50
51Install <code>alchemy@next</code> and <code>effect@{effectVersion}</code>:
52
53<Tabs syncKey="pkgManager">
54 <TabItem label="bun" icon="bun">
55 <Code code={`bun add ${pkgs}`} lang="sh" />
56 </TabItem>
57 <TabItem label="npm" icon="npm">
58 <Code code={`npm install ${pkgs}`} lang="sh" />
59 </TabItem>
60 <TabItem label="pnpm" icon="pnpm">
61 <Code code={`pnpm add ${pkgs}`} lang="sh" />
62 </TabItem>
63 <TabItem label="yarn" icon="seti:yarn">
64 <Code code={`yarn add ${pkgs}`} lang="sh" />
65 </TabItem>
66</Tabs>
67
68## Create the Stack
69
70Every Alchemy program starts with a `Stack` — a collection of
71Resources managed by Providers with state tracked between deploys.
72
73Create an `alchemy.run.ts` file:
74
75```typescript
76// alchemy.run.ts
77import * as Alchemy from "alchemy";
78import * as Effect from "effect/Effect";
79import * as Layer from "effect/Layer";
80
81export default Alchemy.Stack(
82 "MyApp",
83 {
84 // @error: ts(2345) Argument of type '{ providers: Layer.Layer<never, never, never>; }' is not assignable to parameter of type 'StackProps<never>'.
85 // @error: Property 'state' is missing in type '{ providers: Layer.Layer<never, never, never>; }' but required in type 'StackProps<never>'.
86 providers: Layer.empty,
87 },
88 Effect.gen(function* () {
89 // we'll add resources here next
90 }),
91);
92```
93
94TypeScript is unhappy: the `state` property is required. Every Stack
95needs a **state store** so Alchemy can persist resource state between
96deploys and compute diffs against your infrastructure.
97
98## Configure state
99
100Railway has no state backend of its own, so we'll keep state on disk
101with `Alchemy.localState()` — it writes to `.alchemy/` next to your
102code, no setup required:
103
104```diff lang="typescript"
105// alchemy.run.ts
106import * as Alchemy from "alchemy";
107import * as Effect from "effect/Effect";
108import * as Layer from "effect/Layer";
109
110export default Alchemy.Stack(
111 "MyApp",
112 {
113 providers: Layer.empty,
114+ state: Alchemy.localState(),
115 },
116 Effect.gen(function* () {
117 // we'll add resources here next
118 }),
119);
120```
121
122:::note[Other state stores]
123Local state is perfect for a solo project. When you need state your
124team and CI can share, swap in `Cloudflare.state()` or `AWS.state()`
125— see [State Store](/state-store).
126:::
127
128## Add a Project
129
130Resources represent cloud infrastructure. Each resource is
131`yield*`-ed inside the Stack's Effect generator.
132
133Let's declare a Railway Project and observe the type error:
134
135```diff lang="typescript"
136// alchemy.run.ts
137import * as Alchemy from "alchemy";
138+import * as Railway from "alchemy/Railway";
139import * as Effect from "effect/Effect";
140import * as Layer from "effect/Layer";
141
142export default Alchemy.Stack(
143 "MyApp",
144 {
145 providers: Layer.empty,
146 // @error: ts(2322) Type 'Layer<never, never, never>' is not assignable to type 'Layer<NoInfer<Providers>, never, StackServices>'.
147 // @error: Type 'Providers' is not assignable to type 'never'.
148 state: Alchemy.localState(),
149 },
150 Effect.gen(function* () {
151+ const site = yield* Railway.Project("Site");
152 }),
153);
154```
155
156Omit `name` and Alchemy generates a unique, DNS-safe Project name
157(max 32 characters, leading letter). The workspace defaults to
158whatever workspace your token belongs to. A production environment
159is created with the Project.
160
161TypeScript is telling us that `Layer.empty` doesn't provide
162`Railway.Providers` — the layer required by `Project`.
163
164## Fix the Providers
165
166Replace `Layer.empty` with `Railway.providers()` to resolve the type
167error:
168
169```diff lang="typescript"
170// alchemy.run.ts
171import * as Alchemy from "alchemy";
172import * as Railway from "alchemy/Railway";
173import * as Effect from "effect/Effect";
174-import * as Layer from "effect/Layer";
175
176export default Alchemy.Stack(
177 "MyApp",
178 {
179+ providers: Railway.providers(),
180 state: Alchemy.localState(),
181 },
182 Effect.gen(function* () {
183 const site = yield* Railway.Project("Site");
184 }),
185);
186```
187
188Now the program type-checks. The providers layer tells Alchemy how to
189talk to the Railway GraphQL API, and the type system ensures you
190never forget to wire it up.
191
192## Return Stack outputs
193
194Stack outputs let you see important values after a deploy. Return an
195object from the generator to expose the Project's name, id, and
196dashboard URL:
197
198```typescript
199 Effect.gen(function* () {
200 const site = yield* Railway.Project("Site");
201
202 return {
203 name: site.name,
204 projectId: site.projectId,
205 url: site.url,
206 };
207 }),
208```
209
210`url` is `https://railway.com/project/{projectId}`. Nothing is
211listening on the public internet yet — that's
212[Part 2](/railway/tutorial/part-2).
213
214## Deploy
215
216Run `alchemy deploy` to create the Project:
217
218<Tabs syncKey="pkgManager">
219 <TabItem label="bun" icon="bun">
220 <Code code="bun alchemy deploy" lang="sh" />
221 </TabItem>
222 <TabItem label="npm" icon="npm">
223 <Code code="npm run alchemy deploy" lang="sh" />
224 </TabItem>
225 <TabItem label="pnpm" icon="pnpm">
226 <Code code="pnpm alchemy deploy" lang="sh" />
227 </TabItem>
228 <TabItem label="yarn" icon="seti:yarn">
229 <Code code="yarn alchemy deploy" lang="sh" />
230 </TabItem>
231</Tabs>
232
233The first time you deploy, Alchemy prompts for Railway credentials —
234paste the account token you generated in [Setup](/railway/setup).
235The token is verified and saved to your **`default`**
236[profile](/environments/profiles), so you won't be asked again.
237
238<Terminal content={`[u]Plan[/u]: [g]1 to create[/g]
239
240[g]+[/g] [b]Site[/b] [d](Railway.Project)[/d]
241
242Proceed?
243◉ Yes ○ No
244[g]✓[/g] [b]Site[/b] [d](Railway.Project)[/d] [g]created[/g]
245{
246[s2]name: "myapp-site-dev-a1b2c3d4",
247[s2]projectId: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
248[s2]url: "https://railway.com/project/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
249}`} />
250
251Alchemy shows a plan, asks for confirmation, creates the Project, and
252prints the stack outputs.
253
254## Verify it worked
255
256The Project is listed in your workspace in the
257[Railway dashboard](https://railway.com/dashboard). Run
258`alchemy deploy` again. Because nothing changed, the Project shows
259as a no-op:
260
261<Terminal content={`[u]Plan[/u]: [d]no changes[/d]
262
263{
264[s2]name: "myapp-site-dev-a1b2c3d4",
265[s2]projectId: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
266[s2]url: "https://railway.com/project/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
267}`} />
268
269This is the core loop — declare resources in code, deploy, and
270Alchemy figures out what changed.
271
272## Recap
273
274You now have:
275
276- An `alchemy.run.ts` with a Stack and a Railway Project
277- A live Project in your workspace with a generated unique name
278- A production environment created with the Project
279- Stack outputs showing `name`, `projectId`, and the dashboard URL
280
281In [Part 2](/railway/tutorial/part-2), you'll deploy code to this
282Project — a `Railway.Service` that bundles an Effect HTTP handler
283into a container.