1---
2title: REST API (API Gateway v1)
3description: Expose a Lambda with a regional Amazon API Gateway REST API using RestApi, Resource, Method, Deployment, and Stage primitives.
4---
5
6The [Deploy a Lambda Function](/aws/compute/lambda) tutorial uses a **Function URL** for HTTP. Many teams still use **Amazon API Gateway** (REST, v1) as the front door.
7
8Alchemy models the v1 REST control plane as separate resources so you can wire integrations, stages, and custom domains explicitly.
9
10## Try the example
11
12The repo includes **`examples/aws-rest-api`**: a minimal **regional** REST API with `ANY` on the root and a `{proxy+}` resource, **AWS_PROXY** integration to a Lambda, then **Deployment**, **Stage** (`prod`), and **Lambda.Permission** so API Gateway can invoke the function.
13
14```sh
15cd examples/aws-rest-api
16bun install
17bun run deploy
18```
19
20After deploy, open the printed `invokeUrl` in a browser; you should see the Lambda’s text response.
21
22## Shape of the stack
23
24Typical order inside `Effect.gen`:
25
261. **`AWS.ApiGateway.RestApi`** — creates the API and exposes `restApiId` / `rootResourceId`.
272. **`AWS.ApiGateway.Resource`** — optional path segments (e.g. `{proxy+}`).
283. **`AWS.ApiGateway.Method`** — HTTP verb + integration (`AWS_PROXY` Lambda ARNs use `Output.interpolate` with `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${functionArn}/invocations`).
294. **`AWS.ApiGateway.Deployment`** — snapshots the current resource graph; change **`triggers`** (or other props) to force a new deployment when only integrations changed.
305. **`AWS.ApiGateway.Stage`** — attaches a deployment to a named stage (`prod`, `dev`, …).
316. **`AWS.Lambda.Permission`** — `principal: "apigateway.amazonaws.com"` and `sourceArn` covering `execute-api` for your API.
32
33Use **`yield* AWS.AWSEnvironment`** for `region` and `accountId` when building ARNs (as in the example).
34
35## Private integration (VPC link)
36
37For **private** HTTP integrations, create a **`AWS.ApiGateway.VpcLink`** with your load balancer target ARNs, then reference `vpcLinkId` as **`integration.connectionId`** with **`connectionType: "VPC_LINK"`** on `AWS.ApiGateway.Method` (`integration.uri` points at your private backend URL).
38
39```typescript
40const link = yield* AWS.ApiGateway.VpcLink("InternalLink", {
41 name: "internal-nlb",
42 targetArns: [loadBalancer.loadBalancerArn],
43});
44
45yield* AWS.ApiGateway.Method("PrivateGet", {
46 restApiId: api.restApiId,
47 resourceId: resource.resourceId,
48 httpMethod: "GET",
49 integration: {
50 type: "HTTP_PROXY",
51 integrationHttpMethod: "GET",
52 uri: "https://api.internal.example/resource",
53 connectionType: "VPC_LINK",
54 connectionId: link.vpcLinkId,
55 },
56});
57```
58
59See also [VpcLink](/providers/aws/apigateway/vpclink) in the generated API reference.
60
61## Custom domain
62
63Serve the API from your own hostname instead of the default `execute-api` URL. You need a validated ACM certificate, an **`AWS.ApiGateway.DomainName`**, a **`AWS.ApiGateway.BasePathMapping`**, and a DNS record pointing at the domain's regional target.
64
65### Request a certificate
66
67```typescript
68const cert = yield* AWS.ACM.Certificate("ApiCertificate", {
69 domainName: "api.example.com",
70 hostedZoneId: "Z1234567890",
71});
72```
73
74With `hostedZoneId` set, the provider creates the DNS validation records in Route 53 and waits for the certificate to be issued. Note that `AWS.ACM.Certificate` always requests the certificate in **`us-east-1`** (the region CloudFront and **EDGE** endpoints require) — a **REGIONAL** domain name needs a certificate in the API's own region, so outside `us-east-1` pass a pre-provisioned regional certificate ARN instead.
75
76### Create the domain name
77
78```typescript
79const domain = yield* AWS.ApiGateway.DomainName("ApiDomain", {
80 domainName: "api.example.com",
81 regionalCertificateArn: cert.certificateArn,
82 endpointConfiguration: { types: ["REGIONAL"] },
83 securityPolicy: "TLS_1_2",
84});
85```
86
87A regional domain exposes `regionalDomainName` and `regionalHostedZoneId` — the alias target for DNS. (EDGE domains use `certificateArn` and the `distributionDomainName` / `distributionHostedZoneId` attributes instead.)
88
89### Map the API to the domain
90
91```typescript
92yield* AWS.ApiGateway.BasePathMapping("Root", {
93 domainName: domain.domainName,
94 restApiId: api.restApiId,
95 stage: stage.stageName,
96});
97```
98
99Omitting `basePath` maps the domain root; set `basePath: "v1"` to serve the stage under `https://api.example.com/v1`.
100
101### Point DNS at the domain
102
103```typescript
104yield* AWS.Route53.Record("ApiAlias", {
105 hostedZoneId: "Z1234567890",
106 name: "api.example.com",
107 type: "A",
108 aliasTarget: {
109 hostedZoneId: domain.regionalHostedZoneId.as<string>(),
110 dnsName: domain.regionalDomainName.as<string>(),
111 },
112});
113```
114
115An alias **A** record targets the regional endpoint; `.as<string>()` narrows the optional outputs. See [DomainName](/providers/aws/apigateway/domainname) and [BasePathMapping](/providers/aws/apigateway/basepathmapping) in the API reference, and the [custom domains guide](/aws/networking/custom-domains) for the broader pattern (CloudFront, static sites).
116
117## API reference
118
119Generated docs for each primitive live under **Providers → AWS → ApiGateway** in the sidebar (for example [RestApi](/providers/aws/apigateway/restapi)). Run `bun generate:api-reference` locally after changing JSDoc on resources.
120
121## Next steps
122
123- **Custom domains:** covered [above](#custom-domain); see also the [custom domains guide](/aws/networking/custom-domains).
124- **API keys / throttling:** `ApiKey`, `UsagePlan`, `UsagePlanKey`.
125- **Authorizers:** `Authorizer` for Cognito or Lambda TOKEN / REQUEST flows.
126- **Gateway responses:** `GatewayResponse` for consistent 4xx/5xx bodies.
127
128HTTP APIs (v2) are a different product; this tutorial is **REST v1** only.