Introduction
This project demonstrates the creation of a serverless recipe generator web application using AWS Amplify and Amazon Bedrock. By entering a list of ingredients, users receive unique, AI-generated recipes through a user-friendly, HTML-based interface. This app showcases the power of Generative AI to enhance user engagement and streamline recipe generation.
Using AWS Amplify for hosting, Amazon Bedrock for recipe generation, Cognito for authentication, and Lambda for backend processing, we’ll explore a complete solution from deployment to secure, scalable operations.
Tech Stack
- AWS Amplify: Full-stack hosting and continuous deployment for the frontend.
- Amazon Bedrock (Claude 3 Sonnet): Provides the Generative AI capabilities for recipe generation.
- AWS AppSync: Manages GraphQL APIs for real-time communication between the frontend and backend.
- AWS Cognito: Facilitates secure user authentication.
- AWS Lambda: Runs serverless backend functions to process user inputs.
- Node.js and npm: Required for running the React app and managing dependencies.
- Git & GitHub: Version control and hosting for continuous deployment via Amplify.
Prerequisites
To follow along with this tutorial, you will need:
- AWS Account: Requires permissions for Amplify, Cognito, Bedrock, and Lambda.
- AWS CLI: Needed for resource management.
- Node.js & npm: For running the React app.
- Amplify CLI: For initializing and configuring the Amplify project.
- Basic AWS Knowledge: Familiarity with Amplify, Cognito, AppSync, and Lambda services.
- GitHub Repository: To enable Amplify’s continuous deployment integration.
Problem Statement or Use Case
Problem: Searching for suitable recipes based on available ingredients can be time-consuming, and manually sifting through results often yields non-personalized options.
Solution: This serverless recipe generator offers a real-time AI-powered solution, allowing users to input ingredients and receive tailored recipes instantly. The use of Amazon Bedrock makes it possible to generate diverse, personalized recipes on demand, saving users time and enhancing their cooking experience.
Real-World Relevance: This type of application has potential in e-commerce, cooking platforms, and smart home solutions. By integrating Generative AI and serverless architecture, we demonstrate a scalable, real-time solution for creating personalized recipes or content.
Architecture Diagram
The architecture diagram below provides an overview of the components:
Component Breakdown
Frontend (React Application):
User-facing interface where users input ingredients and receive generated recipes. Hosted on AWS Amplify with continuous deployment.AWS Cognito:
Manages secure authentication for users, ensuring only authorized access to backend resources.AWS AppSync:
Provides the GraphQL API to handle communication between the frontend and backend services.AWS Lambda:
Serverless function that takes user input and forwards it to Amazon Bedrock for AI-powered recipe generation.Amazon Bedrock (Claude 3 Sonnet):
Utilizes generative AI to create unique recipes based on input ingredients.
Step-by-Step Implementation
Task 1: Host a Static Website
In this task, you will create a React application and deploy it to the Cloud using AWS Amplify.
Step 1: Create a New React Application
- In a new terminal or command line window, run the following command to use Vite to create a React application:
npm create vite@latest ai-recipe-generator -- --template react-ts -y
- Navigate to the project directory:
cd ai-recipe-generator
- Install the necessary dependencies:
npm install
- Start the development server:
npm run dev
This will start your React application locally. You should see the Vite + React app running.
- In the terminal window, open the local link to view the application.
Step 2: Initialize a GitHub Repository
In this step, you will create a GitHub repository and commit your code to it.
Sign in to GitHub at GitHub.
-
Create a new repository:
- For Repository name, enter
ai-recipe-generator
. - Choose the Public radio button.
- Select Create a new repository.
- For Repository name, enter
Open a new terminal window, navigate to your project’s root folder (
ai-recipe-generator
), and run the following commands to initialize Git and push the application to your GitHub repository:
git init
git add .
git commit -m "first commit"
git remote add origin git@github.com:<your-username>/ai-recipe-generator.git
git branch -M main
git push -u origin main
Replace the git@github.com:<your-username>/ai-recipe-generator.git
with your own GitHub SSH URL.
Step 3: Install the Amplify Packages
- Open a new terminal window, navigate to your app's root folder (
ai-recipe-generator
), and run the following command:
npm create amplify@latest -y
This will scaffold a lightweight Amplify project in your app’s directory.
- After installation, push the changes to GitHub:
git add .
git commit -m 'installing amplify'
git push origin main
Step 4: Deploy Your App with AWS Amplify
Sign in to the AWS Management Console and open the AWS Amplify console at AWS Amplify.
Click Create new app.
On the Start building with Amplify page, select GitHub for Deploy your app and click Next.
Authenticate with GitHub. You will be automatically redirected back to the Amplify console. Choose the repository and main branch you created earlier, then select Next.
Leave the default build settings and click Next.
Review your inputs and click Save and deploy.
AWS Amplify will now build your source code and deploy your app at https://...amplifyapp.com
. Your app will automatically update with every Git push. It may take up to 5 minutes to deploy.
- Once the build is complete, click Visit deployed URL to see your web app live.
Task 2: Manage Users
In this task, you will configure Amplify Auth for user authentication and enable Amazon Bedrock foundation model access.
Step 1: Set up Amplify Auth
- The app uses email as the default login mechanism. When users sign up, they will receive a verification email. Customize the verification email by updating the following code in the
ai-generated-recipes/amplify/auth/resource.ts
file:
import { defineAuth } from "@aws-amplify/backend";
export const auth = defineAuth({
loginWith: {
email: {
verificationEmailStyle: "CODE",
verificationEmailSubject: "Welcome to the AI-Powered Recipe Generator!",
verificationEmailBody: (createCode) =>
`Use this code to confirm your account: ${createCode()}`,
},
},
});
Save the file.
The image on the right shows an example of the customized verification email.
Step 2: Set up Amazon Bedrock Model Access
- Sign in to the AWS Management Console and open the Amazon Bedrock console at AWS Amazon Bedrock.
Verify that you are in the N. Virginia (us-east-1) region, and select Get started.
In the Foundation models section, choose the Claude model.
Scroll down to the Claude models section, select the Claude 3 Sonnet tab, and click Request model access.
- If you already have access to some models, the button will show Manage model access instead.
In the Base models section, for Claude 3 Sonnet, choose Available to request, then select Request model access.
On the Edit model access page, click Next.
On the Review and Submit page, click Submit.
Now your application should be up and running, with user authentication and AI-powered recipe generation using Amazon Bedrock's Claude 3 Sonnet.
Here's the reformatted version of Task 3: Build a Serverless Backend and Task 4: Deploy the Backend API, with improved structure and readability for a Medium blog post:
Task 3: Build a Serverless Backend
In this task, you will use AWS Amplify and AWS Lambda to build a serverless function.
Step 1: Create a Lambda Function for Handling Requests
On your local machine, navigate to the
ai-recipe-generator/amplify/data
folder, and create a new file namedbedrock.js
.Update the file with the following code:
The code defines a request function that constructs the HTTP request to invoke the Claude 3 Sonnet foundation model in Amazon Bedrock. The response function parses the response and returns the generated recipe.
export function request(ctx) {
const { ingredients = [] } = ctx.args;
// Construct the prompt with the provided ingredients
const prompt = `Suggest a recipe idea using these ingredients: ${ingredients.join(", ")}.`;
// Return the request configuration
return {
resourcePath: `/model/anthropic.claude-3-sonnet-20240229-v1:0/invoke`,
method: "POST",
params: {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1000,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `\n\nHuman: ${prompt}\n\nAssistant:`,
},
],
},
],
}),
},
};
}
export function response(ctx) {
// Parse the response body
const parsedBody = JSON.parse(ctx.result.body);
// Extract the text content from the response
const res = {
body: parsedBody.content[0].text,
};
// Return the response
return res;
}
The request
function constructs the AI prompt and sends it to Amazon Bedrock. The response
function parses the generated recipe from the returned JSON.
Step 2: Add Amazon Bedrock as a Data Source
- Open the
amplify/backend.ts
file and add the following code. Save the file.
The code adds an HTTP data source for Amazon Bedrock to your API and grants it the necessary permissions to invoke the Claude model.
import { defineBackend } from "@aws-amplify/backend";
import { data } from "./data/resource";
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
import { auth } from "./auth/resource";
const backend = defineBackend({
auth,
data,
});
const bedrockDataSource = backend.data.resources.graphqlApi.addHttpDataSource(
"bedrockDS",
"https://bedrock-runtime.us-east-1.amazonaws.com",
{
authorizationConfig: {
signingRegion: "us-east-1",
signingServiceName: "bedrock",
},
}
);
bedrockDataSource.grantPrincipal.addToPrincipalPolicy(
new PolicyStatement({
resources: [
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0",
],
actions: ["bedrock:InvokeModel"],
})
);
This code integrates Amazon Bedrock into your backend, enabling the serverless app to invoke the Claude 3 Sonnet model.
Task 4: Deploy the Backend API
Step 1: Set Up Amplify Data
- In the
amplify/backend.ts
file, define the schema and backend resources as follows:
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
const schema = a.schema({
BedrockResponse: a.customType({
body: a.string(),
error: a.string(),
}),
askBedrock: a
.query()
.arguments({ ingredients: a.string().array() })
.returns(a.ref("BedrockResponse"))
.authorization((allow) => [allow.authenticated()])
.handler(
a.handler.custom({ entry: "./bedrock.js", dataSource: "bedrockDS" })
),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "apiKey",
apiKeyAuthorizationMode: {
expiresInDays: 30,
},
},
});
In this step, you define a GraphQL schema and link the askBedrock
query to the custom Lambda function you created earlier (bedrock.js
). The schema allows authenticated users to invoke the function and retrieve a recipe suggestion.
Step 2: Deploy Cloud Resources
- Open a new terminal window, navigate to your app's project folder (
ai-recipe-generator
), and run the following command to deploy cloud resources into an isolated development space:
npx ampx sandbox
This command sets up a sandbox environment where you can quickly iterate on your changes.
- After the sandbox has been fully deployed, you will see a confirmation message in the terminal, and an
amplify_outputs.json
file will be generated and added to your project.
The deployment process is now complete, and you can begin interacting with your serverless backend.
The serverless backend is now set up and ready to handle recipe generation requests using Amazon Bedrock. You've created a Lambda function to handle interactions with the Claude 3 Sonnet model, integrated it with AWS Amplify, and deployed it to the cloud.
Here's how you can present Task 5: Build the Frontend and the challenges faced in the process for your blog:
Task 5: Build the Frontend
Now that the serverless backend is set up, it's time to create the frontend. This step will guide you through building the user interface (UI) using AWS Amplify libraries, styled components, and React. We’ll also implement authentication to ensure that only authorized users can access the app.
Step 1: Install the Amplify Libraries
To get started with integrating AWS Amplify into your frontend, you'll need to install the core Amplify libraries. The aws-amplify
library provides all the necessary APIs to interact with your backend, while @aws-amplify/ui-react
contains pre-built UI components that help you scaffold the authentication flow and other UI elements.
- Open a new terminal window, navigate to your project’s root folder (
ai-recipe-generator
), and run the following command to install the libraries:
npm install aws-amplify @aws-amplify/ui-react
Step 2: Style the App UI
Next, we’ll style the frontend to create a clean, modern interface. We'll focus on centering the layout and styling the form where users will input their ingredients.
- Open
ai-recipe-generator/src/index.css
, and update it with the following code to set global styles and center the UI:
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color: rgba(255, 255, 255, 0.87);
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
.card {
padding: 2em;
}
.box:nth-child(3n + 1) {
grid-column: 1;
}
.box:nth-child(3n + 2) {
grid-column: 2;
}
.box:nth-child(3n + 3) {
grid-column: 3;
}
This CSS will help ensure that the app's layout is centered, the font is legible, and the UI components have a clean, consistent style.
- Next, open
ai-recipe-generator/src/App.css
and update it with the following code to style the ingredient input form:
.app-container {
margin: 0 auto;
padding: 20px;
text-align: center;
}
.header-container {
padding-bottom: 2.5rem;
text-align: center;
}
.main-header {
font-size: 2.25rem;
font-weight: bold;
color: #1a202c;
}
.main-header .highlight {
color: #2563eb;
}
.description {
font-weight: 500;
font-size: 1.125rem;
max-width: 65ch;
color: #1a202c;
}
.form-container {
margin-bottom: 20px;
}
.search-container {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
}
.wide-input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
.search-button {
width: 100%;
max-width: 300px;
padding: 10px;
font-size: 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.search-button:hover {
background-color: #0056b3;
}
.result-container {
margin-top: 20px;
transition: height 0.3s ease-out;
overflow: hidden;
}
.loader-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.result {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
white-space: pre-wrap;
word-wrap: break-word;
color: black;
font-weight: bold;
text-align: left;
}
This will ensure that the ingredients form and result display are properly styled.
Step 3: Implement the UI
Now, let’s build the main React component for the app. We’ll integrate the AWS Amplify Authentication components for user sign-up, sign-in, and password recovery.
- Open
ai-recipe-generator/src/main.tsx
and update it with the following code. This will use the AmplifyAuthenticator
component to wrap your app and provide a complete authentication flow:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { Authenticator } from "@aws-amplify/ui-react";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Authenticator>
<App />
</Authenticator>
</React.StrictMode>
);
The Authenticator
component will handle user authentication, including sign-up, sign-in, and MFA (Multi-Factor Authentication).
- Next, open
ai-recipe-generator/src/App.tsx
and update it with the following code to implement the form for ingredient submission and the logic for querying the askBedrock function.
import { FormEvent, useState } from "react";
import { Loader, Placeholder } from "@aws-amplify/ui-react";
import "./App.css";
import { Amplify } from "aws-amplify";
import { Schema } from "../amplify/data/resource";
import { generateClient } from "aws-amplify/data";
import outputs from "../amplify_outputs.json";
import "@aws-amplify/ui-react/styles.css";
Amplify.configure(outputs);
const amplifyClient = generateClient<Schema>({
authMode: "userPool",
});
function App() {
const [result, setResult] = useState<string>("");
const [loading, setLoading] = useState(false);
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setLoading(true);
try {
const formData = new FormData(event.currentTarget);
const { data, errors } = await amplifyClient.queries.askBedrock({
ingredients: [formData.get("ingredients")?.toString() || ""],
});
if (!errors) {
setResult(data?.body || "No data returned");
} else {
console.log(errors);
}
} catch (e) {
alert(`An error occurred: ${e}`);
} finally {
setLoading(false);
}
};
return (
<div className="app-container">
<div className="header-container">
<h1 className="main-header">
Meet Your Personal
<br />
<span className="highlight">Recipe AI</span>
</h1>
<p className="description">
Simply type a few ingredients using the format ingredient1,
ingredient2, etc., and Recipe AI will generate an all-new recipe on
demand...
</p>
</div>
<form onSubmit={onSubmit} className="form-container">
<div className="search-container">
<input
type="text"
className="wide-input"
id="ingredients"
name="ingredients"
placeholder="Ingredient1, Ingredient2, Ingredient3,...etc"
/>
<button type="submit" className="search-button">
Generate
</button>
</div>
</form>
<div className="result-container">
{loading ? (
<div className="loader-container">
<p>Loading...</p>
<Loader size="large" />
<Placeholder size="large" />
<Placeholder size="large" />
<Placeholder size="large" />
</div>
) : (
result && <p className="result">{result}</p>
)}
</div>
</div>
);
}
export default App;
The app allows users to input a list of ingredients, submits the request to the backend via Amplify and Amazon Bedrock, and then displays the generated recipe.
Step 4: Run and Test the App
- Open a new terminal window and navigate to your project’s root directory (
ai-recipe-generator
), then run:
npm run dev
- Visit the local host URL to open the app in your browser and test it.
Step 5: Deploy the App
Once you’ve confirmed that the app is working as expected locally, it’s time to deploy it to AWS Amplify.
- In the terminal, commit your changes:
git add .
git commit -m 'connect to bedrock'
git push origin main
- Go to the AWS Amplify console, and your app should automatically build and deploy.
After deployment, you can access your live app at the provided Amplify URL.
Challenges Faced and Solutions
-
Integrating Bedrock API with Lambda: The response latency from Amazon Bedrock sometimes caused delays.
- Solution: Error handling and retry logic were implemented to handle API delays and ensure smoother user experiences.
-
Frontend and Amplify Configuration: Understanding the integration of Amplify DataStore with AppSync for real-time data updates was tricky.
- Solution: Amplify’s documentation and UI components helped speed up the integration process and simplified backend connectivity.
Conclusion
By combining AWS Amplify for the frontend, Amazon Bedrock for AI-powered recipe generation, and AWS Cognito for secure authentication, we've built a robust, serverless application that generates personalized recipes based on user input. This project demonstrates the potential of AWS services to create scalable, AI-driven applications for various domains like e-commerce, content creation, and beyond.
Explore my GitHub repository.
Asif Khan — Aspiring Cloud Architect | Weekly Cloud Learning Chronicler