Skip to main content

Hands-On: Deploy Your First Lambda Function

What You Will Build

A Node.js function deployed two ways — first through the console to see the moving parts, then via the AWS CLI the way you'd script it in CI — exposed to the internet with a function URL and debugged through CloudWatch logs.

Step 1 — Create the Function in the Console

  1. Go to Lambda → Create function → Author from scratch.
  2. Function name: hello-api.
  3. Runtime: Node.js 22.x. Architecture: arm64 (cheaper, no downside for JS).
  4. Under Permissions, keep Create a new role with basic Lambda permissions — this role lets the function write logs and nothing else.
  5. Click Create function.

Step 2 — Write the Code

The console opens an inline editor on index.mjs. Replace its contents:

export const handler = async (event) => {
const name = event.queryStringParameters?.name ?? 'world';

return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: `Hello, ${name}`,
requestId: event.requestContext?.requestId,
}),
};
};

Click Deploy (the code isn't live until you do — a common gotcha).

Step 3 — Test from the Console

Open the Test tab, create an event named basic with this payload, and click Test:

{
"queryStringParameters": { "name": "console" }
}

You should see a 200 response, plus the duration and billed duration — my cold start was around 120 ms, warm invocations under 2 ms.

Step 4 — Add a Function URL

A function URL gives you a public HTTPS endpoint with zero API Gateway setup:

  1. Configuration → Function URL → Create function URL.
  2. Auth type: NONE (public — fine for this exercise, use AWS_IAM for anything real).
  3. Save, and copy the URL.
curl "https://YOUR_URL_ID.lambda-url.us-east-1.on.aws/?name=curl"

You get the JSON response back. That's a live API with no servers, no port config, no process manager.

Step 5 — Redeploy the Same Function via the AWS CLI

Console clicking doesn't scale. Here's the same lifecycle scripted. First, package the code:

mkdir hello-api && cd hello-api
# create index.mjs with the handler code from Step 2
zip function.zip index.mjs

Update the existing function's code:

aws lambda update-function-code \
--function-name hello-api \
--zip-file fileb://function.zip

Or create a brand-new function from scratch. This needs an execution role — reuse the one the console made (find its ARN under Configuration → Permissions):

aws lambda create-function \
--function-name hello-api-cli \
--runtime nodejs22.x \
--architectures arm64 \
--handler index.handler \
--zip-file fileb://function.zip \
--role arn:aws:iam::123456789012:role/hello-api-role-abc123

Invoke it directly from the CLI without any URL:

aws lambda invoke \
--function-name hello-api-cli \
--payload '{"queryStringParameters":{"name":"cli"}}' \
--cli-binary-format raw-in-base64-out \
response.json

cat response.json

Step 6 — Read the Logs

Every console.log in your handler lands in CloudWatch under the log group /aws/lambda/hello-api. Three ways to read them:

Console: function page → Monitor → View CloudWatch logs → open the latest log stream.

CLI, tailing live (my default while debugging):

aws logs tail /aws/lambda/hello-api --follow

CLI, searching past logs:

aws logs filter-log-events \
--log-group-name /aws/lambda/hello-api \
--filter-pattern "ERROR" \
--start-time $(($(date +%s) - 3600))000

Each invocation logs START, END, and a REPORT line. The REPORT line is the one to watch — it shows duration, billed duration, memory configured, and max memory used. If max memory used sits far below configured, you can lower the memory setting and pay less.

Step 7 — Break It on Purpose

Throw an error to see the failure path:

export const handler = async (event) => {
throw new Error('something exploded');
};

Deploy, hit the function URL, and you get a 502 from the URL layer. The real stack trace is in CloudWatch — this is the habit to build: the HTTP response tells you almost nothing, the logs tell you everything.

Cleanup

aws lambda delete-function --function-name hello-api
aws lambda delete-function --function-name hello-api-cli
aws logs delete-log-group --log-group-name /aws/lambda/hello-api

Lambda itself costs nothing while idle, but the log groups persist until deleted.

Common Problems

SymptomLikely Cause
Changes not reflectedForgot to click Deploy in the console editor
Runtime.HandlerNotFoundHandler setting doesn't match file.exportName (here: index.handler)
CLI invoke fails on payloadMissing --cli-binary-format raw-in-base64-out on AWS CLI v2
Function URL returns 403Auth type is AWS_IAM but the request is unsigned