Skip to content

Part 2: Run a Program

Run a program in its own Archil sandbox, with PUT /:key and GET /:key routes that store files on the disk from Part 1. The finished code is examples/archil-sandbox.

An Archil.Sandbox with main runs your program. main: import.meta.url makes this file both the bundle and the program, and the program listens on port 3000:

src/Api.ts
import * as Archil from "@alchemy.run/archil";
import * as Effect from "effect/Effect";
import * as HttpServerResponse from "effect/http/HttpServerResponse";
export default class Api extends Archil.Sandbox<Api>()(
"Api",
{ main: import.meta.url, port: 3000 },
Effect.gen(function* () {
return {
fetch: Effect.succeed(HttpServerResponse.text("hello")),
};
}),
) {}

Archil.ReadWriteDisk(Files) gives the program its own S3 user on that disk, so it can read and write that disk and nothing else:

src/Api.ts
import * as Archil from "@alchemy.run/archil";
import * as Effect from "effect/Effect";
import * as HttpServerResponse from "effect/http/HttpServerResponse";
import { Files } from "./Storage.ts";
export default class Api extends Archil.Sandbox<Api>()(
"Api",
{ main: import.meta.url, port: 3000 },
Effect.gen(function* () {
const files = yield* Archil.ReadWriteDisk(Files);
return {
fetch: Effect.succeed(HttpServerResponse.text("hello")),
};
}),
}).pipe(Effect.provide(Archil.ReadWriteDiskHttp)),
) {}

Read the key from the path and put the request body on the disk:

src/Api.ts
import { HttpServerRequest } from "effect/http/HttpServerRequest";
return {
fetch: Effect.succeed(HttpServerResponse.text("hello")),
fetch: Effect.gen(function* () {
const request = yield* HttpServerRequest;
const key = new URL(request.url, "http://api").pathname.slice(1);
if (request.method === "PUT") {
const body = yield* request.text;
yield* files.put(key, body);
return HttpServerResponse.empty({ status: 201 });
}
const text = yield* files.getText(key);
return HttpServerResponse.text(text);
}),
};

head returns undefined when the file doesn’t exist:

src/Api.ts
return HttpServerResponse.empty({ status: 201 });
}
const file = yield* files.head(key);
if (file === undefined) {
return HttpServerResponse.text("not found", { status: 404 });
}
const text = yield* files.getText(key);
src/Api.ts
return HttpServerResponse.text(text);
}),
}).pipe(
Effect.catch((error) =>
Effect.succeed(HttpServerResponse.text(String(error), { status: 500 })),
),
),
alchemy.run.ts
import Api from "./src/Api.ts";
import { Files } from "./src/Storage.ts";
Effect.gen(function* () {
const files = yield* Files;
return { diskId: files.diskId };
const api = yield* Api;
return { diskId: files.diskId, url: api.url };
}),
Terminal window
pnpm alchemy deploy

url is https://3000-<id>.<region>.archil.app.

Terminal window
curl -X PUT --data "hello archil" "$URL/notes/first.txt"
curl "$URL/notes/first.txt"

The second command prints hello archil. Leave the program alone for a few minutes and the sandbox stops; the next request starts it again.

The sandbox runs your Effect program and reads and writes the disk through its own S3 user. In Part 3 you’ll test it.