UNPKG

10.2 kBMarkdownView Raw
1# Serverless WSGI
2
3[![npm package](https://nodei.co/npm/serverless-wsgi.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/serverless-wsgi/)
4
5[![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com)
6[![Build Status](https://travis-ci.org/logandk/serverless-wsgi.png?branch=master)](https://travis-ci.org/logandk/serverless-wsgi)
7[![Coverage Status](https://coveralls.io/repos/github/logandk/serverless-wsgi/badge.svg?branch=master)](https://coveralls.io/github/logandk/serverless-wsgi?branch=master)
8[![Dependency Status](https://david-dm.org/logandk/serverless-wsgi.png)](https://david-dm.org/logandk/serverless-wsgi)
9[![Dev Dependency Status](https://david-dm.org/logandk/serverless-wsgi/dev-status.png)](https://david-dm.org/logandk/serverless-wsgi?type=dev)
10
11A Serverless v1.x plugin to build your deploy Python WSGI applications using Serverless. Compatible
12WSGI application frameworks include Flask, Django and Pyramid - for a complete list, see:
13http://wsgi.readthedocs.io/en/latest/frameworks.html.
14
15### Features
16
17- Transparently converts API Gateway requests to and from standard WSGI requests
18- Supports anything you'd expect from WSGI such as redirects, cookies, file uploads etc.
19- Automatically downloads Python packages that you specify in `requirements.txt` and deploys them along with your application
20- Convenient `wsgi serve` command for serving your application locally during development
21
22## Install
23
24```
25npm install --save serverless-wsgi
26```
27
28Add the plugin to your `serverless.yml` file and set the WSGI application:
29
30```yaml
31plugins:
32 - serverless-wsgi
33```
34
35## Flask configuration example
36
37This example assumes that you have intialized your application as `app` inside `api.py`.
38
39```
40project
41├── api.py
42├── requirements.txt
43└── serverless.yml
44```
45
46### api.py
47
48A regular Flask application.
49
50```python
51from flask import Flask
52app = Flask(__name__)
53
54
55@app.route("/cats")
56def cats():
57 return "Cats"
58
59
60@app.route("/dogs/<id>")
61def dog(id):
62 return "Dog"
63```
64
65### serverless.yml
66
67Load the plugin and set the `custom.wsgi.app` configuration in `serverless.yml` to the
68module path of your Flask application.
69
70All functions that will use WSGI need to have `wsgi.handler` set as the Lambda handler and
71use the default `lambda-proxy` integration for API Gateway. This configuration example treats
72API Gateway as a transparent proxy, passing all requests directly to your Flask application,
73and letting the application handle errors, 404s etc.
74
75```yaml
76service: example
77
78provider:
79 name: aws
80 runtime: python2.7
81
82plugins:
83 - serverless-wsgi
84
85functions:
86 api:
87 handler: wsgi.handler
88 events:
89 - http: ANY /
90 - http: ANY {proxy+}
91
92custom:
93 wsgi:
94 app: api.app
95```
96
97### requirements.txt
98
99Add Flask to the application bundle.
100
101```
102Flask==0.12.2
103```
104
105## Deployment
106
107Simply run the serverless deploy command as usual:
108
109```
110$ sls deploy
111Serverless: Packaging Python WSGI handler...
112Serverless: Packaging required Python packages...
113Serverless: Packaging service...
114Serverless: Removing old service versions...
115Serverless: Uploading CloudFormation file to S3...
116Serverless: Uploading service .zip file to S3...
117Serverless: Updating Stack...
118Serverless: Checking Stack update progress...
119..........
120Serverless: Stack update finished...
121```
122
123## Other frameworks
124
125Set `custom.wsgi.app` in `serverless.yml` according to your WSGI callable:
126
127- For Pyramid, use [make_wsgi_app](http://docs.pylonsproject.org/projects/pyramid/en/latest/api/config.html#pyramid.config.Configurator.make_wsgi_app) to intialize the callable
128- Django is configured for WSGI by default, set the callable to `<project_name>.wsgi.application`. See https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ for more information.
129
130## Usage
131
132### Automatic requirement packaging
133
134You'll need to include any packages that your application uses in the bundle
135that's deployed to AWS Lambda. This plugin helps you out by doing this automatically,
136as long as you specify your required packages in a `requirements.txt` file in the root
137of your Serverless service path:
138
139```
140Flask==0.12.2
141requests==2.18.3
142```
143
144For more information, see https://pip.readthedocs.io/en/1.1/requirements.html.
145
146You can use the requirement packaging functionality of _serverless-wsgi_ without the WSGI
147handler itself by including the plugin in your `serverless.yml` configuration, without specifying
148the `custom.wsgi.app` setting. This will omit the WSGI handler from the package, but include
149any requirements specified in `requirements.txt`.
150
151If you don't want to use automatic requirement packaging you can set `custom.wsgi.packRequirements` to false:
152
153```yaml
154custom:
155 wsgi:
156 app: api.app
157 packRequirements: false
158```
159
160For a more advanced approach to packaging requirements, consider using https://github.com/UnitedIncome/serverless-python-requirements.
161
162### Python version
163
164Python is used for packaging requirements and serving the app when invoking `sls wsgi serve`. By
165default, the current runtime setting is expected to be the name of the Python binary in `PATH`,
166for instance `python3.6`. If this is not the name of your Python binary, override it using the
167`pythonBin` option:
168
169```yaml
170custom:
171 wsgi:
172 app: api.app
173 pythonBin: python3
174```
175
176### Local server
177
178For convenience, a `sls wsgi serve` command is provided to run your WSGI application
179locally. This command requires the `werkzeug` Python package to be installed,
180and acts as a simple wrapper for starting werkzeug's built-in HTTP server.
181
182By default, the server will start on port 5000.
183
184```
185$ sls wsgi serve
186 * Running on http://localhost:5000/ (Press CTRL+C to quit)
187 * Restarting with stat
188 * Debugger is active!
189```
190
191Configure the port using the `-p` parameter:
192
193```
194$ sls wsgi serve -p 8000
195 * Running on http://localhost:8000/ (Press CTRL+C to quit)
196 * Restarting with stat
197 * Debugger is active!
198```
199
200When running locally, an environment variable named `IS_OFFLINE` will be set to `"True"`.
201So, if you want to know when the application is running locally, check `os.environ["IS_OFFLINE"]`.
202
203### Explicit routes
204
205If you'd like to be explicit about which routes and HTTP methods should pass through to your
206application, see the following example:
207
208```yaml
209service: example
210
211provider:
212 name: aws
213 runtime: python2.7
214
215plugins:
216 - serverless-wsgi
217
218functions:
219 api:
220 handler: wsgi.handler
221 events:
222 - http:
223 path: cats
224 method: get
225 integration: lambda-proxy
226 - http:
227 path: dogs/{id}
228 method: get
229 integration: lambda-proxy
230
231custom:
232 wsgi:
233 app: api.app
234```
235
236### Custom domain names
237
238If you use custom domain names with API Gateway, you might have a base path that is
239at the beginning of your path, such as the stage (`/dev`, `/stage`, `/prod`). You
240can pass in an `API_GATEWAY_BASE_PATH` environment variable so your WSGI app can
241handle it correctly.
242
243The example below uses the [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager)
244plugin to handle custom domains in API Gateway:
245
246```yaml
247service: example
248
249provider:
250 name: aws
251 runtime: python2.7
252 environment:
253 API_GATEWAY_BASE_PATH: ${self:custom.customDomain.basePath}
254
255plugins:
256 - serverless-wsgi
257 - serverless-domain-manager
258
259functions:
260 api:
261 handler: wsgi.handler
262 events:
263 - http: ANY /
264 - http: ANY {proxy+}
265
266custom:
267 wsgi:
268 app: api.app
269 customDomain:
270 basePath: ${opt:stage}
271 domainName: mydomain.name.com
272 stage: ${opt:stage}
273 createRoute53Record: true
274```
275
276### File uploads
277
278In order to accept file uploads from HTML forms, make sure to add `multipart/form-data` to
279the list of content types with _Binary Support_ in your API Gateway API. The
280[serverless-apigw-binary](https://github.com/maciejtreder/serverless-apigw-binary)
281Serverless plugin can be used to automate this process.
282
283Keep in mind that, when building Serverless applications, uploading
284[directly to S3](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html)
285from the browser is usually the preferred approach.
286
287### Raw context and event
288
289The raw context and event from AWS Lambda are both accessible through the WSGI
290request. The following example shows how to access them when using Flask:
291
292```python
293from flask import Flask, request
294app = Flask(__name__)
295
296
297@app.route("/")
298def index():
299 print(request.environ['context'])
300 print(request.environ['event'])
301```
302
303### Text MIME types
304
305By default, all MIME types starting with `text/` and the following whitelist are sent
306through API Gateway in plain text. All other MIME types will have their response body
307base64 encoded (and the `isBase64Encoded` API Gateway flag set) in order to be
308delivered by API Gateway as binary data.
309
310This is the default whitelist of plain text MIME types:
311
312- `application/json`
313- `application/javascript`
314- `application/xml`
315- `application/vnd.api+json`
316
317In order to add additional plain text MIME types to this whitelist, use the
318`textMimeTypes` configuration option:
319
320```yaml
321custom:
322 wsgi:
323 app: api.app
324 textMimeTypes:
325 - application/custom+json
326 - application/vnd.company+json
327```
328
329## Usage without Serverless
330
331The AWS API Gateway to WSGI mapping module is available on PyPI in the
332`serverless-wsgi` package.
333
334Use this package if you need to deploy Python Lambda functions to handle
335API Gateway events directly, without using the Serverless framework.
336
337```
338pip install serverless-wsgi
339```
340
341Initialize your WSGI application and in your Lambda event handler, call
342the request mapper:
343
344```python
345import app # Replace with your actual application
346import serverless_wsgi
347
348# If you need to send additional content types as text, add then directly
349# to the whitelist:
350#
351# serverless_wsgi.TEXT_MIME_TYPES.append("application/custom+json")
352
353def handle(event, context):
354 return serverless_wsgi.handle_request(app.app, event, context)
355```
356
357# Thanks
358
359Thanks to [Zappa](https://github.com/Miserlou/Zappa), which has been both the
360inspiration and source of several implementations that went into this project.
361
362Thanks to [chalice](https://github.com/awslabs/chalice) for the
363requirement packaging implementation.