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
28 changes: 28 additions & 0 deletions doc/dataSources.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,34 @@ appSync:

- `eventBusArn`: The ARN of the event bus

## Bedrock

```yaml
appSync:
dataSources:
myBedrock:
type: 'AMAZON_BEDROCK_RUNTIME'
config:
models:
- amazon.titan-text-lite-v1
- eu.amazon.nova-2-lite-v1:0
- arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.anthropic.claude-3-5-haiku-20241022-v1:0
```

### config

All fields are optional. When `config` is omitted entirely, the plugin still creates the data source and generates a default service role.

- `models`: Optional list of model identifiers used to scope the generated IAM policy. When omitted, the default role allows `bedrock:InvokeModel` and `bedrock:Converse` on `*`. Each entry is expanded as follows:
- A **bare foundation model ID** (e.g. `amazon.titan-text-lite-v1`) becomes `arn:${AWS::Partition}:bedrock:${region}::foundation-model/<id>`.
- A **cross-region inference profile ID** (prefixed with a geographic code such as `us.`, `eu.`, `apac.`, or `us-gov.` — e.g. `eu.amazon.nova-2-lite-v1:0`) is expanded into **two** ARNs: the inference profile in this account/region (`arn:${AWS::Partition}:bedrock:${region}:${AWS::AccountId}:inference-profile/<id>`) and the underlying foundation model across all regions the profile can route to (`arn:${AWS::Partition}:bedrock:*::foundation-model/<base-id>`). Models like Amazon Nova are only invokable through an inference profile, so use the profile ID here (and as the `modelId` in your resolver).
- A **full ARN** (or CloudFormation intrinsic function) is used as-is.
- `region`: AWS region used when expanding bare model IDs. Defaults to the stack region.
- `serviceRoleArn`: The service role ARN for this DataSource. If not provided, a new one will be created.
- `iamRoleStatements`: Statements to use for the generated IAM Role. If not provided, default statements will be used.

Resolvers invoke Bedrock through the `APPSYNC_JS` runtime using `InvokeModel` or `Converse` request objects. AppSync only supports synchronous invocations that complete within 10 seconds; streaming APIs are not supported. See the [AWS AppSync Bedrock resolver reference](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-bedrock-js.html) for request/response shapes and helper utilities.
Comment on lines +158 to +168
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Call out the Bedrock model-access prerequisite.

