Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# API Gateway to Lambda to Amazon Bedrock with Application-Level Guardrails

This pattern deploys a REST API that invokes Amazon Bedrock with **per-request Guardrails** — content and topic filtering applied at the application level on each individual API call.

> **Application-level vs Account-level:** This pattern applies guardrails per-request by passing `guardrailIdentifier` and `guardrailVersion` in each InvokeModel call. This gives you fine-grained control — different APIs can use different guardrails, or skip them entirely. For account-wide enforcement that applies to ALL Bedrock calls automatically, see the [bedrock-guardrails-cross-account-cdk](../bedrock-guardrails-cross-account-cdk) pattern.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-bedrock-guardrails-cdk

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details.

## Requirements

- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
- [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed
- [Node.js](https://nodejs.org/) 20.x or later
- [Amazon Bedrock model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) enabled for a model of your choice in your AWS account

## How it works

1. Client sends a POST request to API Gateway with a `prompt` field
2. Lambda invokes Bedrock InvokeModel with `guardrailIdentifier` and `guardrailVersion` parameters
3. Bedrock evaluates the input against content filters (sexual, violence, hate, insults, misconduct) and topic filters (financial advice)
4. If the guardrail triggers, the response contains the blocked message — no model tokens consumed
5. If the guardrail passes, the model generates a response normally

## Deployment

```bash
cd apigw-lambda-bedrock-guardrails-cdk
npm install
cdk deploy
```

## Testing

```bash
API_URL=$(aws cloudformation describe-stacks \
--stack-name ApigwLambdaBedrockGuardrailsStack \
--query 'Stacks[0].Outputs[?OutputKey==`ApiEndpoint`].OutputValue' --output text)

# Safe prompt — should pass guardrails
curl -X POST "$API_URL" -H "Content-Type: application/json" \
-d '{"prompt": "What is Amazon S3?"}'

# Blocked prompt — triggers topic filter (financial advice)
curl -X POST "$API_URL" -H "Content-Type: application/json" \
-d '{"prompt": "What stocks should I buy for maximum returns in 2026?"}'
```

Expected: The first request returns a normal Bedrock response. The second returns the blocked message with `guardrailAction: "GUARDRAIL_INTERVENED"`.

## Cleanup

```bash
cdk destroy
```

---

Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
7 changes: 7 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { ApigwLambdaBedrockGuardrailsStack } from '../lib/apigw-lambda-bedrock-guardrails-stack';

const app = new cdk.App();
new ApigwLambdaBedrockGuardrailsStack(app, 'ApigwLambdaBedrockGuardrailsStack');
1 change: 1 addition & 0 deletions apigw-lambda-bedrock-guardrails-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "app": "npx ts-node --prefer-ts-exts bin/app.ts" }
41 changes: 41 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"title": "API Gateway to Lambda to Amazon Bedrock with Application-Level Guardrails",
"description": "Deploy a REST API that invokes Amazon Bedrock with per-request Guardrails for content and topic filtering at the application level — each API call is individually screened.",
"language": "TypeScript",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern applies Bedrock Guardrails at the application level — each InvokeModel call passes guardrailIdentifier and guardrailVersion, giving per-request content screening.",
"Unlike account-level enforcement (which applies to ALL Bedrock calls globally), this approach lets different APIs apply different guardrails or skip them entirely.",
"The guardrail includes content filters (sexual, violence, hate, misconduct) and topic filters (financial advice denied).",
"Blocked requests return a configurable message without consuming model tokens."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-bedrock-guardrails-cdk",
"templateURL": "serverless-patterns/apigw-lambda-bedrock-guardrails-cdk",
"projectFolder": "apigw-lambda-bedrock-guardrails-cdk",
"templateFile": "lib/apigw-lambda-bedrock-guardrails-stack.ts"
}
},
"resources": {
"bullets": [
{ "text": "Amazon Bedrock Guardrails", "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html" },
{ "text": "Using Guardrails with InvokeModel", "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-use-invoke-api.html" },
{ "text": "Content and topic filters", "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-content-filters.html" }
]
},
"deploy": { "text": ["cdk deploy"] },
"testing": { "text": ["See the README for testing instructions."] },
"cleanup": { "text": ["cdk destroy"] },
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS, passionate about serverless and AI/ML.",
"linkedin": "nithin-chandran-r"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as bedrock from 'aws-cdk-lib/aws-bedrock';
import { Construct } from 'constructs';

export class ApigwLambdaBedrockGuardrailsStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Bedrock Guardrail with content and topic filters
const guardrail = new bedrock.CfnGuardrail(this, 'AppGuardrail', {
name: 'app-content-guardrail',
blockedInputMessaging: 'Your request was blocked by our content policy.',
blockedOutputsMessaging: 'The response was blocked by our content policy.',
contentPolicyConfig: {
filtersConfig: [
{ type: 'SEXUAL', inputStrength: 'HIGH', outputStrength: 'HIGH' },
{ type: 'VIOLENCE', inputStrength: 'HIGH', outputStrength: 'HIGH' },
{ type: 'HATE', inputStrength: 'HIGH', outputStrength: 'HIGH' },
{ type: 'INSULTS', inputStrength: 'HIGH', outputStrength: 'MEDIUM' },
{ type: 'MISCONDUCT', inputStrength: 'HIGH', outputStrength: 'HIGH' },
],
},
topicPolicyConfig: {
topicsConfig: [
{
name: 'Financial Advice',
definition: 'Providing specific investment recommendations, stock picks, or financial planning advice.',
type: 'DENY',
},
],
},
});

// Guardrail version (required for InvokeModel)
const guardrailVersion = new bedrock.CfnGuardrailVersion(this, 'GuardrailVersion', {
guardrailIdentifier: guardrail.attrGuardrailId,
});

// Lambda function
const fn = new lambda.Function(this, 'BedrockGuardrailsFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('src'),
timeout: cdk.Duration.seconds(30),
memorySize: 256,
environment: {
MODEL_ID: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
GUARDRAIL_ID: guardrail.attrGuardrailId,
GUARDRAIL_VERSION: guardrailVersion.attrVersion,
},
});

fn.addToRolePolicy(new iam.PolicyStatement({
actions: ['bedrock:InvokeModel', 'bedrock:ApplyGuardrail'],
resources: [
'arn:aws:bedrock:*::foundation-model/*',
`arn:aws:bedrock:*:${this.account}:inference-profile/*`,
guardrail.attrGuardrailArn,
],
}));

// REST API
const api = new apigateway.RestApi(this, 'BedrockGuardrailsApi', {
restApiName: 'bedrock-guardrails-api',
description: 'API Gateway -> Lambda -> Bedrock with per-request Guardrails',
});

api.root.addResource('invoke').addMethod('POST', new apigateway.LambdaIntegration(fn));

new cdk.CfnOutput(this, 'ApiEndpoint', { value: api.url + 'invoke' });
new cdk.CfnOutput(this, 'GuardrailId', { value: guardrail.attrGuardrailId });
new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName });
}
}
16 changes: 16 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "apigw-lambda-bedrock-guardrails-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": { "build": "tsc", "cdk": "cdk" },
"dependencies": {
"aws-cdk-lib": "^2.180.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"typescript": "~5.4.0",
"ts-node": "^10.9.0",
"@types/node": "^20.0.0"
}
}
34 changes: 34 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');

const client = new BedrockRuntimeClient();

exports.handler = async (event) => {
const body = JSON.parse(event.body || '{}');
const prompt = body.prompt || 'Hello';

const response = await client.send(new InvokeModelCommand({
modelId: process.env.MODEL_ID,
contentType: 'application/json',
accept: 'application/json',
guardrailIdentifier: process.env.GUARDRAIL_ID,
guardrailVersion: process.env.GUARDRAIL_VERSION,
body: JSON.stringify({
anthropic_version: 'bedrock-2023-05-31',
max_tokens: 512,
messages: [{ role: 'user', content: prompt }],
}),
}));

const result = JSON.parse(new TextDecoder().decode(response.body));

return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
response: result.content?.[0]?.text,
stopReason: result.stop_reason,
guardrailAction: response.guardrailAction || 'NONE',
usage: result.usage,
}),
};
};
8 changes: 8 additions & 0 deletions apigw-lambda-bedrock-guardrails-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2020", "module": "commonjs", "lib": ["es2020"],
"declaration": true, "strict": true, "outDir": "build",
"rootDir": ".", "skipLibCheck": true, "forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules", "build"]
}