Skip to content

How to Upgrade from Starlette 0.x to 1.0: Complete Migration Guide

The Problem

I needed to upgrade a Starlette application from 0.x to 1.0.0rc1. The release notes mentioned breaking changes, but they didn’t tell me exactly what would break in my codebase or how to fix it.

I tried searching for a migration guide. Most posts were “what’s new” lists, not step-by-step instructions. I needed to know: what do I change first? What can wait? How do I test without breaking production?

So I did the upgrade, documented every error, and wrote down the fix for each one.

What This Guide Covers

I upgraded a FastAPI application built on Starlette 0.27.0 to Starlette 1.0.0rc1. This guide covers:

  • Pre-upgrade checklist (what to check before you start)
  • Step-by-step upgrade process
  • Breaking changes with code examples
  • FastAPI compatibility issues
  • Testing strategy
  • Rollback plan

If you’re on Starlette 0.20+, the upgrade is straightforward. If you’re on an older version, you’ll have more work.

Pre-Upgrade Checklist

Before touching any dependencies, I checked these things:

1. Python Version

Starlette 1.0 requires Python 3.9+. Run this:

Terminal window
python --version

If you see anything below 3.9, upgrade Python first. There’s no workaround.

2. Current Starlette Version

Terminal window
pip show starlette

This tells you how far you’re jumping. 0.27.0 to 1.0.0rc1 is a small gap. 0.17.0 to 1.0.0rc1 is a big one.

3. GraphQL Usage

Search your codebase for GraphQL imports:

Terminal window
grep -r "from starlette.graphql" .
grep -r "GraphQLApp" .

If you find anything, you’ll need to migrate to a third-party GraphQL library.

4. File Upload Endpoints

Search for UploadFile usage:

Terminal window
grep -r "UploadFile" .

If you have file upload endpoints, test them. The API changed in 0.24.0.

5. Test Coverage

Run your test suite:

Terminal window
pytest --cov

Aim for 80%+ coverage. Without good tests, you’re flying blind during the upgrade.

Step 1: Create a Staging Environment

Don’t upgrade in production. Create a branch and a virtual environment:

Terminal window
# Create upgrade branch
git checkout -b upgrade-starlette-1.0
# Tag your current state (for rollback)
git tag pre-starlette-1.0-upgrade
# Create virtual environment
python -m venv venv-test
source venv-test/bin/activate # Windows: venv-test\Scripts\activate
# Install current dependencies
pip install -r requirements.txt

This gives you a clean slate. If everything breaks, you can roll back instantly.

Step 2: Install Starlette 1.0.0rc1

Terminal window
pip install starlette==1.0.0rc1

Or update your requirements.txt:

starlette==1.0.0rc1

Then reinstall:

Terminal window
pip install -r requirements.txt

Step 3: Run Your Test Suite

Terminal window
pytest

Watch for:

  • Import errors
  • Test failures
  • Deprecation warnings

Write down every failure. You’ll fix them one by one.

Step 4: Fix Import Errors (GraphQL)

If you’re using Starlette’s built-in GraphQL, you’ll see this:

ModuleNotFoundError: No module named 'starlette.graphql'

GraphQL support was removed in Starlette 0.17.0. You need to switch to a third-party library.

I recommend Strawberry GraphQL:

# OLD (Starlette 0.x)
from starlette.graphql import GraphQLApp
from starlette.applications import Starlette
app = Starlette()
app.add_route('/graphql', GraphQLApp(schema=schema))
# NEW (Strawberry)
from strawberry.asgi import GraphQL
from starlette.applications import Starlette
graphql_app = GraphQL(schema)
app = Starlette(routes=[
Route('/graphql', graphql_app)
])

Install it:

Terminal window
pip install strawberry-graphql

Alternative libraries:

  • Graphene - Mature, widely adopted
  • Ariadne - Fast ASGI-first implementation

Step 5: Fix UploadFile Errors

