AWS Amplify helps you build and deploy generative AI applications by providing you with the tools needed to build a fullstack application that integrates with Amazon Bedrock. Amazon Bedrock is a fully managed service that provides access to Foundation Models (FMs) so you can build generative AI applications easily and not worry about managing AI infrastructure. Integrating generative AI capabilities into your application or building your own generative AI application requires more than just making calls to a foundation model. To build a fullstack generative AI application, in addition to Amazon Bedrock, you will need:
With Amplify and Amazon Bedrock, you can create a generative AI application easily in under an hour. This guide will walk you through the steps of building a generative AI sandbox.
For this we will use React and Next.js to take advantage of the full managed hosting of Next.js server-side rendering (SSR) capabilities in Amplify Hosting. The first step is to create a new Next.js project using yarn, npm, pnpm, or Bun.
Copy code example
npx create-next-app@latest
Copy code example
✔ Would you like to use TypeScript with this project? … No / Yes✔ Would you like to use ESLint with this project? … No / Yes✔ Would you like to use Tailwind CSS with this project? … No / Yes✔ Would you like to use `src/` directory with this project? … No / Yes✔ Would you like to use experimental `app/` directory with this project? … No / Yes✔ What import alias would you like configured? … @/*
For this project we will use the Next.js Pages router. Feel free to change the settings to your liking. Then go into your newly created Next.js app and add Amplify to your project with the Amplify CLI:
Copy code example
amplify init
Copy code example
? Enter a name for the project amplify-gen-aiThe following configuration will be applied: Project information| Name: amplify-gen-ai| Environment: dev| Default editor: Visual Studio Code| App type: javascript| Javascript framework: react| Source Directory Path: src| Distribution Directory Path: build| Build Command: npm run-script build| Start Command: npm run-script start? Initialize the project with the above configuration? Yes
While adding authentication to your app is optional, allowing anyone to use an LLM through your app without authenticating is not recommended in practice. This is because requests to an LLM incur costs based on the amount of text sent to and received from the LLM. Most LLMs, like Amazon Bedrock, also enforce rate limits.
You can add authentication to your application with the Amplify CLI or Amplify Studio. For this, we will use the CLI:
Copy code example
amplify add auth
The output from the CLI should look like this:
Copy code example
Using service: Cognito, provided by: awscloudformation The current configured provider is Amazon Cognito. Do you want to use the default authentication and security configuration? _**Default configuration**_ Warning: you will not be able to edit these selections. How do you want users to be able to sign in? _**Email**_ Do you want to configure advanced settings? _**No, I am done.**_
Then push your Amplify project to the cloud:
Copy code example
amplify push
Copy code example
✔ Successfully pulled backend environment dev from the cloud. Current Environment: dev ┌──────────┬──────────────────────┬───────────┬───────────────────┐│ Category │ Resource name │ Operation │ Provider plugin │├──────────┼──────────────────────┼───────────┼───────────────────┤│ Auth │ amplifyGenAi │ Create │ awscloudformation │└──────────┴──────────────────────┴───────────┴───────────────────┘
Add the frontend packages for Amplify and Amplify UI:
Copy code example
npm i --save @aws-amplify/ui-react aws-amplify
Then add these imports to the top of the src/pages/_app.tsx :
Copy code example
import '@aws-amplify/ui-react/styles.css'; import Amplify > from 'aws-amplify'; import awsconfig from '../aws-exports';
And then after the imports, add this to configure Amplify:
Copy code example
Amplify.configure( . awsconfig, // this lets you run Amplify code on the server-side in Next.js ssr: true >);
Then wrap the in JSX with the Authenticator component from '@aws-amplify/ui-react'. Your file should now look like this:
Copy code example
import '@aws-amplify/ui-react/styles.css'; import Amplify > from 'aws-amplify'; import awsconfig from '../aws-exports'; import type AppProps > from 'next/app'; import Authenticator > from '@aws-amplify/ui-react'; Amplify.configure( . awsconfig, // this lets you run Amplify code on the server-side in Next.js ssr: true >); export default function App( Component, pageProps >: AppProps) return ( Authenticator> Component . pageProps> /> /Authenticator> ); >
If you start up your local dev server, you should now see this:
Amplify authenticator login form with email and password fields and sign in button. " width="998" height="836" />
Create an account for yourself for testing. Once you log in, you should see the default Next.js starter homepage.
Add the Amazon Bedrock SDK to your dependencies:
Copy code example
npm i --save @aws-sdk/client-bedrock-runtime
The default Next.js template with the Pages router should come with an example API route at src/pages/api/hello.ts. Let’s rename that file to chat.ts. The first thing we will need to do in this file is configure Amplify on the server-side. To do that, you will import Amplify and your Amplify config file that Amplify creates for you.
Copy code example
import Amplify > from 'aws-amplify'; import awsconfig from '@/aws-exports'; Amplify.configure( . awsconfig, ssr: true >);
Then, to get the Authentication credentials, import withSSRContext from aws-amplify . Change the default handler function to an async function.
Copy code example
- export default function handler( + export default async function handler(
Then, in the body of the function, create an SSR context:
Copy code example
const SSR = withSSRContext( req >);
This will give you access to all the Amplify Library functionality on the server-side, like using the Auth module. To get the current credentials of the user, include the following:
Copy code example
const credentials = await SSR.Auth.currentCredentials();
To test that this is working, you can output the credentials to the console and boot up your local server again. Let’s do that to make sure everything is working so far. In src/pages/index.tsx, create a function called callAPI that does a fetch to our API endpoint:
Copy code example
const callAPI = () => fetch('/api/hello'); >;
Then in the JSX of the page, add a button that calls our callAPI function on click:
Copy code example
button onClick=callAPI>>Click mebutton>
If you are logged in and click the button, your authentication credentials should print to the console in your terminal.
Copy code example
fetch('/api/hello', method: 'POST', body: JSON.stringify( input: 'Who are you?' >) >);
Open src/pages/api/chat.ts and remove that console.log and initialize the Amazon Bedrock client with your user’s credentials:
Copy code example
const bedrock = new BedrockRuntimeClient( serviceId: 'bedrock', region: 'us-east-1', credentials >);
Then you can send the Bedrock client the InvokeModel command. Here is the full chat.ts file with comments for what it is doing.
Copy code example
import BedrockRuntimeClient, InvokeModelCommand > from '@aws-sdk/client-bedrock-runtime'; import Amplify, withSSRContext > from 'aws-amplify'; import type NextApiRequest, NextApiResponse > from 'next'; import awsExports from '@/aws-exports'; Amplify.configure( . awsExports, ssr: true >); export default async function handler( req: NextApiRequest, res: NextApiResponse ) const body = JSON.parse(req.body); const SSR = withSSRContext( req >); const credentials = await SSR.Auth.currentCredentials(); const bedrock = new BedrockRuntimeClient( serviceId: 'bedrock', region: 'us-east-1', credentials >); // Anthropic's Claude model expects a chat-like string // of 'Human:' and 'Assistant:' responses separated by line breaks. // You should always end your prompt with 'Assistant:' and Claude // will respond. There are various prompt engineering techniques // and frameworks like LangChain you can use here too. const prompt = `Human:$body.input>\n\nAssistant:`; const result = await bedrock.send( new InvokeModelCommand( modelId: 'anthropic.claude-v2', contentType: 'application/json', accept: '*/*', body: JSON.stringify( prompt, // LLM costs are measured by Tokens, which are roughly equivalent // to 1 word. This option allows you to set the maximum amount of // tokens to return max_tokens_to_sample: 2000, // Temperature (1-0) is how 'creative' the LLM should be in its response // 1: deterministic, prone to repeating // 0: creative, prone to hallucinations temperature: 1, top_k: 250, top_p: 0.99, // This tells the model when to stop its response. LLMs // generally have a chat-like string of Human and Assistant message // This says stop when the Assistant (Claude) is done and expects // the human to respond stop_sequences: ['\n\nHuman:'], anthropic_version: 'bedrock-2023-05-31' >) >) ); // The response is a Uint8Array of a stringified JSON blob // so you need to first decode the Uint8Array to a string // then parse the string. res.status(200).json(JSON.parse(new TextDecoder().decode(result.body))); >
Then update the callAPI function in your frontend to accept a JSON response:
Copy code example
fetch('/api/hello', method: 'POST', body: JSON.stringify( input: 'Who are you?' >) >) .then((res) => res.json()) .then((data) => console.log(data); >);
But if you click that button, you will see this in your terminal:
Copy code example
⨯ AccessDeniedException: User: arn:aws:sts::553879240338:assumed-role/amplify-amplifyGenAi-dev-205330-authRole/CognitoIdentityCredentials is not authorized to perform: bedrock:InvokeModel on resource:
When you create an Authentication backend with Amplify, it sets up Amazon Cognito and creates two IAM roles, one for unauthenticated users and another for authenticated users. These IAM roles let AWS know what resources your authenticated and guest users can access. We didn’t give our authenticated users access to Amazon Bedrock yet. Let’s do that now.
You can update the IAM policies Amplify creates with an override. Run this command:
Copy code example
amplify override project
Edit the override.ts file it creates and add a policy to allow the authenticated role access to Amazon Bedrock’s InvokeModel command like this:
Copy code example
import AmplifyProjectInfo, AmplifyRootStackTemplate > from '@aws-amplify/cli-extensibility-helper'; export function override( resources: AmplifyRootStackTemplate, amplifyProjectInfo: AmplifyProjectInfo ) const authRole = resources.authRole; const basePolicies = Array.isArray(authRole.policies) ? authRole.policies : [authRole.policies]; authRole.policies = [ . basePolicies, policyName: '', policyDocument: Version: '2012-10-17', Statement: [ Effect: 'Allow', Action: 'bedrock:InvokeModel', Resource: 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2' > ] > > ]; >
Run amplify push to sync your changes with the cloud. Now restart your local Next.js server and try to call Amazon Bedrock again through your API route.
The last thing to do is to add hosting to our Next.js application. Amplify has you covered there too. Create a new Git repository in GitHub, Bitbucket, or GitLab. Commit your code and push it to the git provider of your choice. Let’s use GitHub as an example. If you have the GitHub CLI installed, you can run:
Copy code example
gh repo create
Copy code example
? What would you like to do? Push an existing local repository to GitHub? Path to local repository .? Repository name amplify-gen-ai? Repository owner danny? Description? Visibility Private✓ Created repository danny/amplify-gen-ai on GitHub? Add a remote? Yes? What should the new remote be called? origin✓ Added remote git@github.com:danny/amplify-gen-ai.git? Would you like to push commits from the current branch to "origin"? Yes
Now we can add Amplify Hosting from the Amplify CLI:
Copy code example
amplify add hosting
Copy code example
✔ Select the plugin module to execute · Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment) ? Choose a type Continuous deployment (Git-based deployments)? Continuous deployment is configured in the Amplify Console. Please hit enter once you connect your repositoryAmplify hosting urls:┌──────────────┬───────────────────────────────────────────┐│ FrontEnd Env │ Domain │├──────────────┼───────────────────────────────────────────┤│ main │ https://main.dade6up5cdfhu.amplifyapp.com │
Now you have a hosted fullstack application using Amplify connected to Amazon Bedrock.
If you are using TypeScript, you will need to ignore the Amplify directory in the tsconfig file at the root of your project or you might get a type error when Amplify tries to build your Next.js application. Update the "exclude" array in your tsconfig to have "amplify":
Copy code example
"exclude": ["node_modules","amplify"]