/f/151162/1080x1080/25feb05173/automation-star-79.png)
Hello developers, wanderlusters and people who really need a vacation. Ever stared at your screen thinking “Where should I go next?” Same. So I built an API that picks a travel destination based on your mood and budget, no thinking required. It’s serverless (thanks, AWS SAM), written in TypeScript, and runs like a dream on Lambda.
Feel romantic? Try Slovenia. Offbeat? Uzbekistan. Broke but beachy? Albania’s calling. This API gets your vibe and sends you packing.
AWS Lambda lets you run backend code without touching servers, it runs when needed and scales automatically. AWS SAM (Serverless Application Model) makes setting all that up ridiculously easy. It handles the messy stuff like creating IAM roles, permissions, and API Gateway connections for you. You define everything in one YAML file, and with a single command, SAM builds and deploys your app. If you're new to serverless or just want to move fast without fighting AWS configs, SAM has your back.
Before we build our mood-matching travel genie, let’s get your setup ready. Nothing too complicated, just the essentials (and a few nice-to-haves).
Install from: nodejs
Check it's working:
node -v
npm -v
We’re using AWS SSO (aka IAM Identity Center) to log in, no messy access keys, no drama. Install AWS CLI
Set it up with SSO:
aws configure sso
You’ll be prompted to: - Enter your SSO start URL (from your AWS org) - Choose the AWS account & role - Pick a default region (e.g eu-west-1)
Log in when needed:
aws sso login
Easy, secure, and way less painful than juggling keys.
SAM uses Docker to simulate the Lambda runtime on your machine. Install docker desktop
Verify it’s running:
docker --version
Make sure the IAM role you log into via SSO can: - Create and update Lambda functions - Create IAM roles - Deploy CloudFormation stacks - Access S3 (SAM uses it to upload deployment files)
If you’re using SSO in a team setup, your admin probably has you covered, but it’s always good to double-check.
VS Code is your bestie here. Bonus points if you install the AWS Toolkit extension, makes it super easy to browse resources, test Lambdas, and deploy.
Time to spin up our project using SAM’s built-in starter templates. I like starting with the TypeScript Hello World, it gives us a clean slate with just enough boilerplate to not feel lost.
sam init
Then go with these options: - Template source: 1 - AWS Quick Start Templates - Template type: 1 - Hello World Example - Use most popular runtime? No (we want to use Typescript) - Runtime: nodejs22.x (as of now, the latest supported) - Package type: Image - Starter template: Hello World Example TypeScript - Would you like to enable X-Ray/monitoring/testing? No to all (we’re keeping it simple and keeping our AWS bill low) - Project name: Wanderlamb
This will generate the base project folder with a sample Lambda function, Docker setup, and TypeScript support baked in, perfect for what we’re building.
This command compiles your TypeScript code and prepares your project for deployment or local testing.
sam build
It takes your .ts files and converts them into .js, bundles your dependencies, and outputs everything into the .aws-sam/build/ folder so SAM knows what to deploy or run locally.
Heads Up! Anytime you change: - Your Lambda function code - The template.yaml file - Add a new function - Update environment variables or API paths
You must run sam build again to see the changes locally or deploy the latest version. Don’t skip it, or you’ll be wondering why your function still acts like it's 3 commits behind
Want to make sure your template.yaml is actually valid before deploying? Run:
sam validate
This checks your syntax and resources so you don’t hit errors later on.
!Important: If you're running your Lambda functions locally, you’ll need to have Docker Desktop running, SAM uses it to simulate the Lambda runtime.
Want to test your function with a sample event? Use:
sam local invoke HelloFunction --event events/event.json
Replace HelloFunction with your actual function name (e.g. WanderLamb) and customize event.json with your test payload.
To spin up a local API Gateway (great for using Bruno), Run:
sam local start-api --skip-pull-image -p 4000
This serves your API on http://localhost:4000/ and skips downloading the runtime Docker image again (which speeds things up).
Before deploying, you need an S3 bucket for SAM to upload your Lambda package.
This command walks you through setting everything up for your first deploy:
sam deploy --guided
It will ask for: - Stack name (your app name) - Region (e.g. eu-west-1) - S3 bucket (use the one you created) - Permissions confirmation (say "Y" to allow SAM to create roles for you)
After your first deploy, SAM saves your answers to samconfig.toml so future deploys are one-liners:
sam deploy
To avoid errors, make sure this section exists in your samconfig.toml:
resolve_s3 = false
s3_bucket = "wanderlamb-bucket"
This tells SAM: “Hey, I already have an S3 bucket, no need to auto-create one for me.”
Okay, the “Hello World” thing was cute, but we’re building WanderLamb, a vibe-matching travel API that actually does something.
Resources:
WanderLambFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: wanderLamb/
Events:
WanderLamb:
Type: Api
Properties:
Path: /travel
Method: get
So now when you hit: /travel?maxBudget=40&mood=wild-life …it serves actual recommendations.
Pulls your query params like mood=wild-life and maxBudget=40
Filters the destinations list (yep, we’ve got one)
If there's a match, it picks one at random
If not, it sends a polite “nothing found”
Sends a clean JSON response back to you Here’s the the logic:
export const lambdaHandler = async (event: APIGatewayEvent): Promise<APIGatewayProxyResult> => {
try {
const query: RequestQuery = parseQueryParams(event.pathParameters || {});
const filtered = filterByMood(filterByBudget(destinations, query.maxBudget), query.mood);
if (filtered.length === 0) {
return buildErrorResponse(404, 'No destinations match your filters.');
}
const result = getRandomItem(filtered);
return buildDestinationResponse(result);
} catch (error) {
console.error('Error processing request:', error);
return buildErrorResponse(500, 'Internal server error.');
}
};
More code lives on our github space, if you wanna see all the goodies.
You built it, tested it, and deployed it, now it’s time to see WanderLamb in action. Fire up your local API, throw in a mood and a budget, and let the cloud do its thing.
Tudum, your next destination? Chile. Dry desert air, starry skies in the Atacama, and a warm plate of pastel de choclo waiting for you.
No logins. No spreadsheets. Just pure travel vibes, powered by TypeScript and a bit of serverless magic. Happy wandering!