Introducing Stelvio

Introducing Stelvio

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 and created 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:

  1. Try Stelvio and share your experience
  2. Give feedback on the API design
  3. Share your use cases
  4. Report issues

You might also like

Get Stelvio updates

Stay informed about new features & development.

    We won't send you spam. Unsubscribe at any time.