A skeleton for building a full stack agentic app including
- Frontend with NextJs & AI SDK
- Backend with Python & FastAPI & Strands Agents
- Database for preserving agent session using S3
- Authorization with Cognito / OAuth2
- CDK (Cloud Formation) for managing and deploying all the resources.
For more details, please refer to my blog The Full Stack Agentic App Architecture I Use + Skeleton App Available
AWS Cognito User pool is used for both authorizing request made to AgentCore Runtime as well as API Gateway APIs.
S3 Bucket is used for both preserving individual sessions automatically by Strands Agent, using S3SessionManager, as well as custom session metadata such as name and etc. directly code-wise.
Bucket structure:
/<user_id>/
├── sessions.json # Custom Sessions metadata such as name.
└── session_<session_id>/
├── session.json # Session metadata
└── agents/
└── agent_<agent_id>/
├── agent.json # Agent metadata
└── messages/
├── message_<id1>.json
└── message_<id2>.jsonAgent is built with Strands Agents, with its sessions preserved on S3.
Agent invocation endpoint is servered with AWS Bedrock AgentCore Runtime with the following capabilities.
- Stream or non-streaming
- Multi-modal input, ie: text, images, and documents.
- Header
Authorization: Bearer ${cognito_access_token}
- Body
class InvocationRequest(BaseModel):
"""
Invocation Request input schema.
Attributes:
inputs: A list of input content block.
stream: whether to stream message or not.
session_id: id representing a chat session.
user_id: id of the user.
"""
inputs: List[InputContentBlock]
stream: bool
session_id: str
user_id: strAPI Gateway and Lambda is used for serving other agent-related API Endpoints including
GET /{user_id}: Get all existing sessions (chats) for a specific usersGET /{user_id}/{session_id}/messages: Get messages (histories) for a sessionPOST, PUT, DELETE /{user_id}/{session_id}: Create, update session name, delete a session
When getting past messages, I am removing all documents binary (or base64 encoded) from the response becauseI am not displaying the actual document contents in the frontend app, but only some basic metadata.
If allowing user to download past documents is needed, either return the base64 encoded contents within the response, or consider using a S3 Presigned URL.
- Header
Authorization: ${cognito_access_token}
Note: Bearer is not needed in this case.
- Query
For POST, PUT /{user_id}/{session_id}, query parameter name is needed for specifying session (chat) name.
Frontend is built with NextJs & AI SDK, containerized and deployed on AWS AppRunner, with the following capabilities.
- User Login/Logout with Cognito
- Chat & Stream responses (Text, images, documents)
- Retrieve previous session (chat) history
- Manage sessions
- Download the repository
- Set Up AWS CDK as described here
- Optionally set up cdk context.
"develop": {
"LOCAL_HOST": "http://localhost:3000", // local host for frontend app
"CALLBACK_PATH": "", // cognito call back paths
"LOGOUT_PATH": "", // cognito logout path
"BUCKET_NAME": "agent-app-session-database", // s3 bucket name
"MODEL_ID": "jp.anthropic.claude-haiku-4-5-20251001-v1:0" // model id for invocation.
}
- Start Docker daemon
- Run the following command which will create all the resources above as well as configuring necessary environment variables.
cd cdk
cdk deploy --all
# Or deploy with a specific context
# cdk deploy --all --context context=developS3 bucket and Cognito User pool is need so make sure to have those deployed first.
- Install Dependencies
uv venv
source .venv/bin/activate
uv sync- Configure Environment variables (default values provided)
MODEL_IDREGION_NAMES3_BUCKET_NAMEAGENT_ID
- Run Agent App (agent invocation)
uv run uvicorn app.agent_app:app --host 0.0.0.0 --port 8080- Run Management App (Managing sessions, histories, and etc.)
uv run uvicorn app.management_app:app --host 0.0.0.0 --port 8081- Build Image
docker buildx create --use
docker buildx build --platform linux/arm64 -t backend-agent:arm64 --load .- Run Agent App
By default, the image is built with CMD to start agent app.
docker run --platform linux/arm64 -p 8080:8080 \
-e AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
-e AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
-e AWS_SESSION_TOKEN="$AWS_SESSION_TOKEN" \
-e AWS_REGION="$AWS_REGION" \
backend-agent:arm64- Run Management App
To start the management app, we can use the same image and override CMD command.
docker run --platform linux/arm64 -p 8081:8081 \
-e AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
-e AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
-e AWS_SESSION_TOKEN="$AWS_SESSION_TOKEN" \
-e AWS_REGION="$AWS_REGION" \
backend-agent:arm64 uv run uvicorn app.management_app:app --host 0.0.0.0 --port 8081- Set up environment variables
- COGNITO_CLIENT_ID=""
- COGNITO_CALLBACK_PATH=""
- COGNITO_LOGOUT_PATH=""
- COGNITO_DOMAIN_NAME="agent-app"
- COGNITO_USER_POOL_ID="..."
- AWS_REGION="ap-northeast-1"
- AGENTCORE_RUNTIME_ARN="arn:aws:bedrock-agentcore:ap-northeast-1:...:runtime/..."
- MANAGEMENT_API_URL="https://....execute-api.ap-northeast-1.amazonaws.com/prod/"
- Start the app
npm run dev