If you have file upload endpoints, you might see deprecation warnings or test failures.

The problem: UploadFile.file is now required.

Old code (0.23.x and earlier):

from starlette.datastructures import UploadFile
# file was optional, created automatically
upload = UploadFile(filename="test.txt")

New code (0.24.0+):

from starlette.datastructures import UploadFile
from tempfile import SpooledTemporaryFile
# file must be provided explicitly
spool = SpooledTemporaryFile(max_size=500_000)
upload = UploadFile(file=spool, filename="test.txt")

In FastAPI endpoints:

from fastapi import UploadFile
@app.post("/upload")
async def upload_file(file: UploadFile):
# OLD (might fail in 1.0.0)
content = await file.read()
# NEW (recommended)
with await file.file as f:
content = await f.read()
return {"size": len(content)}

Test every file upload endpoint after this change.

Bonus fix: max_file_size was renamed to spool_max_size in 0.46.0:

# OLD
upload = UploadFile(max_file_size=1_000_000)
# NEW
upload = UploadFile(spool_max_size=1_000_000)

Step 6: Update Route Decorators (Optional)

If you’re using @app.route() decorators, you’ll see deprecation warnings:

DeprecationWarning: @app.route() is deprecated, use Route() instead

Old code:

from starlette.applications import Starlette
app = Starlette()
@app.route('/users/{user_id}')
async def get_user(request):
user_id = request.path_params['user_id']
return JSONResponse({'user_id': user_id})

New code:

from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse
async def get_user(request):
user_id = request.path_params['user_id']
return JSONResponse({'user_id': user_id})
routes = [
Route('/users/{user_id}', get_user)
]
app = Starlette(routes=routes)

This is more code, but it’s cleaner and easier to test.

Note: @app.route() still works in 1.0.0rc1. You can defer this refactoring if you’re pressed for time.

Step 7: Check FastAPI Compatibility

FastAPI depends on Starlette. When you upgrade Starlette, you should also upgrade FastAPI.

Check your FastAPI version:

Terminal window
pip show fastapi

Recommendation: FastAPI 0.100+ for Starlette 1.0.0rc1.

Upgrade FastAPI:

Terminal window
pip install fastapi --upgrade

Test these endpoints specifically:

  • File uploads (UploadFile)
  • Custom middleware
  • GraphQL (if applicable)
  • WebSocket connections

Step 8: Verify Middleware Behavior

Starlette 0.24.0 introduced lazy middleware initialization. This means middleware is built at request time, not application startup.

Most apps don’t need changes. But if you have custom middleware, test it:

from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
app = Starlette(middleware=[
Middleware(SessionMiddleware, secret_key="your-secret-key")
])

Verify:

  1. Middleware runs in the correct order
  2. Session data persists correctly
  3. CORS headers are applied properly
  4. Authentication/authorization works

Step 9: Test URL Routing (IPv6)

Starlette 0.24.0 fixed IPv6 URL parsing. This is transparent for most apps, but test if you use IPv6 addresses:

from starlette.datastructures import URL
url = URL("http://[::1]:8000/path")
assert url.host == "::1"
assert url.port == 8000

If you proxy requests or parse URLs manually, add a test case for IPv6.

Testing Strategy

Don’t deploy without testing. Here’s what I did:

1. Unit Tests

Run your existing unit tests:

Terminal window
pytest

Focus on:

  • Route handlers
  • File upload logic
  • Middleware behavior

2. Integration Tests

Test your API endpoints:

Terminal window
pytest tests/test_api.py

Verify:

  • All endpoints return 200 OK
  • File uploads work correctly
  • Database operations complete
  • External integrations (Redis, S3, etc.) connect

3. Manual Testing

Load your staging environment and click through:

  • Authentication flow
  • File upload/download
  • Admin panels
  • API documentation (Swagger/ReDoc)

4. Load Testing

Benchmark before and after:

