Spring is the season of nature’s greenery, blooming flowers, and buzzing insects. There are millions and trillions of species of flora and fauna. Often, we come across something mesmerizing but don’t know what it is!
To solve this mystery, Spring Pokédex comes to the rescue. This blog post dives into how the Spring Pokédex works and the architecture behind it.
UX Workflow – A Magical Experience 🌿

Ashley, a budding AWS engineer, went to the park on a beautiful spring day. While strolling around, she noticed a reddish-pink flower with a unique, shooting fragrance. She had never seen this flower and wondered what is the name of this species. Suddenly, she recalled that her friend Brock had once mentioned that an app named Spring Pokédex is handy in these situations.
She quickly opened the application, created an account, and logged in without any issues. Thanks to AWS Cognito for handling user management.
After that, she clicked on the “Scan Now” button and took a photo of the plant. The image started uploading and a notification confirmed the successful upload. A short while later, a notification popped up: “Creature Identified!”. Ashley screamed with joy! As she tapped the notification, the screen displayed 🌸Frangipani (Plumeria).
Behind the scenes, the image was securely uploaded using S3-signed URLs, and a real-time notification system (powered by Momento Topics) awaited the results.
She clicked on the “Share” button and a unique shareable link was generated (powered by Momento Cache) which she quickly sent to Brock.
Later that day, after reaching home, Ashley thought “What if other users could access the images she uploaded? 🤔”. Curious, she opened her laptop and tried to access the image she uploaded but this time as another user. To her surprise, it did not work, access was denied!
Why? Because access to files has been protected using CloudFront signed cookies.
Ashley was seriously impressed. Her curiosity sparked, and she wanted to dive deeper into the architecture behind the application. She wondered if any documentation was available online — and luckily, there was.
Spring Pokédex Architecture – High Level Overview 🔮
Let’s see what really powers the magic of Spring Pokédex🪄

Bit Scary, isn’t it? Well, let us break it down into small workflows.
1. CloudFront CDN And Its Behaviours
The application is delivered through AWS CloudFront distribution. And it has 3 behaviors pointing to 3 different origins.
- The first behaviour matches “/api*” in the URL and points to the AWS API gateway (protected by Cognito authorizer).
- The second behaviour looks for the path pattern “/images” and points to the “images” folder of the application bucket. This behaviour restricts view access using signedCookies and signedURLs.
- If none of the above behaviour match, the default behaviour serves the React application from the “dist” folder of the S3 bucket (using origin access identity OAI).
2. What Happens When App Loads?
If the user is not authenticated, redirection happens to Cognito managed login. After successful authentication, the user is redirected to the callback URL of the application with authorization_code that is used to request ACCESS_TOKEN (as part of the oAuth2.0 protocol).
After that, a request to “/api/getSignedCookies” is made to get CloudFront signed cookies for “/images/cognitoUserId/*” folder of S3, ie. users can access images uploaded by them only.
Also, a request is made to “/api/getDisposableToken” endpoint to get the Momento disposable token. Lambda function with valid Momento credentials sits behind the API and acts as a disposable token vending machine.
Momento token contains view-only scopes for Momento Topic named “pubsub-cache:cogintoUserId” and cache named “shared-cache”. Frontend uses the Momento disposable token to subscribe to the topic.
3. Discover Nature – One Scan At A Time!
The user clicks on the “Scan Now” button and selects (captures using the camera) an image to upload. Frontend makes a request to “/api/getSignedURL” endpoint to get signedURL and upload the image. Once the image is uploaded, frontend reflects the status as “Processing” and waits for a status update message on the Momento topic.
The image is uploaded to AWS S3 as “/images/cognito-user-id/image.png” and triggers the SpringScanner AWS Lambda function. The Lambda function generates a CloudFront signed URL for the image and calls OpenAI API using it.
On success, lambda stores data in the DynamoDB table and also publishes a success message to the Momento topic. On failure or crash of lambda function, the event goes to SQS queue (Dead-Letter Queue) and triggers DLQ handler lambda function that publishes failure message to the topic.
Frontend listens for these messages and shows details to the user in realtime. Mometo topic helps to achieve this without having any websockets connection.

4. Check History Or Share Your Discovery
At the time of the scan, data is stored in a DynamoDB table where userId is the partition key and S3ObjectKey is the sort key. History of scans for a user can be fetched by querying dynamo table using userId (/scanHistory endpoint).
When the user decides to share the scan with someone, api call to “/shareScan” endpoint is made. Using the userId from lambda event and scanId received as part of API payload, a DynamoDB call retrieves the scan details.
Since image access is allowed only for the original uploader, the imageUrl
can’t be shared directly. To solve this, a CloudFront signed URL is generated with a validity of 24 hours.
This signed URL and the scan details are stored in a Momento cache with a unique identifier. That identifier becomes part of the shareable URL. Both the cache entry and the signed URL have a TTL of 24 hours, so the shared scan remains accessible to others for one day only.
5. Rate Limiting The Scans
Momento cache is also used to limit how many scan operations a user can perform in a single day.
For each user, a cache key like “userid-date-today” is created, and its value is incremented with every image upload (this logic is integrated into the getSignedURL
Lambda function).
Once the max threshold is reached, the API returns a 429 Too Many Requests, and the frontend blocks further uploads.
6. Securing The Secrets
The Lambda functions use sensitive credentials like the Momento API key, OpenAI API key, and the CloudFront private key. These secrets should never be stored directly in code.
One option is to store them in Lambda environment variables and encrypt them using a KMS customer-managed key for additional security. But a more secure and flexible approach is to store secrets in AWS SSM Parameter Store or AWS Secrets Manager.
To simplify secret access in Lambda, middy middleware is used. Middy helps to inject secrets from SSM or Secrets Manager into function’s context.
🐼 Final Thoughts
Ashley closed her laptop with a smile. What started as a simple curiosity in the park turned into a deep dive into modern serverless event-driven architecture.
Thanks for reading, and I hope you enjoyed Spring Pokédex project 🌱