Skip to main content

Full Example

A complete example using features with DynamoDB, cron jobs, and split Lambda routes.

1. deployless.yaml​

deployless.yaml
name: ums-api
provider: aws
stage: dev

paths:
features: app/features
shared: app/shared

globals:
runtime: python3.13
memory: 256
timeout: 30
log_retention: 14

api:
endpoint_type: REGIONAL
cors:
allow_origin: "*"
allow_methods: [GET, POST, PUT, DELETE, OPTIONS]
allow_headers: [Content-Type, Authorization, X-Api-Key]

env:
LOG_LEVEL: INFO

2. Feature: users​

app/features/user/routes.py
from flask import Blueprint, request, g, jsonify
import deployless as dpl

from app.features.user.schemas import CreateUserRequest, UpdateUserRequest
from app.features.user.use_cases import create_user, list_users, get_user, update_user, delete_user
from app.shared.decorators import require_auth, require_scopes

# ── Lambda configuration ──
dpl.configure(
memory=512,
timeout=30,
description="User Management Service",
resources={
"users": dpl.DynamoDB(
"ums-users",
pk="tenant_id",
pk_type="S",
sk="user_id",
sk_type="S",
gsi=[
{
"name": "EmailIndex",
"pk": "email",
"pk_type": "S",
}
],
ttl_attribute="expires_at",
deletion_policy="Retain",
),
"sessions": dpl.DynamoDB(
"ums-sessions",
pk="session_id",
ttl_attribute="expires_at",
),
},
env={
"TOKEN_EXPIRY": "3600",
},
)

# ── Cron: daily cleanup ──
@dpl.cron(
schedule="rate(24 hours)",
memory=128,
timeout=60,
description="Cleanup expired sessions",
)
def cleanup_sessions(event, context):
return {"status": "ok"}

# ── Flask Blueprint ──
user_bp = Blueprint("user_bp", __name__, url_prefix="/users")

@user_bp.route("", methods=["POST"])
@require_auth
@require_scopes(["ums:users:create"])
def create_user_route():
data = request.get_json()
req = CreateUserRequest(
email=data.get("email"),
password=data.get("password"),
scopes=data.get("scopes", []),
)
response = create_user(req, g.user["tenant_id"])
return jsonify(response.to_dict()), 201

@user_bp.route("", methods=["GET"])
@require_auth
@require_scopes(["ums:users:read"])
def list_users_route():
response = list_users(g.user["tenant_id"])
return jsonify(response.to_dict()), 200

@user_bp.route("/<user_id>", methods=["GET"])
@require_auth
@require_scopes(["ums:users:read"])
def get_user_route(user_id):
response = get_user(g.user["tenant_id"], user_id)
return jsonify(response.to_dict()), 200

@user_bp.route("/<user_id>", methods=["PUT"])
@require_auth
@require_scopes(["ums:users:update"])
def update_user_route(user_id):
data = request.get_json()
req = UpdateUserRequest(
email=data.get("email"),
password=data.get("password"),
scopes=data.get("scopes"),
)
response = update_user(g.user["tenant_id"], user_id, req)
return jsonify(response.to_dict()), 200

@user_bp.route("/<user_id>", methods=["DELETE"])
@require_auth
@require_scopes(["ums:users:delete"])
def delete_user_route(user_id):
delete_user(g.user["tenant_id"], user_id)
return "", 204

3. Build​

deployless build --verbose
[deployless] Project: ums-api | Stage: dev | Provider: aws
[deployless] Features found: ['auth', 'tenant', 'user']
[deployless] auth: 3 routes, 0 split
[deployless] tenant: 2 routes, 0 split
[deployless] user: 5 routes, 0 split
[deployless] Crons: ['cleanup_sessions']
[deployless] Validation passed.
[deployless] Built: .dist/AuthFunction
[deployless] Built: .dist/TenantFunction
[deployless] Built: .dist/UserFunction
[deployless] Built cron: .dist/CleanupSessionsFunction
[deployless] Template generated: /path/to/project/template.yaml

4. Deploy​

# First time (SAM interactive wizard)
deployless deploy --guided --stage prod

# Subsequent deployments
deployless deploy --stage prod

5. Generated template.yaml (summary)​

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: ums-api — Generated by deployless

Globals:
Function:
Runtime: python3.13
MemorySize: 256
Timeout: 30
Environment:
Variables:
LOG_LEVEL: INFO
APP_STAGE: dev

Resources:
Api:
Type: AWS::Serverless::Api
Properties:
StageName: dev
EndpointConfiguration: REGIONAL
Cors:
AllowOrigin: "'*'"
AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization,X-Api-Key'"

UserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: .dist/UserFunction/
Handler: bootstrap.handler
MemorySize: 512
Timeout: 30
Description: User Management Service
Environment:
Variables:
UMS_USERS_TABLE:
Ref: UmsUsersTable
UMS_SESSIONS_TABLE:
Ref: UmsSessionsTable
TOKEN_EXPIRY: '3600'
Events:
UserPostGet:
Type: Api
Properties:
RestApiId:
Ref: Api
Path: /users
Method: get
# ... more events

UmsUsersTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
Properties:
TableName: ums-users
BillingMode: PAY_PER_REQUEST
# ... attributes, GSI, TTL

CleanupSessionsFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: .dist/CleanupSessionsFunction/
Handler: bootstrap.handler
MemorySize: 128
Timeout: 60
Events:
Schedule:
Type: Schedule
Properties:
Schedule: rate(24 hours)

Outputs:
ApiUrl:
Description: API Gateway endpoint URL
Value:
Fn::Sub: https://${Api}.execute-api.${AWS::Region}.amazonaws.com/dev
UserFunctionArn:
Value:
Fn::GetAtt: [UserFunction, Arn]