Skip to content

Commit 842c2cd

Browse files
authored
Add FastAPI response streaming example (#211)
1 parent 796acc8 commit 842c2cd

File tree

8 files changed

+363
-0
lines changed

8 files changed

+363
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
2+
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
3+
4+
### Linux ###
5+
*~
6+
7+
# temporary files which can be created if a process still has a handle open of a deleted file
8+
.fuse_hidden*
9+
10+
# KDE directory preferences
11+
.directory
12+
13+
# Linux trash folder which might appear on any partition or disk
14+
.Trash-*
15+
16+
# .nfs files are created when an open file is removed but is still being accessed
17+
.nfs*
18+
19+
### OSX ###
20+
*.DS_Store
21+
.AppleDouble
22+
.LSOverride
23+
24+
# Icon must end with two \r
25+
Icon
26+
27+
# Thumbnails
28+
._*
29+
30+
# Files that might appear in the root of a volume
31+
.DocumentRevisions-V100
32+
.fseventsd
33+
.Spotlight-V100
34+
.TemporaryItems
35+
.Trashes
36+
.VolumeIcon.icns
37+
.com.apple.timemachine.donotpresent
38+
39+
# Directories potentially created on remote AFP share
40+
.AppleDB
41+
.AppleDesktop
42+
Network Trash Folder
43+
Temporary Items
44+
.apdisk
45+
46+
### PyCharm ###
47+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
48+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
49+
50+
# User-specific stuff:
51+
.idea/**/workspace.xml
52+
.idea/**/tasks.xml
53+
.idea/dictionaries
54+
55+
# Sensitive or high-churn files:
56+
.idea/**/dataSources/
57+
.idea/**/dataSources.ids
58+
.idea/**/dataSources.xml
59+
.idea/**/dataSources.local.xml
60+
.idea/**/sqlDataSources.xml
61+
.idea/**/dynamic.xml
62+
.idea/**/uiDesigner.xml
63+
64+
# Gradle:
65+
.idea/**/gradle.xml
66+
.idea/**/libraries
67+
68+
# CMake
69+
cmake-build-debug/
70+
71+
# Mongo Explorer plugin:
72+
.idea/**/mongoSettings.xml
73+
74+
## File-based project format:
75+
*.iws
76+
77+
## Plugin-specific files:
78+
79+
# IntelliJ
80+
/out/
81+
82+
# mpeltonen/sbt-idea plugin
83+
.idea_modules/
84+
85+
# JIRA plugin
86+
atlassian-ide-plugin.xml
87+
88+
# Cursive Clojure plugin
89+
.idea/replstate.xml
90+
91+
# Ruby plugin and RubyMine
92+
/.rakeTasks
93+
94+
# Crashlytics plugin (for Android Studio and IntelliJ)
95+
com_crashlytics_export_strings.xml
96+
crashlytics.properties
97+
crashlytics-build.properties
98+
fabric.properties
99+
100+
### PyCharm Patch ###
101+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
102+
103+
# *.iml
104+
# modules.xml
105+
# .idea/misc.xml
106+
# *.ipr
107+
108+
# Sonarlint plugin
109+
.idea/sonarlint
110+
111+
### Python ###
112+
# Byte-compiled / optimized / DLL files
113+
__pycache__/
114+
*.py[cod]
115+
*$py.class
116+
117+
# C extensions
118+
*.so
119+
120+
# Distribution / packaging
121+
.Python
122+
build/
123+
develop-eggs/
124+
dist/
125+
downloads/
126+
eggs/
127+
.eggs/
128+
lib/
129+
lib64/
130+
parts/
131+
sdist/
132+
var/
133+
wheels/
134+
*.egg-info/
135+
.installed.cfg
136+
*.egg
137+
138+
# PyInstaller
139+
# Usually these files are written by a python script from a template
140+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
141+
*.manifest
142+
*.spec
143+
144+
# Installer logs
145+
pip-log.txt
146+
pip-delete-this-directory.txt
147+
148+
# Unit test / coverage reports
149+
htmlcov/
150+
.tox/
151+
.coverage
152+
.coverage.*
153+
.cache
154+
.pytest_cache/
155+
nosetests.xml
156+
coverage.xml
157+
*.cover
158+
.hypothesis/
159+
160+
# Translations
161+
*.mo
162+
*.pot
163+
164+
# Flask stuff:
165+
instance/
166+
.webassets-cache
167+
168+
# Scrapy stuff:
169+
.scrapy
170+
171+
# Sphinx documentation
172+
docs/_build/
173+
174+
# PyBuilder
175+
target/
176+
177+
# Jupyter Notebook
178+
.ipynb_checkpoints
179+
180+
# pyenv
181+
.python-version
182+
183+
# celery beat schedule file
184+
celerybeat-schedule.*
185+
186+
# SageMath parsed files
187+
*.sage.py
188+
189+
# Environments
190+
.env
191+
.venv
192+
env/
193+
venv/
194+
ENV/
195+
env.bak/
196+
venv.bak/
197+
198+
# Spyder project settings
199+
.spyderproject
200+
.spyproject
201+
202+
# Rope project settings
203+
.ropeproject
204+
205+
# mkdocs documentation
206+
/site
207+
208+
# mypy
209+
.mypy_cache/
210+
211+
### VisualStudioCode ###
212+
.vscode/*
213+
!.vscode/settings.json
214+
!.vscode/tasks.json
215+
!.vscode/launch.json
216+
!.vscode/extensions.json
217+
.history
218+
219+
### Windows ###
220+
# Windows thumbnail cache files
221+
Thumbs.db
222+
ehthumbs.db
223+
ehthumbs_vista.db
224+
225+
# Folder config file
226+
Desktop.ini
227+
228+
# Recycle Bin used on file shares
229+
$RECYCLE.BIN/
230+
231+
# Windows Installer files
232+
*.cab
233+
*.msi
234+
*.msm
235+
*.msp
236+
237+
# Windows shortcuts
238+
*.lnk
239+
240+
# Build folder
241+
242+
*/build/*
243+
244+
# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# FastAPI Response Streaming
2+
3+
This example shows how to use Lambda Web Adapter to run a FastAPI application with response streaming via a Function URL.
4+
5+
### How does it work?
6+
7+
We add Lambda Web Adapter layer to the function and configure wrapper script.
8+
9+
1. attach Lambda Adapter layer to your function. This layer containers Lambda Adapter binary and a wrapper script.
10+
1. x86_64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:16`
11+
2. arm64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:16`
12+
2. configure Lambda environment variable `AWS_LAMBDA_EXEC_WRAPPER` to `/opt/bootstrap`. This is a wrapper script included in the layer.
13+
3. set function handler to a startup command: `run.sh`. The wrapper script will execute this command to boot up your application.
14+
15+
To get more information of Wrapper script, please read Lambda documentation [here](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-modify.html#runtime-wrapper).
16+
17+
This is the resource for Lambda function. The function urls's invoke mode is configured as "RESPONSE_STREAM", and Lambda environment variable "AWS_LWA_INVOKE_MODE" is set to "response_stream".
18+
19+
```yaml
20+
FastAPIFunction:
21+
Type: AWS::Serverless::Function
22+
Properties:
23+
CodeUri: app/
24+
Handler: run.sh
25+
Runtime: python3.9
26+
MemorySize: 256
27+
Environment:
28+
Variables:
29+
AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
30+
AWS_LWA_INVOKE_MODE: response_stream
31+
PORT: 8000
32+
Layers:
33+
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:16
34+
FunctionUrlConfig:
35+
AuthType: NONE
36+
InvokeMode: RESPONSE_STREAM
37+
```
38+
39+
### Build and Deploy
40+
41+
Run the following commands to build and deploy the application to lambda.
42+
43+
```bash
44+
sam build --use-container
45+
sam deploy --guided
46+
```
47+
When the deployment completes, take note of FastAPI's Value. It is the API Gateway endpoint URL.
48+
49+
### Verify it works
50+
51+
Open FastAPI's URL in a browser, you should see "This is streaming from Lambda" streams back 10 times.
52+

examples/fastapi-response-streaming/__init__.py

Whitespace-only changes.

examples/fastapi-response-streaming/app/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from fastapi import FastAPI
2+
from fastapi.responses import StreamingResponse
3+
import asyncio
4+
5+
app = FastAPI()
6+
7+
8+
async def streamer():
9+
for i in range(10):
10+
await asyncio.sleep(1)
11+
yield b"This is streaming from Lambda \n"
12+
13+
14+
@app.get("/")
15+
async def index():
16+
return StreamingResponse(streamer(), media_type="text/plain; charset=utf-8")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
anyio==3.6.2
2+
click==8.1.3
3+
fastapi==0.92.0
4+
h11==0.14.0
5+
idna==3.4
6+
importlib-metadata==6.0.0
7+
pydantic==1.10.5
8+
sniffio==1.3.0
9+
starlette==0.25.0
10+
typing_extensions==4.5.0
11+
uvicorn==0.20.0
12+
zipp==3.13.0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
3+
PATH=$PATH:$LAMBDA_TASK_ROOT/bin PYTHONPATH=$LAMBDA_TASK_ROOT exec python -m uvicorn --port=$PORT main:app
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: >
4+
fastapi response streaming
5+
6+
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
7+
Globals:
8+
Function:
9+
Timeout: 60
10+
11+
Resources:
12+
FastAPIFunction:
13+
Type: AWS::Serverless::Function
14+
Properties:
15+
CodeUri: app/
16+
Handler: run.sh
17+
Runtime: python3.9
18+
MemorySize: 256
19+
Environment:
20+
Variables:
21+
AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
22+
AWS_LWA_INVOKE_MODE: response_stream
23+
PORT: 8000
24+
Layers:
25+
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:16
26+
FunctionUrlConfig:
27+
AuthType: NONE
28+
InvokeMode: RESPONSE_STREAM
29+
30+
Outputs:
31+
FastAPIFunctionUrl:
32+
Description: "Function URL for FastAPI function"
33+
Value: !GetAtt FastAPIFunctionUrl.FunctionUrl
34+
FastAPIFunction:
35+
Description: "FastAPI Lambda Function ARN"
36+
Value: !GetAtt FastAPIFunction.Arn

0 commit comments

Comments
 (0)