What is StelvioPermalink
Stelvio is a framework that helps Python developers build AWS applications without leaving Python.
It aims to dramatically simplify creation and management of AWS resources while ensuring best practices so developers can focus on their application instead of infrastructure.
No YAML, JSON, or other configuration languages — just pure Python code.
Currently Stelvio supports:
- Lambda functions & Layers
- API Gateway (v1)
- DynamoDB tables
- Linking - Automated IAM
Quick OverviewPermalink
Here's a complete Stelvio program that creates a todo-list API, including a DynamoDB table, API Gateway endpoints, and Lambda function:
from stelvio.app import StelvioApp
from stelvio.config import StelvioAppConfig, AwsConfig
from stelvio.aws.api_gateway import Api
from stelvio.aws.dynamo_db import DynamoTable, AttributeType
app = StelvioApp(name="stelvio-app")
@app.config
def configuration(env: str) -> StelvioAppConfig:
return StelvioAppConfig(
environments=['dev'],
aws=AwsConfig(
region='us-east-1',
profile='your-profile',
)
)
@app.run
def run() -> None:
table = DynamoTable(
name="todos",
fields={
"username": AttributeType.STRING,
"created": AttributeType.STRING,
},
partition_key="username",
sort_key="created",
)
api = Api("todos-api")
api.route("POST", "/todos", handler="functions/todos.post", links=[table])
api.route("GET", "/todos/{username}", handler="functions/todos.get")
Current FeaturesPermalink
Current release v0.3.0 includes:
- Lambda Functions with support for dependencies
- Lambda Layers
- Dynamo DB tables
- API gateway with endpoints using lambda functions - Stelvio will create resources, methods and integrations automatically as well as stage and deployment.
- Linking/Automated permissions - Stelvio automatically creates IAM roles. You can link components and Stelvio will automatically create IAM policies.
- CLI to handle deployment and related tasks
Quick start and example projectPermalink
Stelvio uses Pulumi under the hood to create your infrastructure. But this is completely hidden from us. We'll just use Stelvio's CLI.
Need more details? Check out the full Quickstart guide.
PrerequisitesPermalink
You'll need:
- Python 3.12+
- AWS account with deployment permissions
Let's build something!
Setting up a projectPermalink
1. AWS CredentialsPermalink
First, make sure you have an AWS account with programmatic access and rights to deploy infrastructure.
You can configure credentials in several ways:
Option 1: AWS CLI profiles
aws configure --profile YOUR_PROFILE_NAME
Then either specify the profile name during stlv init
or use:
export AWS_PROFILE=YOUR_PROFILE_NAME
Option 2: Environment variables
export AWS_ACCESS_KEY_ID="<YOUR_ACCESS_KEY_ID>"
export AWS_SECRET_ACCESS_KEY="<YOUR_SECRET_ACCESS_KEY>"
2. Create ProjectPermalink
If you can use uv
but you can use anything.
Using uv
# Create a new project
uv init stelvio-app && cd stelvio-app
# Install Stelvio
uv add stelvio
# Initialize Stelvio project
uv run stlv init
Using poetry
# Create a new project
poetry new stelvio-app && cd stelvio-app
# Install Stelvio
poetry add stelvio
# Initialize Stelvio project
poetry run stlv init
Using pip
# Create a new project
mkdir stelvio-app && cd stelvio-app
python -m venv .venv && source .venv/bin/activate
# Install Stelvio
pip install stelvio
# Initialize Stelvio project
stlv init
The stlv init
command will:
- Ask for your AWS profile name (or press Enter to use default credentials)
- Ask for your AWS region
- Create
stlv_app.py
with your project configuration
Building a Todo API using StelvioPermalink
Project structurePermalink
Your project will look like this:
stelvio-app/
├── stlv_app.py # Infrastructure code
└── functions/ # Lambda functions
└── todos.py # Our function code
Define our infrastructurePermalink
Let's build our API step by step. First, open stlv_app.py
. You'll see this starter code:
from stelvio.app import StelvioApp
from stelvio.config import StelvioAppConfig, AwsConfig
app = StelvioApp("stelvio-app")
@app.config
def configuration(env: str) -> StelvioAppConfig:
return StelvioAppConfig(
aws=AwsConfig(
region="us-east-1",
profile="your-profile", # or None if using env vars
),
)
@app.run
def run() -> None:
# Create your infra here
pass
Now let's add our resources. We'll create a simple API to create and list todos.
We need to put our infrastructure definitions inside the @app.run
function.
1. Create the DynamoDB table for storing todos:
First, let's add the imports we need at the top of the file:
from stelvio.app import StelvioApp
from stelvio.config import StelvioAppConfig, AwsConfig
# New imports
from stelvio.aws.dynamo_db import AttributeType, DynamoTable
from stelvio.aws.api_gateway import Api
Now let's update our @app.run
function to create a DynamoDB table:
@app.run
def run() -> None:
# Stelvio code for Dynamo table
table = DynamoTable(
name="todos",
fields={
"username": AttributeType.STRING,
"created": AttributeType.STRING,
},
partition_key="username",
sort_key='created'
)
The table uses
username
as partition key andcreated
as sort key.
2. Add the API endpoints:
@app.run
def run() -> None:
# Stelvio code for Dynamo table
table = DynamoTable(
name="todos",
fields={
"username": AttributeType.STRING,
"created": AttributeType.STRING,
},
partition_key="username",
sort_key='created'
)
# Stelvio code for API
api = Api("todo-api")
api.route("POST", "/todos", handler="functions/todos.post", links=[table])
api.route("GET", "/todos/{username}", handler="functions/todos.get")
This creates a complete REST API setup. Stelvio automatically handles:
- API Gateway
- Resources (todos, {username})
- Methods (GET and POST)
- Lambda with code from
functions/todos.py
file with:- properly configured env vars containing table name and arn
- generated routing code to properly route requests to proper functions
- Lambda integration between methods and lambda
- IAM (roles, policies, etc.)
- Stage (v1), Deployment and Log groups
Here's our complete infrastructure code:
from stelvio.app import StelvioApp
from stelvio.config import StelvioAppConfig, AwsConfig
from stelvio.aws.dynamo_db import AttributeType, DynamoTable
from stelvio.aws.api_gateway import Api
app = StelvioApp("stelvio-app")
@app.config
def configuration(env: str) -> StelvioAppConfig:
return StelvioAppConfig(
aws=AwsConfig(
region="us-east-1",
profile="your-profile", # or None if using env vars
),
)
@app.run
def run() -> None:
table = DynamoTable(
name="todos",
fields={
"username": AttributeType.STRING,
"created": AttributeType.STRING,
},
partition_key="username",
sort_key='created'
)
api = Api("todo-api")
api.route("POST", "/todos", handler="functions/todos.post", links=[table])
api.route("GET", "/todos/{username}", handler="functions/todos.get")
Just few lines of Python code, and Stelvio takes care of all the AWS setup for you.
Lambda codePermalink
Now let's write the todo API logic in functions/todos.py
:
import json
from datetime import datetime
import boto3
from boto3.dynamodb.conditions import Key
from stlv_resources import Resources # Auto-generated by Stelvio
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(Resources.todos.table_name)
def post(event, context):
# Parse the request body
body = json.loads(event.get('body', '{}'))
# Create item
item = {
'username': body.get('username'),
'created': datetime.utcnow().isoformat(),
'title': body.get('title'),
'done': False
}
# Save to DynamoDB
table.put_item(Item=item)
return {
'statusCode': 201,
'body': json.dumps(item)
}
def get(event, context):
# Get username from query parameters
username = event.get('pathParameters', {}).get('username')
# Query DynamoDB
response = table.query(
KeyConditionExpression=Key('username').eq(username)
)
return {
'statusCode': 200,
'body': json.dumps({
'todos': response['Items']
})
}
DeployPermalink
To deploy we run uv run stlv deploy
. (Or poetry run stlv deploy
or stlv deploy
)
When it finishes it should print something like this at the end:
Outputs:
api_todos-api_arn : "arn:aws:apigateway:us-east-1::/restapis/lr912369v9"
api_todos-api_id : "lr912369v9"
api_todos-api_invoke_url : "https://lr912369v9.execute-api.us-east-1.amazonaws.com/v1"
api_todos-api_stage_name : "v1"
dynamotable_todos_arn : "arn:aws:dynamodb:us-east-1:482403123050:table/stelvio-app-michal-todos-409cbbe"
dynamotable_todos_name : "stelvio-app-michal-todos-409cbbe"
# more outputs here
✓ Deployed in 44s
Copy the api_todos-api_invoke_url
value.
Testing Your APIPermalink
Let's test our todo API, we'll use CURL:
1. Create a todo item:
curl -X POST https://YOUR_API_URL/todos/ \
-d '{"username": "john", "title": "Buy milk"}'
2. And now we can list todos:
curl https://YOUR_API_URL/todos/john
That's it! You've built and deployed a serverless API with just a few files of Python code.
What We've BuiltPermalink
With just a few lines of Python code we've created:
- A Dynamo DB table for todos
- Lambda function with proper permissions
- A REST API with create and list endpoints
- Full AWS deployment with logging
All in pure Python - no YAML, no console clicking, no complex configuration files.
What's NextPermalink
Project StatusPermalink
Stelvio is in active development.
Current LimitationsPermalink
- Lambda: Basic configuration only (no VPC, storage).
- Dynamo DB: No indexes or streams. Only PAY_PER_REQUEST billing mode.
- API Gateway: No authorizers, CORS or custom domains
Coming Soon - RoadmapPermalink
We're working to address these limitations and add support for S3 support. Then SQS, SNS and Aurora.
Getting InvolvedPermalink
If you're interested in contributing, the best way to help right now is to:
- Try Stelvio and share your experience
- Give feedback on the API design
- Share your use cases
- Report issues
LinksPermalink
- 📖 Stelvio Documentation
- 💻 GitHub Repository
- 💬 Questions? Reach me at @michal_stlv
- 📧 michal@stelvio.dev
You might also like
Get Stelvio updates
Stay informed about new features & development.