A copied quick start can still fail until the account has access to the selected foundation model in that region. Adding that note here would prevent a pretty confusing first-run failure path for users. (docs.aws.amazon.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doc/dataSources.md` around lines 157 - 164, Add a short prerequisite note in
the doc/dataSources.md near the `models` and `region` bullet points (the section
describing model IDs and ARN expansion) stating that the AWS account must have
access to the selected Bedrock foundation model in the chosen region before
using the plugin or copying the quick start; reference the Bedrock access
requirement and link or point to the AWS Nova invocation docs so readers know
where to request model access and avoid first-run failures.


## NONE

```yaml
Expand Down
41 changes: 41 additions & 0 deletions e2e/datasource-bedrock.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { synthesize } from './helpers/synthesize';
import {
expectDataSourceOfType,
findResourcesByType,
} from './helpers/assertions';

describe('examples/datasource-bedrock', () => {
let result: ReturnType<typeof synthesize>;

beforeAll(() => {
result = synthesize('examples/datasource-bedrock');
});

afterAll(() => {
result.cleanup();
});

it('creates an AMAZON_BEDROCK_RUNTIME data source', () => {
const ds = expectDataSourceOfType(
result.template,
'AMAZON_BEDROCK_RUNTIME',
);
expect(ds.resource.Properties?.Name).toBe('bedrock');
});

it('generates a service role with bedrock:InvokeModel permissions', () => {
const roles = findResourcesByType(result.template, 'AWS::IAM::Role');
const bedrockRole = roles.find(({ resource }) => {
const policies = resource.Properties?.Policies as Array<{
PolicyDocument?: { Statement?: Array<{ Action?: string[] }> };
}>;
return policies?.some((policy) =>
policy.PolicyDocument?.Statement?.some((statement) =>
statement.Action?.includes('bedrock:InvokeModel'),
),
);
});

expect(bedrockRole).toBeDefined();
Comment on lines +26 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Tighten this IAM assertion to the Bedrock datasource role.

This passes if any synthesized role has bedrock:InvokeModel, so it can miss regressions in the datasource’s own default role. It also leaves bedrock:Converse untested even though that permission is part of the new Bedrock role contract in this PR context.

💡 Suggested fix
   it('generates a service role with bedrock:InvokeModel permissions', () => {
-    const roles = findResourcesByType(result.template, 'AWS::IAM::Role');
-    const bedrockRole = roles.find(({ resource }) => {
-      const policies = resource.Properties?.Policies as Array<{
-        PolicyDocument?: { Statement?: Array<{ Action?: string[] }> };
-      }>;
-      return policies?.some((policy) =>
-        policy.PolicyDocument?.Statement?.some((statement) =>
-          statement.Action?.includes('bedrock:InvokeModel'),
-        ),
-      );
-    });
+    const ds = expectDataSourceOfType(result.template, 'AMAZON_BEDROCK_RUNTIME');
+    const roleLogicalId = (ds.resource.Properties?.ServiceRoleArn as {
+      'Fn::GetAtt'?: [string, string];
+    })?.['Fn::GetAtt']?.[0];
+
+    expect(roleLogicalId).toBeDefined();
+
+    const bedrockRole = roleLogicalId
+      ? result.template.Resources?.[roleLogicalId]
+      : undefined;
+
+    const policies = bedrockRole?.Properties?.Policies as Array<{
+      PolicyDocument?: { Statement?: Array<{ Action?: string | string[] }> };
+    }>;
+
+    const hasBedrockPermissions = policies?.some((policy) =>
+      policy.PolicyDocument?.Statement?.some((statement) => {
+        const actions = Array.isArray(statement.Action)
+          ? statement.Action
+          : statement.Action
+            ? [statement.Action]
+            : [];
+        return (
+          actions.includes('bedrock:InvokeModel') &&
+          actions.includes('bedrock:Converse')
+        );
+      }),
+    );
 
-    expect(bedrockRole).toBeDefined();
+    expect(hasBedrockPermissions).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/datasource-bedrock.e2e.test.ts` around lines 26 - 39, Narrow the IAM
assertion so it targets the Bedrock datasource role specifically (instead of any
role) by enhancing the predicate in the bedrockRole lookup: use
findResourcesByType and then filter roles where the role clearly belongs to the
datasource (for example check resource.Properties.RoleName or
resource.Metadata['aws:cdk:path'] or another datasource-specific identifier
present on the role) and then assert that that role's Policies contain both
'bedrock:InvokeModel' and 'bedrock:Converse'; update the expect to verify that
this specific bedrock datasource role is defined and that its PolicyDocument
Statement Actions include both permissions.

});
});
1 change: 1 addition & 0 deletions e2e/helpers/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export function expectDataSourceOfType(
| 'HTTP'
| 'RELATIONAL_DATABASE'
| 'AMAZON_EVENTBRIDGE'
| 'AMAZON_BEDROCK_RUNTIME'
| 'NONE',
): { logicalId: string; resource: CfnResource } {
return expectResourceWithProperties(template, 'AWS::AppSync::DataSource', {
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ stay current with the plugin's actual behavior — if they break, CI fails.
| [datasource-http](./datasource-http/) | HTTP data source with optional IAM signing |
| [datasource-none](./datasource-none/) | NONE data source (local resolvers) |
| [datasource-eventbridge](./datasource-eventbridge/) | EventBridge data source |
| [datasource-bedrock](./datasource-bedrock/) | Amazon Bedrock runtime data source for synchronous model invocations |
| [datasource-opensearch](./datasource-opensearch/) | Amazon OpenSearch Service data source |
| [datasource-rds](./datasource-rds/) | Relational Database (Aurora Serverless) data source |
| [caching](./caching/) | Server-side caching configuration |
Expand Down
19 changes: 19 additions & 0 deletions examples/datasource-bedrock/resolvers/summarize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { invokeModel } from '@aws-appsync/utils/ai';

export function request(ctx) {
return invokeModel({
modelId: 'eu.amazon.nova-micro-v1:0',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba examples/datasource-bedrock/resolvers/summarize.js | sed -n '1,80p'
nl -ba examples/datasource-bedrock/serverless.yml | sed -n '1,260p'
rg -n "datasourceBedrockConfig|models:|modelId" examples/datasource-bedrock -C2

Repository: sid88in/serverless-appsync-plugin

Length of output: 120


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== summarize.js (first 120 lines) ==="
cat -n examples/datasource-bedrock/resolvers/summarize.js | sed -n '1,120p'

echo
echo "=== serverless.yml (first 320 lines) ==="
cat -n examples/datasource-bedrock/serverless.yml | sed -n '1,320p'

echo
echo "=== Search bedrock datasource config for allowlist + model id ==="
rg -n "datasourceBedrockConfig|models:|modelId" examples/datasource-bedrock -C2

Repository: sid88in/serverless-appsync-plugin

Length of output: 2207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate plugin code that handles AMAZON_BEDROCK_RUNTIME and "models" allowlist
ls -la
rg -n "AMAZON_BEDROCK_RUNTIME|bedrock.*models|config\.models|invokeModel\(" -S . --glob='*.js' --glob='*.ts' --glob='*.mjs' --glob='*.cjs'

Repository: sid88in/serverless-appsync-plugin

Length of output: 3821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate plugin code that handles AMAZON_BEDROCK_RUNTIME and "models" allowlist
ls -la
rg -n "AMAZON_BEDROCK_RUNTIME|bedrock.*models|config\.models|invokeModel\(" -S . --glob='*.js' --glob='*.ts' --glob='*.mjs' --glob='*.cjs'

Repository: sid88in/serverless-appsync-plugin

Length of output: 3821


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the AMAZON_BEDROCK_RUNTIME handling and any model allowlist logic
rg -n "case 'AMAZON_BEDROCK_RUNTIME'|config\.models|models" src/resources/DataSource.ts -n
sed -n '380,520p' src/resources/DataSource.ts

Repository: sid88in/serverless-appsync-plugin

Length of output: 4207


Align invokeModel.modelId with the Bedrock models allowlist

examples/datasource-bedrock/resolvers/summarize.js calls invokeModel with modelId: 'eu.amazon.nova-micro-v1:0', but examples/datasource-bedrock/serverless.yml allows only dataSources.bedrock.config.models: ['amazon.nova-micro-v1:0']. Since eu.* is treated as an inference-profile ID by the plugin, the IAM policy won’t include the inference-profile/... resource unless that exact ID is allowlisted, which can deny bedrock:InvokeModel at runtime.

  • Add eu.amazon-nova-micro-v1:0 to serverless.yml’s dataSources.bedrock.config.models, or change the resolver to use amazon.nova-micro-v1:0.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/datasource-bedrock/resolvers/summarize.js` at line 5, The resolver
uses invokeModel.modelId 'eu.amazon.nova-micro-v1:0' which doesn't match the
Bedrock allowlist in dataSources.bedrock.config.models; update one side so they
align: either add 'eu.amazon-nova-micro-v1:0' (or the plugin-expected
inference-profile ID format) to the serverless.yml
dataSources.bedrock.config.models allowlist, or change the resolver's
invokeModel.modelId to 'amazon.nova-micro-v1:0' so it matches the existing
allowlist; modify the modelId in
examples/datasource-bedrock/resolvers/summarize.js or the models array in
serverless.yml accordingly.

body: {
messages: [
{
role: 'user',
content: [{ text: ctx.args.text }],
},
],
},
});
}

export function response(ctx) {
return ctx.result.output.message.content[0].text;
}
Comment on lines +1 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Show the resolver file and any nearby examples for invokeModel usage (Titan vs Nova).
repo_root="."
file="examples/datasource-bedrock/resolvers/summarize.js"

echo "== File: $file =="
if [ -f "$file" ]; then
  nl -ba "$file" | sed -n '1,200p'
else
  echo "Missing file: $file"
fi

echo
echo "== Find other invokeModel usages under examples/datasource-bedrock/resolvers =="
# List resolver files
fd -t f -e js examples/datasource-bedrock/resolvers | sort || true
echo
# Search for invokeModel calls
rg -n "invokeModel\\(" examples/datasource-bedrock/resolvers || true

echo
echo "== Search for ctx.result usage in these resolvers =="
rg -n "ctx\\.result" examples/datasource-bedrock/resolvers || true

Repository: sid88in/serverless-appsync-plugin

Length of output: 1988


🌐 Web query:

Amazon Bedrock Nova invokeModel messages inferenceConfig response shape ctx.result.output vs results

💡 Result:

When using the Amazon Bedrock InvokeModel API with Amazon Nova models, the response body follows a specific schema where the generated content is located under the output key, not a results key [1]. The response structure returned by the model in the body of the InvokeModel response is as follows [1]: { "output": { "message": { "role": "assistant", "content": [ { "text": "string" }, { "toolUse": {... } }, { "reasoningContent": {... } } ] } }, "stopReason": "string", "usage": { "inputTokens": int, "outputTokens": int, "totalTokens": int } } Key points regarding the response structure: 1. output: This is the primary container for the assistant's response. It contains a message object, which holds the role (assistant) and a content array [1]. 2. content: This array contains the actual response blocks, such as text, toolUse, or reasoningContent [1]. 3. No results key: The Nova models' Invoke API schema does not utilize a top-level results key. If you are seeing code snippets referencing results, they likely pertain to different model providers or older legacy Bedrock response formats [2]. 4. Accessing the output: To access the generated text in Python using Boto3, you should read the response body and traverse the JSON structure [3][4]: response = client.invoke_model(modelId="amazon.nova-micro-v1:0", body=json.dumps(request_body)) response_body = json.loads(response["body"].read) text_output = response_body["output"]["message"]["content"]["text"] This schema is designed to be consistent with the Converse API, providing a unified experience for developers regardless of which Bedrock API they choose [5][6].

Citations:


🌐 Web query:

@aws-appsync/utils/ai invokeModel ctx.result structure output vs results

💡 Result:

In AWS AppSync JavaScript resolvers using @aws-appsync/utils/ai, the Bedrock model invocation response is accessed via the context object as ctx.result [1][2]. Structure of ctx.result The structure of ctx.result is determined by the specific Amazon Bedrock model being invoked [2][3]. When you use invokeModel, AppSync passes the response from the Bedrock InvokeModel API directly into ctx.result [2]. Because different models return different response bodies (e.g., Anthropic Claude, Amazon Titan, or Meta Llama), the shape of ctx.result varies [2][3]. Common Access Patterns For many legacy or specific Amazon Titan models, developers often access the output using paths such as ctx.result.results.outputText [2][4]. However, there is no single universal output structure, as it depends on the model's inference response format [2][3]. You should inspect the specific JSON response body of the model you are using in the Amazon Bedrock API documentation to determine the correct path for your data [2][3]. Key Considerations 1. Direct API Mapping: Because ctx.result reflects the Bedrock API response directly, there is no generic "output" field guaranteed across all models [2]. 2. Troubleshooting: If you are unsure of the structure, you can log the entire ctx.result object in your AppSync logs during development to inspect the returned JSON schema. 3. Converse vs. InvokeModel: If you are using the newer Converse API (often preferred for its unified interface across models), the response structure differs significantly from the older InvokeModel API, typically nesting data within response.message.content [5]. Always ensure your resolver response handler maps the model-specific response structure to the schema expected by your GraphQL type [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Show the resolver file and any nearby examples for invokeModel usage (Titan vs Nova).
repo_root="."
file="examples/datasource-bedrock/resolvers/summarize.js"

echo "== File: $file =="
if [ -f "$file" ]; then
  nl -ba "$file" | sed -n '1,200p'
else
  echo "Missing file: $file"
fi

echo
echo "== Find other invokeModel usages under examples/datasource-bedrock/resolvers =="
# List resolver files
fd -t f -e js examples/datasource-bedrock/resolvers | sort || true
echo
# Search for invokeModel calls
rg -n "invokeModel\\(" examples/datasource-bedrock/resolvers || true

echo
echo "== Search for ctx.result usage in these resolvers =="
rg -n "ctx\\.result" examples/datasource-bedrock/resolvers || true

Repository: sid88in/serverless-appsync-plugin

Length of output: 1988


🌐 Web query:

Amazon Bedrock Nova invokeModel messages inferenceConfig response shape (outputText vs results[0].outputText)

💡 Result:

The response shape you should use depends entirely on whether you are using the Converse API or the legacy InvokeModel API. For Amazon Nova models on Amazon Bedrock, AWS recommends using the Converse API [1][2]. The InvokeModel API is supported but follows a different, model-specific response structure [1]. 1. Converse API Response Shape When using the Converse API, the response format is standardized across models [2]. The text content is accessed via a nested structure: response['output']['message']['content']['text'] [3][4][2] 2. InvokeModel API Response Shape When using the InvokeModel API, the response body structure depends on the specific model's native schema [1][5]. - If you were using older models like Amazon Titan, you might be accustomed to the results['outputText'] path [6][7]. - However, for Amazon Nova models invoked via InvokeModel, the structure aligns with the Converse schema, typically returning an output object containing the message [8][1]: response_body['output']['message']['content']['text'] Summary If you are writing code for Amazon Nova, avoid relying on results['outputText'], as this is specific to older models like Amazon Titan [6][7]. You should use the Converse API structure [1][2], which is consistent: response['output']['message']['content']['text'] [3][4][2]

Citations:


🌐 Web query:

@aws-appsync/utils/ai invokeModel response fields ctx.result output results

💡 Result:

In AWS AppSync JavaScript resolvers using @aws-appsync/utils/ai invokeModel, the Bedrock runtime response is available on the resolver response context as ctx.result. The response helper returns ctx.result.results.outputText for the first invocation result (in the examples/docs). 1) ctx.result is the container for the data returned by the data source (available in response handlers) ctx.result is described as “a container for the results of this resolver” in the AppSync resolver context reference. [1] 2) For invokeModel, the text output is typically at ctx.result.results.outputText The Bedrock JS resolver reference shows an invokeModel response handler returning ctx.result.results.outputText. [2] Also, in a community example handling invokeModel, they parse JSON from ctx.result.results.outputText. [3] 3) Where “ctx.result.output …” comes from (Converse vs invokeModel) A community thread shows accessing ctx.result.output.message.content.text in a resolver that uses converse (not invokeModel), and notes similar behavior was exhibited by invokeModel in their case. Because this is a community issue (not the official resolver-reference example), treat this “output.message…” shape as model/adapter-dependent and verify with your actual invocation output. [4] Practical guidance for response mapping - If you used invokeModel via @aws-appsync/utils/ai, start with: - ctx.result.results.outputText [2] - If you need a different field path (e.g., ctx.result.output.message.content.text), inspect/print the full ctx.result from your resolver logs first, since the exact shape can vary by operation/model. [1][4]

Citations:


🌐 Web query:

@aws-appsync/utils/ai invokeModel Amazon Nova ctx.result structure output results

💡 Result:

When using the @aws-appsync/utils/ai invokeModel helper in AWS AppSync, the structure of the returned ctx.result is determined by the underlying Amazon Bedrock InvokeModel API response [1][2]. Because invokeModel performs a low-level invocation of the model, the response is generally contained within the body field of the Bedrock API response [2]. The specific structure of the data inside ctx.result.body depends entirely on the model being called (in this case, Amazon Nova) [2][3]. For Amazon Nova, the model's response is typically returned as a JSON object [3]. Because Amazon Bedrock returns the body as a blob or a stream of bytes, you must parse the response body in your AppSync response resolver to access the data effectively [2][3]. Key points for implementation: 1. Response Access: The response from Bedrock is stored in ctx.result. When using invokeModel, you generally access the returned JSON data by parsing the body [2][3]. 2. Parsing: Depending on your runtime environment, you may need to use util.parseJson(ctx.result.body) to convert the raw response body into a usable JavaScript object [1]. 3. Model-Specific Output: Amazon Nova provides structured output based on how you prompt it (e.g., requesting JSON format) [3]. Ensure you have defined your prompt to return the desired format, as the invokeModel helper simply passes back the raw Bedrock response [1][2]. Example Resolver Structure: // response resolver export function response(ctx) { // Parse the Bedrock response body const body = util.parseJson(ctx.result.body); // Access the specific field based on the expected Nova output structure // For example, if Nova returns { "content": "..." } return body.content; } Note: If you encounter issues with ctx.result structure, ensure that you are not confusing the invokeModel helper with the converse helper [4][1]. The converse helper provides a more standardized response structure (e.g., ctx.result.output.message.content) that is abstracted from the underlying model's raw body [4]. If you require a consistent, model-agnostic response format, consider using the converse helper instead of invokeModel [4][1].

Citations:


🌐 Web query:

AppSync bedrock js invokeModel Amazon Nova request body messages inferenceConfig

💡 Result:

When invoking Amazon Nova models via AWS AppSync using the invokeModel function, you must provide a request body that adheres to the specific schema expected by the Amazon Nova Invoke API [1]. Unlike the simpler text-based prompts used for legacy models, the Amazon Nova Invoke API requires a structured JSON payload that includes the message history and configuration [1]. To perform this in an AppSync JavaScript resolver, your invokeModel request object should be structured as follows: import { invokeModel } from '@aws-appsync/utils/ai'; export function request(ctx) { return invokeModel({ modelId: 'amazon.nova-micro-v1:0', // Or your specific Nova model ID body: { schemaVersion: 'messages-v1', messages: [ { role: 'user', content: [{ text: 'Your prompt here' }] } ], inferenceConfig: { maxTokens: 500, temperature: 0.7, topP: 0.9 } } }); } Key Requirements: 1. Schema Version: You must include "schemaVersion": "messages-v1" in the body [1]. 2. Messages: The input must be provided as an array of message objects, each containing a role ("user" or "assistant") and content [1]. 3. InferenceConfig: This object holds your inference parameters, such as maxTokens, temperature, topP, and topK [1][2]. 4. System Prompts: If you need to provide system-level instructions, you can include a system array at the top level of the body: "system": [{"text": "Your system prompt here"}] [3][1]. While the AppSync @aws-appsync/utils/ai library provides convenient helpers, ensuring the body structure matches the Nova-specific Invoke API requirements is critical for successful inference [1]. Note that for conversational applications where you need to maintain state, AWS recommends using the Converse API if possible, which is also supported in AppSync via the converse helper function [4][1][5].

Citations:


🌐 Web query:

docs.aws.amazon.com nova invokeModel request body messages inferenceConfig example

💡 Result:

To invoke Amazon Nova models using the Amazon Bedrock InvokeModel API, you must provide a JSON-formatted request body that includes the messages structure and an optional inferenceConfig object [1][2][3]. The InvokeModel API requires the contentType header to be set to application/json [3][4]. Request Body Structure Example Below is a standard example of the JSON request body structure for Amazon Nova [2][5]: { "schemaVersion": "messages-v1", "messages": [ { "role": "user", "content": [ { "text": "Describe the purpose of a 'hello world' program in one line." } ] } ], "inferenceConfig": { "maxTokens": 512, "temperature": 0.5, "topP": 0.9 } } Key Components 1. messages: An array of objects representing conversational turns [1][6]. Each object must include: - role: Typically "user" or "assistant" [1][5]. - content: An array of content blocks, such as text, images, or videos [6][7]. 2. inferenceConfig: Optional parameters to control model generation [6][5]: - maxTokens: The maximum number of tokens to generate [6]. - temperature: Controls randomness (valid range: 0.00001 to 1.0) [6]. - topP: Nucleus sampling probability cutoff (valid range: 0 to 1) [6]. - topK: Limits sampling to the top K options (valid range: 0 to 128) [6]. 3. schemaVersion: Recommended as "messages-v1" to ensure correct schema interpretation [5]. Note: Amazon Nova models also support system prompts, which can be included in the request body using a top-level "system" field [1]. For more complex inputs, such as images or videos, the content block supports specific source structures (e.g., base64-encoded strings for the Invoke API) [6][7].

Citations:


🌐 Web query:

resolver-reference-bedrock-js invokeModel ctx.result.results.outputText example

💡 Result:

In AWS AppSync’s resolver reference (bedrock-js utilities), the pattern for reading the text returned from a Bedrock InvokeModel call is: return ctx.result.results.outputText from your response function. For example, the docs show response(ctx) { return ctx.result.results.outputText }. [1] Context / why this path: - The AppSync Bedrock invokeModel helper stores the Bedrock invocation’s response in ctx.result (and the docs’ response example reads from ctx.result.results.outputText). [1] - The underlying Bedrock runtime InvokeModel API returns an inference response body, but the AppSync resolver reference is what maps that into ctx.result.* fields for GraphQL resolvers. [2][1] Concrete example from the AppSync resolver reference: export function response(ctx) { return ctx.result.results.outputText } [1] If you instead want the raw mapped result object, the same page also shows a response(ctx) { return ctx.result } example. [1]

Citations:


Fix Amazon Nova Bedrock invokeModel request/response in summarize.js

  • The current request uses Titan-style inputText, but amazon.nova-micro-v1:0 InvokeModel expects a Nova messages payload (schemaVersion: "messages-v1", messages: [{ role, content: [{ text }] }], optionally inferenceConfig), so the request body is incompatible with the selected model.
  • The current response returns ctx.result.results; for Bedrock invokeModel the resolver reference maps the generated text to ctx.result.results.outputText, so extract outputText instead of returning the entire results object.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/datasource-bedrock/resolvers/summarize.js` around lines 1 - 14, The
request() currently sends Titan-style inputText; update the invokeModel body for
modelId 'amazon.nova-micro-v1:0' to use the Nova messages payload: include
schemaVersion: "messages-v1" and a messages array (e.g. a single message with
role "user" and content containing the text to summarize), and optionally add an
inferenceConfig if needed; in response(), return the generated text from
ctx.result.results.outputText instead of returning ctx.result.results. Make
these changes in the request and response functions to match the Bedrock Nova
messages schema and extract outputText.

3 changes: 3 additions & 0 deletions examples/datasource-bedrock/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
summarize(text: String!): AWSJSON
}
29 changes: 29 additions & 0 deletions examples/datasource-bedrock/serverless.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
service: appsync-datasource-bedrock

provider:
name: aws
runtime: nodejs22.x

plugins:
- serverless-appsync-plugin

appSync:
name: datasource-bedrock
authentication:
type: API_KEY
apiKeys:
- name: default

resolvers:
Query.summarize:
kind: UNIT
dataSource: bedrock
code: ./resolvers/summarize.js

dataSources:
bedrock:
type: AMAZON_BEDROCK_RUNTIME
description: Amazon Bedrock runtime for synchronous model invocations
config:
models:
- amazon.nova-micro-v1:0
Loading
Loading