Terminal window
# Install locust
pip install locust
# Run load test
locust -f locustfile.py --host=https://staging.yourapp.com

Look for:

  • Increased response times
  • Memory leaks
  • Connection pool exhaustion

Common Issues & Solutions

Issue: Import errors after upgrade

ModuleNotFoundError: No module named 'starlette.graphql'

Solution: Install a third-party GraphQL library (Strawberry, Graphene, Ariadne).

Issue: File upload tests failing

TypeError: UploadFile.__init__() missing 1 required positional argument: 'file'

Solution: Update your code to pass file argument explicitly. See Step 5.

Issue: Middleware not loading

AttributeError: 'NoneType' object has no attribute 'middleware'

Solution: Verify middleware is initialized before the app starts. Check initialization order.

Issue: FastAPI routes breaking

AssertionError: Response status code should be 200, but was 422

Solution: Update FastAPI to 0.100+ and verify request/response models.

Rollback Strategy

If everything breaks, roll back:

Terminal window
# 1. Deactivate virtual environment
deactivate
# 2. Checkout pre-upgrade tag
git checkout pre-starlette-1.0-upgrade
# 3. Reinstall old dependencies
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# 4. Run tests to verify
pytest

Better approach: Keep your old requirements.txt:

Terminal window
# Before upgrade
cp requirements.txt requirements-old.txt
# After upgrade (if rollback needed)
pip install -r requirements-old.txt

Production Deployment Checklist

Before deploying to production:

  • All tests passing (unit, integration, E2E)
  • Staging environment validated
  • Performance benchmarks acceptable (no regressions)
  • Monitoring/alerting configured (Sentry, Datadog, etc.)
  • Rollback plan documented
  • Team briefed on changes
  • Deployment window scheduled (low-traffic hours)

Migration Timeline

Here’s how long it took me:

  • Assessment: 1-2 hours (check versions, search codebase)
  • Testing: 2-4 hours (run tests, identify failures)
  • Migration: 4-8 hours (fix breaking changes, refactor code)
  • Deployment: 1-2 hours (staging rollout, monitoring)

Total: 1-2 days for a medium-sized FastAPI app.

Your mileage may vary. Larger apps with extensive file upload logic or custom middleware might take 3-5 days.

When NOT to Upgrade

Skip this release if:

  • You’re on Python 3.8 or earlier → Upgrade Python first
  • You’re using Starlette’s built-in GraphQL → Migrate to third-party first
  • You have extensive file upload logic with no test coverage → Add tests first
  • You’re running a critical production system with no staging environment → Set up staging first

Wait for 1.0.0 final if:

  • You want maximum stability
  • You’re not pressed for new features
  • You’re close to a major deadline

What I Recommend

If you have good test coverage, try 1.0.0rc1 in staging now. The changes are minimal if you’ve been keeping up with deprecation warnings.

If you’re risk-averse, wait for the final 1.0.0 release. Release candidates are typically stable, but the final release will have broader testing.

Either way, don’t wait too long. Old Starlette versions won’t receive security updates forever.

Summary

Migrating from Starlette 0.x to 1.0 isn’t scary if you follow a systematic approach:

  1. Check Python version → Must be 3.9+
  2. Audit your codebase → GraphQL, file uploads, middleware
  3. Install 1.0.0rc1 → Test environment only
  4. Run test suite → Document every failure
  5. Fix breaking changes → GraphQL removal, UploadFile changes
  6. Update FastAPI → Version 0.100+ recommended
  7. Test thoroughly → Unit, integration, load tests
  8. Deploy to staging → Monitor for regressions
  9. Rollout to production → During low-traffic hours

The non-breaking improvements (lazy middleware, IPv6 fixes, HTTP Range support) are nice bonuses that require no code changes.

Most importantly: have a rollback plan. Tag your pre-upgrade state, keep your old requirements.txt, and deploy during low-traffic hours.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments