LeafWise is a plant care application built with Next.js and TypeScript. It integrates AWS services via the modular AWS SDK v3 (Cognito, AppSync, S3) and uses Genkit with Google's Gemini models for plant health analysis and care plan generation. Backend infrastructure is provisioned with Amplify Gen 2 (ampx), but no Amplify browser SDKs are used.
- User accounts powered by AWS Cognito (register, log in, profile updates).
- Plant management with images stored on Amazon S3.
- AI plant diagnosis and care plan generation via Genkit flows running on AWS Lambda.
- Detailed plant pages showing growth photos, health trends and editable care tasks.
- Calendar view of upcoming care tasks.
- Data Import/Export: Comprehensive backup and restoration of all user data, including plant details, care tasks, and embedded image data.
- Internationalization with English and Vietnamese translations.
- PWA capabilities including offline support and installability.
- Next.js 15 (App Router) + TypeScript
- AWS Amplify Gen 2 (backend infrastructure:
auth,data,storage) - AWS SDK v3 (Cognito Identity Provider, S3, S3 presigner)
- Genkit with Google Gemini models (runs on AWS Lambda with Function URL)
- Apollo Client for AppSync (GraphQL with auth header injection)
- ShadCN UI and Tailwind CSS
- React Context API and React Hook Form
- Framer Motion, date-fns
- Install dependencies:
npm install
- Create a
.env.local(Next.js dev) and.env(ampx) with required variables:SeeGOOGLE_API_KEY=YOUR_GOOGLE_AI_API_KEY # AI Flows Lambda Function URL (use http://localhost:4100 for local dev) NEXT_PUBLIC_AI_API_URL=http://localhost:4100 # AWS Cognito NEXT_PUBLIC_COGNITO_REGION=us-east-1 NEXT_PUBLIC_COGNITO_USER_POOL_ID=us-east-1_xxxxx NEXT_PUBLIC_COGNITO_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxx NEXT_PUBLIC_COGNITO_IDENTITY_POOL_ID=us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # AWS AppSync NEXT_PUBLIC_APPSYNC_ENDPOINT=https://xxx.appsync-api.us-east-1.amazonaws.com/graphql # Amazon S3 NEXT_PUBLIC_S3_BUCKET_NAME=leafwise NEXT_PUBLIC_S3_REGION=us-east-1
.env.examplefor reference. All variables are required. - Start the AI dev server (in a separate terminal):
npm run ai:dev
- Start the Next.js dev server:
npm run dev
This repo uses Amplify Gen 2 with the ampx CLI. No global Amplify CLI install is required — use npx ampx for backend-only tasks. Frontend uses AWS SDK v3 directly; no Amplify browser SDKs or Hosting.
To work with the AWS Amplify backend (e.g., modify data models, authentication rules, or storage configurations), set up your AWS environment:
-
Configure AWS Credentials: Ensure you have AWS credentials configured on your machine. If using AWS SSO, run:
aws configure sso aws sso login
Alternatively, you can use the provided npm script:
npm run aws:login # This will prompt you to log in via AWS SSO. -
Run the Amplify Sandbox (Gen 2): To initialize your cloud sandbox backend environment, run:
npm run aws:sandbox
This command deploys your backend infrastructure (Cognito, AppSync, S3) to AWS. Configuration is loaded from environment variables (see step 2 above).
-
(Optional) Run the Genkit dev UI for flow debugging in a separate terminal:
npm run genkit:devFor the AI HTTP server (same API as Lambda), use instead:
npm run ai:dev- Set environment variables in Amplify Console (not from
.env.local):- All required build-time vars:
GOOGLE_API_KEY,NEXT_PUBLIC_AI_API_URL,NEXT_PUBLIC_COGNITO_REGION,NEXT_PUBLIC_COGNITO_USER_POOL_ID,NEXT_PUBLIC_COGNITO_CLIENT_ID,NEXT_PUBLIC_COGNITO_IDENTITY_POOL_ID,NEXT_PUBLIC_APPSYNC_ENDPOINT,NEXT_PUBLIC_S3_BUCKET_NAME,NEXT_PUBLIC_S3_REGION GOOGLE_API_KEYmust also be added in Manage secrets (for Lambda runtime access)- Deployment vars:
S3_BUCKET_NAME(target static site bucket),CF_DIST_ID(CloudFront distribution ID)
- All required build-time vars:
- The Amplify build runs:
npx ampx pipeline-deploy(backend)- Next.js static export
aws s3 syncto your bucket andcloudfront create-invalidation
AWS Configuration: The application uses environment variables for all AWS service endpoints and credentials:
src/lib/awsConfig.tscentralizes configuration loading and validation- No
amplify_outputs.jsondependency for frontend code - Backend infrastructure managed by Amplify Gen 2 (
amplify/folder) driven vianpx ampx
Authentication: Direct AWS SDK v3 + Cognito (no Amplify Auth wrapper):
src/contexts/AuthContext.tsxhandles login, register, confirm signup, token refresh, and sets acognito_id_tokencookie for server GraphQL- Tokens stored in localStorage with automatic refresh 5 minutes before expiry
- Client GraphQL uses Apollo auth link; server GraphQL reads ID token from cookie
Route Protection:
src/middleware.tsenforces access rules using thecognito_id_tokencookiesrc/lib/auth/routes.tsdefines protected/public routes and redirect logic
Data: Apollo Client for AppSync GraphQL queries (no Amplify Data wrapper):
src/lib/apolloClient.tsconfigures Apollo Client with auth header injectionsrc/lib/serverClient.tsexportscreateServerApolloClient()for server components and route handlers (create per-request clients)src/lib/graphql/operations.tsdefines all GraphQL queries and mutations- Components use Apollo's
useQueryanduseMutationhooks for data operations
Server usage example:
// In a server component or route handler
import { createServerApolloClient } from '@/lib/serverClient';
export async function getData() {
const client = await createServerApolloClient();
const { data } = await client.query({ /* ... */ });
return data;
}Notes:
- AppSync (Cognito User Pools) expects the raw JWT (no Bearer) in
Authorization. cognito_id_tokencookie (set by the client) is read server-side for auth.
Why AWS SDK v3 (Browser)
- Modular and tree-shakeable bundles
- Direct control over auth headers and credentials
- Works cleanly with S3 + CloudFront static hosting (no Amplify Hosting dependency)
Storage: AWS SDK v3 S3 (no Amplify Storage wrapper):
src/lib/s3Utils.tsprovidesuploadFile(),deleteFile(), anddeleteMultipleFiles()utilitiessrc/hooks/useS3Image.tsgenerates short-lived signed URLs for secure image access- Uploads/deletes use Cognito Identity credentials acquired at runtime; browser-safe SigV4 signing
After copying these files, run:
npm install
npm run typecheck
npm run buildThis ensures all types are correct and the code compiles properly.
amplify/– Amplify backend definitions for auth, data and storage.src/app/– Next.js route handlers and pages.src/components/– Reusable React components.src/ai/– Genkit flows for diagnosis and care plan logic.src/contexts/– React context providers (auth, language, plant data, etc.).src/hooks/– Custom hooks including utilities for S3 images and PWA state.src/lib/– Shared utilities and service clients (awsConfig, apolloClient, serverClient).public/– Static assets and service worker.
- Build static export (configured via
next.config.tsoutput: 'export'):npm run build # output in ./out - Upload to S3 and invalidate CloudFront (manual):
aws s3 sync out s3://<YOUR_S3_BUCKET> --delete aws cloudfront create-invalidation --distribution-id <YOUR_CF_DIST_ID> --paths "/*"
- CI/CD (optional):
amplify.ymlruns:npx ampx pipeline-deployfor backend infranpx ampx generate outputs(if needed for ops)aws s3 syncandcloudfront create-invalidationfor frontend
Environment variables for CI/CD (set in your build environment, not .env.local):
S3_BUCKET_NAME: Target S3 bucket for the static siteCF_DIST_ID: CloudFront distribution ID for invalidation
CloudFront + S3 prerequisites (one-time, via AWS Console):
- S3 bucket: enable versioning, block all public access
- CloudFront distribution: set S3 bucket as origin with OAI; default root object
index.html - SPA routing: custom error response 404 →
/index.htmlwith response code 200 - Optional: attach custom domain + ACM certificate; add DNS CNAME to CloudFront domain
Note: Remove any Amplify Hosting rewrites; SPA routing is handled by CloudFront’s custom error response.
- Delete the current Amplify sandbox deployment:
npx ampx sandbox delete
- VS Code task:
delete-amplify-sandbox(Terminal → Run Task) to run the same command.

