Build a Serverless AI Chatbot API with Amazon Bedrock, Lambda, and API Gateway
Introduction Adding AI to a web app used to mean training models, managing GPUs, and wrestling with infrastructure. With Amazon Bedrock, it now takes a few dozen lines of code and a couple of managed services. In this article, I'll walk through building a small serverless chatbot API that any frontend (React, plain JavaScript, or a mobile app) can call. I built this while exploring how to connect LLM features to full-stack projects, and I'm sharing the approach and the mistakes I made so you can skip them.
Architecture Overview
The flow is simple:
- The client sends a
POSTrequest with a JSON body containing a prompt. - API Gateway receives the request and triggers a Lambda function.
- Lambda calls Amazon Bedrock using the Converse API.
- Bedrock returns the model's reply, and Lambda sends it back as JSON.
Step 1: Enable Model Access in Amazon Bedrock
In the AWS Console, open Amazon Bedrock, go to Model access, and request access to the foundation model you want to use. Some models are available immediately, while others may need approval. Note the model ID of your chosen model, since you will pass it to Lambda as an environment variable.
Step 2: Create the Lambda Function
Create a new Lambda function with the Python runtime. Then add an environment variable:
MODEL_ID= the model ID you noted in Step 1
Paste in this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import json
import os
import boto3
bedrock = boto3.client("bedrock-runtime")
MODEL_ID = os.environ["MODEL_ID"]
SYSTEM_PROMPT = (
"You are a helpful assistant for a developer community. "
"Keep answers clear and concise."
)
def reply(status, payload):
return {
"statusCode": status,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
"body": json.dumps(payload),
}
def lambda_handler(event, context):
try:
body = json.loads(event.get("body") or "{}")
prompt = (body.get("prompt") or "").strip()
if not prompt:
return reply(400, {"error": "Please provide a prompt."})
if len(prompt) > 2000:
return reply(400, {"error": "Prompt is too long."})
result = bedrock.converse(
modelId=MODEL_ID,
system=[{"text": SYSTEM_PROMPT}],
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": 512, "temperature": 0.5},
)
answer = result["output"]["message"]["content"][0]["text"]
return reply(200, {"answer": answer})
except Exception as e:
print(f"Error: {e}")
return reply(500, {"error": "Something went wrong."})Step 3: Give Lambda Permission to Call Bedrock
By default, your function's execution role cannot invoke Bedrock. Attach a policy like this to the role, following the principle of least privilege:
1
2
3
4
5
6
7
8
9
10
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel"],
"Resource": "*"
}
]
}For production, replace
* with the specific model ARN you use. Also increase the Lambda timeout from the default 3 seconds to something like 30 seconds, because model responses can take longer.Step 4: Expose It with API Gateway
- Open API Gateway and create an HTTP API.
- Add a Lambda integration pointing to your function.
- Create a route:
POST /chat. - Enable CORS so your browser-based frontend can call it.
- Deploy and copy the invoke URL.
Step 5: Test It
From your terminal:
1
2
3
curl -X POST "https://YOUR_API_ID.execute-api.YOUR_REGION.amazonaws.com/chat" \
-H "Content-Type: application/json" \
-d '{"prompt": "Explain AWS Lambda in two sentences."}'Securing and Controlling Costs
A public AI endpoint can become an expensive surprise, so treat these as part of the build, not an afterthought:
- Throttle requests. Set rate limits on your API Gateway stage so one user cannot flood it.
- Add authentication. Use API keys, IAM authorization, or Amazon Cognito before sharing the URL widely.
- Set a budget alarm. Create an AWS Budgets alert so you hear about unexpected spend early.
Where to Go Next
- Conversation memory: store chat history in Amazon DynamoDB and send previous turns with each request
- Streaming responses: use streaming for a faster, more natural chat feel
- RAG: use Knowledge Bases for Amazon Bedrock so the model can answer from your own documents
Conclusion
If you build on this, I would love to hear what you create. Share your version in the comments, and let me know which extension (memory, RAG, or streaming) you would like to see covered next.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article