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