Launch Your First Serverless API: Hands-On with AWS Chalice on AWS Lambda



This content originally appeared on DEV Community and was authored by Shrijith Venkatramana

Hello, I’m Shrijith Venkatramana. I’m building LiveReview, a private AI code review tool that runs on your LLM key (OpenAI, Gemini, etc.) with affordable pricing — built for small teams. Do check it out and give it a try!

If you’re looking to spin up quick APIs without managing servers, AWS Chalice might be your next go-to tool. It’s a Python framework that lets you build and deploy serverless apps on AWS Lambda, and the best part? You can host experimental dynamic APIs for free under AWS’s free tier limits. In this guide, we’ll walk through getting started, from setup to deployment, with plenty of code examples you can copy-paste and run. We’ll keep things practical, focusing on what works for rapid prototyping.

By the end, you’ll have a running API, plus ideas on handling events like S3 uploads or scheduled tasks. All based on Chalice’s straightforward decorator-based approach.

Why Chalice Stands Out for Quick Serverless Builds

Chalice simplifies serverless development by handling the heavy lifting: it generates IAM policies automatically, deploys via a single command, and integrates seamlessly with AWS services like API Gateway, S3, SNS, and SQS.

Key advantages:

  • Decorator-driven API: Define routes, schedules, or event handlers with simple Python decorators.
  • No server management: Everything runs on Lambda, so scale is automatic.
  • Free for experiments: Leverage AWS Lambda’s free tier (1 million requests/month) for low-traffic APIs.
  • Python-focused: Supports Python 3.9 to 3.13, matching Lambda’s runtimes.

Compared to alternatives like Serverless Framework or SAM, Chalice feels lighter for Python devs—less YAML config, more code.

Feature Chalice Serverless Framework AWS SAM
Primary Language Python Multi-language Multi-language
Deployment Command chalice deploy sls deploy sam deploy
Auto IAM Policies Yes Partial No
Event Integrations Built-in decorators for S3, SQS, etc. Plugins needed YAML templates

If you’re new to serverless, Chalice’s minimal setup gets you deploying faster. Check the official GitHub for the latest updates: aws/chalice.

Setting Up Your Local Environment for Chalice

Before coding, get your machine ready. You’ll need Python (3.9+), a virtual environment, and AWS credentials.

First, verify Python:

python3 --version
# Output: Python 3.9.22 (or higher, up to 3.13)

Create and activate a virtual env:

python3 -m venv venv-chalice
source venv-chalice/bin/activate  # On macOS/Linux; use venv-chalice\Scripts\activate on Windows

Install Chalice:

pip install chalice

Test it:

chalice --help
# Output: Usage: chalice [OPTIONS] COMMAND [ARGS]...

For AWS access, create ~/.aws/config if you don’t have one:

mkdir ~/.aws
cat << EOF > ~/.aws/config
[default]
aws_access_key_id=YOUR_ACCESS_KEY
aws_secret_access_key=YOUR_SECRET_KEY
region=us-west-2  # Or your preferred region
EOF

Pro tip: Use AWS IAM for a least-privilege user. If you’re stuck on creds, refer to Boto3’s credential docs.

This setup takes under 5 minutes and ensures smooth deployments.

Creating Your Initial Chalice Project Structure

With tools ready, generate a project skeleton. Run:

chalice new-project my-first-api
cd my-first-api

This creates:

  • app.py: Your main application file.
  • requirements.txt: For dependencies (empty at start).
  • .chalice/: Config files—don’t edit manually yet.

Peek at the directory:

ls -la
# Output:
# drwxr-xr-x   .chalice
# -rw-r--r--   app.py
# -rw-r--r--   requirements.txt

app.py starts with a basic API:

from chalice import Chalice

app = Chalice(app_name='my-first-api')

@app.route('/')
def index():
    return {'hello': 'world'}

This defines a root route returning JSON. No extras needed yet—Chalice handles the rest.

If you add libraries like requests, update requirements.txt and Chalice bundles them on deploy.

Exploring app.py: Routes and Basic Functionality

The heart of Chalice is app.py, where you define your logic using decorators.

Core elements:

  • Chalice(app_name): Initializes your app.
  • @app.route('/'): Maps HTTP methods (GET by default) to functions.
  • Return values: JSON-serializable dicts or strings.

Extend the basic example with a dynamic route:

from chalice import Chalice

app = Chalice(app_name='my-first-api')

@app.route('/')
def index():
    return {'hello': 'world'}

@app.route('/greet/{name}')
def greet(name):
    return {'message': f'Hello, {name}!'}

Run locally for testing:

chalice local
# Output: Serving on http://127.0.0.1:8000

Curl it:

curl http://127.0.0.1:8000/greet/Dev
# Output: {"message": "Hello, Dev!"}

