Private services
A Cloud Run service is private unless you set invokerIamDisabled.
Callers need a Google identity token from an account that holds
roles/run.invoker on it. InvokeService sets that up for you.
A private backend
Section titled “A private backend”Leave invokerIamDisabled off:
import * as GCP from "@alchemy.run/gcp";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/http/HttpServerResponse";
export default class Quotes extends GCP.Function<Quotes>()( "Quotes", { main: import.meta.url }, Effect.gen(function* () { return { fetch: HttpServerResponse.json({ quote: "Make it work, make it right." }), }; }),) {}Requests without a valid token get a 403 before they reach your code.
Call it from another service
Section titled “Call it from another service”import * as GCP from "@alchemy.run/gcp";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/http/HttpServerResponse";import Quotes from "./Quotes.ts";
export default class Gateway extends GCP.Function<Gateway>()( "Gateway", { main: import.meta.url, invokerIamDisabled: true }, Effect.gen(function* () { const quotes = yield* GCP.Run.InvokeService(Quotes);
return { fetch: Effect.gen(function* () { const response = yield* quotes.fetch("/quote"); return HttpServerResponse.text(yield* response.text, { status: response.status, contentType: "application/json", }); }).pipe(Effect.orDie), }; }).pipe(Effect.provide(GCP.Run.InvokeServiceHttp)),) {}On deploy, InvokeService grants the Gateway roles/run.invoker on
Quotes and nothing else, and gives the Gateway Quotes’ URL. Each call
attaches a fresh identity token. You pass a path, not a URL.
Send a body
Section titled “Send a body”const response = yield* orders.fetch("/orders", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ sku: "abc", quantity: 1 }),});const order = yield* response.json;Handle an unreachable backend
Section titled “Handle an unreachable backend”A failed call fails with GCP.Run.InvokeServiceError. Turn it into a
502:
const response = yield* quotes.fetch("/quote").pipe( Effect.catchTag("GCP.Run.InvokeServiceError", (error) => Effect.succeed({ status: 502, text: Effect.succeed(JSON.stringify({ error: error.message })), }), ),);Example
Section titled “Example”examples/gcp-service-to-service
is a public gateway in front of a private backend.
Where next
Section titled “Where next”- VPC & private networking — reach private IPs from Cloud Run.
- IAM & bindings — how the invoker grant is scoped.