Lambda: code without servers
Create a function in the console, test it, give it a public function URL and call it with curl, read its logs, and choose memory, timeout, versions and triggers deliberately.
By the end of this lesson you'll be able to
0 of 5 completeA function is still a production workload
Lambda runs your code when something happens (an HTTP request, a file landing in S3, a message in a queue, a schedule) and bills per request and per millisecond. There are no servers to patch or size, but there is still design work: you own the handler, its dependencies, permissions, timeout, retries, logs and alarms.
This lesson builds one small function in a real account, calls it four ways (CLI, console test, a public function URL, the logs), and deletes it.
Package, role and invocation
Lambda → Functions → Create function:
The execution role is what the code runs as. Lambda assumes it for every invocation, so the function never holds access keys. The default role can only write logs; add exactly the extra permissions the code needs.
The handler, index.mjs:
export const handler = async (event) => { const name = event.queryStringParameters?.name ?? event.name ?? 'world'; console.log(JSON.stringify({ msg: 'greeting', name })); return { statusCode: 200, headers: { 'content-type': 'application/json' }, body: JSON.stringify({ message: `Hello, ${name}!`, region: process.env.AWS_REGION }), }; };
Deploy it from the CLI and invoke it directly:
$ zip fn.zip index.mjs $ aws lambda update-function-code --function-name academy-hello --zip-file fileb://fn.zip $ aws lambda invoke --function-name academy-hello --cli-binary-format raw-in-base64-out \ --payload '{"name":"Rajeev"}' out.json && cat out.json {"statusCode":200,"headers":{"content-type":"application/json"},"body":"{\"message\":\"Hello, Rajeev!\",\"region\":\"ap-south-1\"}"}
aws lambda update-function-code --function-name academy-hello --zip-file fileb://fn.zipawsthe AWS command line
The Test tab does the same from the console:
Function URLs and API Gateway
A function URL gives the function its own HTTPS endpoint:
$ aws lambda create-function-url-config --function-name academy-hello --auth-type NONE $ aws lambda add-permission --function-name academy-hello --statement-id url-public \ --action lambda:InvokeFunctionUrl --principal '*' --function-url-auth-type NONE $ aws lambda add-permission --function-name academy-hello --statement-id url-invoke \ --action lambda:InvokeFunction --principal '*' --invoked-via-function-url $ curl -s "https://<url-id>.lambda-url.ap-south-1.on.aws/?name=learner" {"message":"Hello, learner!","region":"ap-south-1"}
aws lambda create-function-url-config --function-name academy-hello --auth-type NONEawsthe AWS command line
curl -s "https://<url-id>.lambda-url.ap-south-1.on.aws/?name=learner"curlmake an HTTP request-ssilent: no progress bar
Through a URL, Lambda turns the returned object into a real HTTP response: status 200, the header, and only the
body. A public URL needs both permission statements; with only the first, requests get 403 Forbidden.
| Function URL | API Gateway (HTTP API / REST API) | |
|---|---|---|
| Endpoint | one per function | many routes to many functions |
| Auth | IAM or none | IAM, JWT (Cognito, Auth0), Lambda authorizers, API keys |
| Extras | CORS | throttling, usage plans, custom domains, stages, request validation (REST) |
| Use for | a webhook, a quick internal endpoint | a real public API |
Limits, versions and triggers
| Setting | What to know |
|---|---|
| Memory (128 MB–10 GB) | CPU grows with memory; more memory often makes a function faster and cheaper |
| Timeout (up to 15 min) | bounds one invocation; set it just above the slowest normal run |
| Concurrency | how many run at once; reserved concurrency caps a function so it can't overwhelm a database |
| Cold start | the first request on a new environment loads the runtime and your code; keep packages small |
Every invocation logs to CloudWatch Logs under /aws/lambda/<function>, ending with a report line that shows the
bill:
REPORT RequestId: 288a1386-efea-40df-92ed-bbdbe736b6f2 Duration: 12.28 ms Billed Duration: 13 ms Memory Size: 128 MB Max Memory Used: 80 MB
Versions freeze the code and configuration ($LATEST is always the editable one). An alias such as
prod points at a version, so a rollback is moving the alias back. Layers share libraries between
functions, and container image functions let you ship up to 10 GB as an image from ECR (lesson 11).
Triggers turn events into invocations, each with its own retry behaviour:
| Trigger | How it calls | On failure |
|---|---|---|
| API Gateway / function URL | synchronous | the caller gets the error |
| S3 events | asynchronous | Lambda retries twice, then a dead-letter queue or destination |
| SQS | Lambda polls batches | messages return to the queue, then its DLQ (lesson 14) |
| EventBridge schedule | asynchronous | retries, then a DLQ or destination |
Retries mean the same event can arrive twice, so make handlers idempotent.
SQS and HTTP API are production-shaped triggers
An SQS event source mapping polls a queue and batches messages into Lambda events. The function must be idempotent because a message can be delivered again; configure a visibility timeout longer than the function timeout and use a dead-letter queue for messages that repeatedly fail. An API Gateway HTTP API is the better front door when you need routes, authorizers, throttling or access logs; a Function URL is the smaller alternative.
Enable X-Ray tracing when you need a request map across Lambda and downstream calls. It adds useful correlation, not business-level observability, so retain structured logs and metrics too.
$ aws lambda get-function-configuration --function-name "$FUNCTION" \ --query '{Runtime:Runtime,Memory:MemorySize,Timeout:Timeout,Tracing:TracingConfig.Mode}' --output table $ aws lambda list-event-source-mappings --function-name "$FUNCTION" \ --query 'EventSourceMappings[].{State:State,Source:EventSourceArn,Batch:BatchSize}' --output table $ aws apigatewayv2 get-apis --query 'Items[].{Name:Name,Protocol:ProtocolType,Endpoint:ApiEndpoint}' --output table
aws lambda get-function-configuration --function-name "$FUNCTION" \awsthe AWS command line
aws apigatewayv2 get-apis --query 'Items[].{Name:Name,Protocol:ProtocolType,Endpoint:ApiEndpoint}' --output table--querypick fields from the JSON response--outputoutput format
Create the queue, API and mapping only for the exercise, record the returned IDs, and delete each by ID before leaving the account. Never leave a public endpoint or an event source polling a queue after the lesson.
A public function, called and removed
Create a function from the console or CLI, invoke it directly, give it a public function URL, call that URL with curl, read its log, then delete the URL, the function, its log group and its role.
Hello, learner! from the URL, a REPORT line in the logs, and ResourceNotFoundException after deletionReveal one safe solution
$ # Console: Lambda → Create function → academy-hello, Node.js 24.x → Create (it also creates the role) $ zip fn.zip index.mjs # the handler from this lesson $ aws lambda update-function-code --function-name academy-hello --zip-file fileb://fn.zip $ aws lambda wait function-updated --function-name academy-hello $ URL=$(aws lambda create-function-url-config --function-name academy-hello --auth-type NONE --query FunctionUrl --output text) $ aws lambda add-permission --function-name academy-hello --statement-id url-public \ --action lambda:InvokeFunctionUrl --principal '*' --function-url-auth-type NONE $ aws lambda add-permission --function-name academy-hello --statement-id url-invoke \ --action lambda:InvokeFunction --principal '*' --invoked-via-function-url $ curl -s "${URL}?name=learner" {"message":"Hello, learner!","region":"ap-south-1"} $ ROLE=$(aws lambda get-function --function-name academy-hello --query Configuration.Role --output text | awk -F/ '{print $NF}') $ aws lambda delete-function-url-config --function-name academy-hello $ aws lambda delete-function --function-name academy-hello $ aws logs delete-log-group --log-group-name /aws/lambda/academy-hello $ for p in $(aws iam list-attached-role-policies --role-name $ROLE --query 'AttachedPolicies[].PolicyArn' --output text); do aws iam detach-role-policy --role-name $ROLE --policy-arn $p; aws iam delete-policy --policy-arn $p; done $ aws iam delete-role --role-name $ROLE
aws lambda update-function-code --function-name academy-hello --zip-file fileb://fn.zipawsthe AWS command line
curl -s "${URL}?name=learner"curlmake an HTTP request-ssilent: no progress bar
for p in $(aws iam list-attached-role-policies --role-name $ROLE --query 'AttachedPolicies[].PolicyArn' --output text); doforloop over a list$(aws iam list-attached-role-policies --role-name $ROLE --query 'AttachedPolicies[].PolicyArn' --output text);the output of another commanddostart of what the loop runs each time
The console-created role comes with its own small logging policy, which is why the loop deletes the policy too. A few invocations are far inside Lambda's free tier.
Try it yourself
0 of 5 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
What does a Lambda execution role provide?
Official reading for this lesson
Or get every quiz answer right and it completes itself.