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 |
|
11 | A Serverless v1.x plugin to build your deploy Python WSGI applications using Serverless. Compatible
|
12 | WSGI application frameworks include Flask, Django and Pyramid - for a complete list, see:
|
13 | http://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 | ```
|
25 | npm install --save serverless-wsgi
|
26 | ```
|
27 |
|
28 | Add the plugin to your `serverless.yml` file and set the WSGI application:
|
29 |
|
30 | ```yaml
|
31 | plugins:
|
32 | - serverless-wsgi
|
33 | ```
|
34 |
|
35 | ## Flask configuration example
|
36 |
|
37 | This example assumes that you have intialized your application as `app` inside `api.py`.
|
38 |
|
39 | ```
|
40 | project
|
41 | ├── api.py
|
42 | ├── requirements.txt
|
43 | └── serverless.yml
|
44 | ```
|
45 |
|
46 | ### api.py
|
47 |
|
48 | A regular Flask application.
|
49 |
|
50 | ```python
|
51 | from flask import Flask
|
52 | app = Flask(__name__)
|
53 |
|
54 |
|
55 | @app.route("/cats")
|
56 | def cats():
|
57 | return "Cats"
|
58 |
|
59 |
|
60 | @app.route("/dogs/<id>")
|
61 | def dog(id):
|
62 | return "Dog"
|
63 | ```
|
64 |
|
65 | ### serverless.yml
|
66 |
|
67 | Load the plugin and set the `custom.wsgi.app` configuration in `serverless.yml` to the
|
68 | module path of your Flask application.
|
69 |
|
70 | All functions that will use WSGI need to have `wsgi.handler` set as the Lambda handler and
|
71 | use the default `lambda-proxy` integration for API Gateway. This configuration example treats
|
72 | API Gateway as a transparent proxy, passing all requests directly to your Flask application,
|
73 | and letting the application handle errors, 404s etc.
|
74 |
|
75 | ```yaml
|
76 | service: example
|
77 |
|
78 | provider:
|
79 | name: aws
|
80 | runtime: python2.7
|
81 |
|
82 | plugins:
|
83 | - serverless-wsgi
|
84 |
|
85 | functions:
|
86 | api:
|
87 | handler: wsgi.handler
|
88 | events:
|
89 | - http: ANY /
|
90 | - http: ANY {proxy+}
|
91 |
|
92 | custom:
|
93 | wsgi:
|
94 | app: api.app
|
95 | ```
|
96 |
|
97 | ### requirements.txt
|
98 |
|
99 | Add Flask to the application bundle.
|
100 |
|
101 | ```
|
102 | Flask==0.12.2
|
103 | ```
|
104 |
|
105 | ## Deployment
|
106 |
|
107 | Simply run the serverless deploy command as usual:
|
108 |
|
109 | ```
|
110 | $ sls deploy
|
111 | Serverless: Packaging Python WSGI handler...
|
112 | Serverless: Packaging required Python packages...
|
113 | Serverless: Packaging service...
|
114 | Serverless: Removing old service versions...
|
115 | Serverless: Uploading CloudFormation file to S3...
|
116 | Serverless: Uploading service .zip file to S3...
|
117 | Serverless: Updating Stack...
|
118 | Serverless: Checking Stack update progress...
|
119 | ..........
|
120 | Serverless: Stack update finished...
|
121 | ```
|
122 |
|
123 | ## Other frameworks
|
124 |
|
125 | Set `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 |
|
134 | You'll need to include any packages that your application uses in the bundle
|
135 | that's deployed to AWS Lambda. This plugin helps you out by doing this automatically,
|
136 | as long as you specify your required packages in a `requirements.txt` file in the root
|
137 | of your Serverless service path:
|
138 |
|
139 | ```
|
140 | Flask==0.12.2
|
141 | requests==2.18.3
|
142 | ```
|
143 |
|
144 | For more information, see https://pip.readthedocs.io/en/1.1/requirements.html.
|
145 |
|
146 | You can use the requirement packaging functionality of _serverless-wsgi_ without the WSGI
|
147 | handler itself by including the plugin in your `serverless.yml` configuration, without specifying
|
148 | the `custom.wsgi.app` setting. This will omit the WSGI handler from the package, but include
|
149 | any requirements specified in `requirements.txt`.
|
150 |
|
151 | If you don't want to use automatic requirement packaging you can set `custom.wsgi.packRequirements` to false:
|
152 |
|
153 | ```yaml
|
154 | custom:
|
155 | wsgi:
|
156 | app: api.app
|
157 | packRequirements: false
|
158 | ```
|
159 |
|
160 | For a more advanced approach to packaging requirements, consider using https://github.com/UnitedIncome/serverless-python-requirements.
|
161 |
|
162 | ### Python version
|
163 |
|
164 | Python is used for packaging requirements and serving the app when invoking `sls wsgi serve`. By
|
165 | default, the current runtime setting is expected to be the name of the Python binary in `PATH`,
|
166 | for instance `python3.6`. If this is not the name of your Python binary, override it using the
|
167 | `pythonBin` option:
|
168 |
|
169 | ```yaml
|
170 | custom:
|
171 | wsgi:
|
172 | app: api.app
|
173 | pythonBin: python3
|
174 | ```
|
175 |
|
176 | ### Local server
|
177 |
|
178 | For convenience, a `sls wsgi serve` command is provided to run your WSGI application
|
179 | locally. This command requires the `werkzeug` Python package to be installed,
|
180 | and acts as a simple wrapper for starting werkzeug's built-in HTTP server.
|
181 |
|
182 | By 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 |
|
191 | Configure 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 |
|
200 | When running locally, an environment variable named `IS_OFFLINE` will be set to `"True"`.
|
201 | So, if you want to know when the application is running locally, check `os.environ["IS_OFFLINE"]`.
|
202 |
|
203 | ### Explicit routes
|
204 |
|
205 | If you'd like to be explicit about which routes and HTTP methods should pass through to your
|
206 | application, see the following example:
|
207 |
|
208 | ```yaml
|
209 | service: example
|
210 |
|
211 | provider:
|
212 | name: aws
|
213 | runtime: python2.7
|
214 |
|
215 | plugins:
|
216 | - serverless-wsgi
|
217 |
|
218 | functions:
|
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 |
|
231 | custom:
|
232 | wsgi:
|
233 | app: api.app
|
234 | ```
|
235 |
|
236 | ### Custom domain names
|
237 |
|
238 | If you use custom domain names with API Gateway, you might have a base path that is
|
239 | at the beginning of your path, such as the stage (`/dev`, `/stage`, `/prod`). You
|
240 | can pass in an `API_GATEWAY_BASE_PATH` environment variable so your WSGI app can
|
241 | handle it correctly.
|
242 |
|
243 | The example below uses the [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager)
|
244 | plugin to handle custom domains in API Gateway:
|
245 |
|
246 | ```yaml
|
247 | service: example
|
248 |
|
249 | provider:
|
250 | name: aws
|
251 | runtime: python2.7
|
252 | environment:
|
253 | API_GATEWAY_BASE_PATH: ${self:custom.customDomain.basePath}
|
254 |
|
255 | plugins:
|
256 | - serverless-wsgi
|
257 | - serverless-domain-manager
|
258 |
|
259 | functions:
|
260 | api:
|
261 | handler: wsgi.handler
|
262 | events:
|
263 | - http: ANY /
|
264 | - http: ANY {proxy+}
|
265 |
|
266 | custom:
|
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 |
|
278 | In order to accept file uploads from HTML forms, make sure to add `multipart/form-data` to
|
279 | the list of content types with _Binary Support_ in your API Gateway API. The
|
280 | [serverless-apigw-binary](https://github.com/maciejtreder/serverless-apigw-binary)
|
281 | Serverless plugin can be used to automate this process.
|
282 |
|
283 | Keep in mind that, when building Serverless applications, uploading
|
284 | [directly to S3](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html)
|
285 | from the browser is usually the preferred approach.
|
286 |
|
287 | ### Raw context and event
|
288 |
|
289 | The raw context and event from AWS Lambda are both accessible through the WSGI
|
290 | request. The following example shows how to access them when using Flask:
|
291 |
|
292 | ```python
|
293 | from flask import Flask, request
|
294 | app = Flask(__name__)
|
295 |
|
296 |
|
297 | @app.route("/")
|
298 | def index():
|
299 | print(request.environ['context'])
|
300 | print(request.environ['event'])
|
301 | ```
|
302 |
|
303 | ### Text MIME types
|
304 |
|
305 | By default, all MIME types starting with `text/` and the following whitelist are sent
|
306 | through API Gateway in plain text. All other MIME types will have their response body
|
307 | base64 encoded (and the `isBase64Encoded` API Gateway flag set) in order to be
|
308 | delivered by API Gateway as binary data.
|
309 |
|
310 | This 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 |
|
317 | In order to add additional plain text MIME types to this whitelist, use the
|
318 | `textMimeTypes` configuration option:
|
319 |
|
320 | ```yaml
|
321 | custom:
|
322 | wsgi:
|
323 | app: api.app
|
324 | textMimeTypes:
|
325 | - application/custom+json
|
326 | - application/vnd.company+json
|
327 | ```
|
328 |
|
329 | ## Usage without Serverless
|
330 |
|
331 | The AWS API Gateway to WSGI mapping module is available on PyPI in the
|
332 | `serverless-wsgi` package.
|
333 |
|
334 | Use this package if you need to deploy Python Lambda functions to handle
|
335 | API Gateway events directly, without using the Serverless framework.
|
336 |
|
337 | ```
|
338 | pip install serverless-wsgi
|
339 | ```
|
340 |
|
341 | Initialize your WSGI application and in your Lambda event handler, call
|
342 | the request mapper:
|
343 |
|
344 | ```python
|
345 | import app # Replace with your actual application
|
346 | import 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 |
|
353 | def handle(event, context):
|
354 | return serverless_wsgi.handle_request(app.app, event, context)
|
355 | ```
|
356 |
|
357 | # Thanks
|
358 |
|
359 | Thanks to [Zappa](https://github.com/Miserlou/Zappa), which has been both the
|
360 | inspiration and source of several implementations that went into this project.
|
361 |
|
362 | Thanks to [chalice](https://github.com/awslabs/chalice) for the
|
363 | requirement packaging implementation.
|