Chalice supports HTTP methods like POST via methods=['POST'] in the decorator. For full route options, see the Chalice quickstart docs.

This setup lets you prototype APIs quickly without boilerplate.

Deploying Your Chalice App to AWS Lambda

Deployment is Chalice’s killer feature—one command handles packaging, IAM roles, Lambda functions, and API Gateway.

From your project dir:

chalice deploy
# Output:
# Creating deployment package.
# Creating IAM role: my-first-api-dev
# Creating lambda function: my-first-api-dev
# Creating Rest API
# Resources deployed:
#   - Lambda ARN: arn:aws:lambda:us-west-2:123456789012:function:my-first-api-dev
#   - Rest API URL: https://abcd1234.execute-api.us-west-2.amazonaws.com/api/

Test the live endpoint:

curl https://abcd1234.execute-api.us-west-2.amazonaws.com/api/
# Output: {"hello": "world"}

What happens under the hood:

  • Chalice zips your code and deps.
  • Creates a Lambda function.
  • Sets up API Gateway routes.
  • Generates minimal IAM policies.

Redeploy changes with the same command—it updates efficiently.

If issues arise (e.g., permissions), check logs via chalice logs. For region-specific tips, browse AWS Lambda docs.

Your API is now live, free for light use.

Adding Dynamic Routes and Handling Requests

Build on basics by adding params, queries, and responses.

Enhance with a POST route for data processing:

from chalice import Chalice, Response

app = Chalice(app_name='my-first-api')

@app.route('/echo', methods=['POST'])
def echo():
    request = app.current_request
    body = request.json_body
    if body and 'text' in body:
        return Response(body={'echoed': body['text']},
                        status_code=200,
                        headers={'Content-Type': 'application/json'})
    return Response(body={'error': 'Missing text'},
                    status_code=400)

Local test:

chalice local
curl -X POST http://127.0.0.1:8000/echo -H "Content-Type: application/json" -d '{"text": "Test message"}'
# Output: {"echoed": "Test message"}

Use app.current_request for headers, queries, or bodies.

For validation, add libraries like pydantic to requirements.txt:

pip install pydantic
echo "pydantic" >> requirements.txt

This keeps APIs robust for experiments.

Triggering Functions with AWS Events

Chalice isn’t just APIs—handle events like S3 uploads or schedules.

For scheduled tasks:

from chalice import Chalice, Rate

app = Chalice(app_name='my-first-api')

@app.schedule(Rate(5, unit=Rate.MINUTES))
def every_five_minutes(event):
    print("Running task!")
    return {"status": "done"}

Deploy, and it runs every 5 minutes via CloudWatch Events.

For S3 events:

from chalice import Chalice

app = Chalice(app_name='my-first-api')

@app.on_s3_event(bucket='my-experimental-bucket')
def handle_s3_upload(event):
    print(f"File uploaded: {event.key}")
    # Process the file here

Create the bucket first in AWS console. Deploy to wire it up.

Similar for SQS:

from chalice import Chalice

app = Chalice(app_name='my-first-api')

@app.on_sqs_message(queue='my-queue')
def process_queue(event):
    for record in event:
        print(f"Message: {record.body}")

These decorators auto-configure triggers. For more event types, explore Chalice’s event docs.

This expands Chalice beyond APIs into full event-driven apps.

Scaling Experiments: Updates, Deletion, and Tips

Once deployed, iterate fast: edit code, chalice deploy again—it only updates changes.

View logs:

chalice logs
# Output: Tail of Lambda logs...

For cleanup:

chalice delete
# Output:
# Deleting Rest API...
# Deleting Lambda function...
# Deleting IAM role...

Best practices for experiments:

  • Version control: Git your project early.
  • Environments: Use --stage for dev/prod (e.g., chalice deploy --stage prod).
  • Costs: Monitor AWS console—stay in free tier by limiting invocations.
  • Security: Avoid hardcoding secrets; use environment vars via .chalice/config.json.
Command Purpose When to Use
chalice deploy Upload and configure resources After code changes
chalice local Run API locally Quick testing
chalice delete Remove all resources End of experiment
chalice logs Fetch Lambda logs Debugging issues

If scaling up, consider Chalice’s blueprints for reusable code.

Next Steps: Building Production-Ready Apps with Chalice

As you experiment more, dive into advanced features like custom domains via API Gateway or integrating with DynamoDB. Start small—prototype an API for a side project, then add events for automation.

Resources to level up:

  • Official tutorials: Chalice Tutorials
  • Community: Join discussions on GitHub issues or Gitter.

Chalice makes serverless accessible, so tweak these examples and deploy your ideas today. If you hit snags, AWS free tier support is there, and the docs have got your back. Happy coding!


This content originally appeared on DEV Community and was authored by Shrijith Venkatramana