Talk given to full stack cohort at Galvanize/Boulder on December 20, 2016 about building APIs with Python / Flask.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

part1_flaskwebapi.md 8.3 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # Your first Python REST API server
  2. In this tutorial you will learn how to make a simple HTTP/REST API in Python 2.7 (or 3.5). When you're done you will know about:
  3. * how to install/verify your Python installation
  4. * how to do a basic package installation with `pip`, [Python's package manager](https://en.wikipedia.org/wiki/Pip_(Python))
  5. * a few common Python data structures (notably Python objects, dictionaries, tuples, lists)
  6. * how to use the [`json` module](https://docs.python.org/2/library/json.html) in Python
  7. * how to install the [Flask microframework](http://flask.pocoo.org/) for Python
  8. * how to build and run a basic API in Flask
  9. #### ADDITIONAL RESOURCES
  10. |What | Where | Why |
  11. |:-------------------:|:----------------------:|:------------------|
  12. |A nice primer on Python decorators | [https://realpython.com/blog/python/primer-on-python-decorators/](https://realpython.com/blog/python/primer-on-python-decorators/) | You might want to get a little under the hood on how the routes (and other) decorators work. |
  13. |Resource modeling - the RESTful way | [https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling](https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling) | Can't get enough of REST? |
  14. |A thorough intro to RESTful APIs | [https://codeplanet.io/principles-good-restful-api-design/](https://codeplanet.io/principles-good-restful-api-design/) | This really nails the full spectrum of concerns in a nice way, but get some coffee before you start. |
  15. ## STEP 1: Environment check
  16. You will minimally need Python 2.7.1x for this tutorial. Most if not all of the code will work in 3.5.x as well, so feel free to use that moving forward.
  17. ### CHECK YOUR VERSION OF PYTHON
  18. 1. open terminal and type `python -V`
  19. ```
  20. $ python -V
  21. Python 2.7.12 :: Anaconda 4.2.0 (64-bit)
  22. ```
  23. 2. execute `pip install flask`
  24. * if you need to know more about `pip`, please [read more on it](http://www.pythonforbeginners.com/basics/python-pip-usage/).
  25. 3. check to see if you have the `json` library installed
  26. ```
  27. $ python
  28. Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32
  29. Type "help", "copyright", "credits" or "license" for more information.
  30. Anaconda is brought to you by Continuum Analytics.
  31. Please check out: http://continuum.io/thanks and https://anaconda.org
  32. >>> import json
  33. >>> exit()
  34. ```
  35. * **If this returns an error please execute `pip install json`.**
  36. ## Step 2: Build Your First Example App
  37. We're now going to build our first functional app. It isn't going to do anything yet -- we just want to make sure that we can see that everything is looking good in our environment.
  38. 1. Open your favorite editor and **create the file `app.py`**.
  39. 2. Write the following code in your file and **save it**:
  40. ```python
  41. # -*- coding: utf-8 -*-
  42. from flask import Flask
  43. app = Flask(__name__)
  44. if __name__ == "__main__":
  45. app.run()
  46. ```
  47. 3. Go to your command line and type:
  48. ```bash
  49. $ python app.py
  50. ```
  51. You should see the following:
  52. ```
  53. * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
  54. ```
  55. 4. Hooray! You have your first Flask app, test it by opening your browser and pointing it to [localhost:5000](http://localhost:5000) (**except if** you don't see that last line `* Running ...`, then something is really wrong!)
  56. 1. **NOTE:** You haven't implemented any endpoints/routes, so you should get HTTP/404 when you hit [localhost:5000](http://localhost:5000). This is the correct behavior.
  57. 2. The default port is `5000`, which can be changed later.
  58. ## Step 3: Let's dissect what is going on ...
  59. line by line now ...
  60. ```python
  61. from flask import Flask
  62. ```
  63. This is a required import to get any basic Flask app running ... don't forget it, and don't linger on it.
  64. ```python
  65. app = Flask(__name__)
  66. ```
  67. This variable is necessary to initialize the Flask app -- it is the [Application Object](http://flask.pocoo.org/docs/0.11/api/#application-object). We're giving the name `__name__` because we are running this application as a standalone in a single file (`app.py`). You can read more about how Python handles application and module name spaces, but if a file you create is run from the command line, its application `__name__` attribute will be set to `__main__` (unless overridden by the developer, but that is not a typical move in Python, so don't go playing footsy with it until you know why you might want to do so!)
  68. ```python
  69. if __name__ == "__main__":
  70. app.run()
  71. ```
  72. This last bit of code tells Python that if this file is run from the command line (as opposed to being imported as a module), then execute the Flask application method [`run()`](http://flask.pocoo.org/docs/0.11/api/#flask.Flask.run), which starts the HTTP server (read [the docs carefully if you're deploying](http://flask.pocoo.org/docs/0.11/deploying/#deployment) in a production environment).
  73. ## Step 3a: Let's make a few quick changes
  74. ### Change the port
  75. Let's change the port to something like `5200` using the `port` parameter of the `Flask.run()` method like this:
  76. ```python
  77. if __name__ == "__main__":
  78. app.run(port=5200)
  79. ```
  80. ### Change the host ipaddress
  81. The default parameter for the `run()` method is indeed the IP of your running server (which defaults to `127.0.0.1`). You can set it to your fixed IP or use `0.0.0.0` to make it externally available.
  82. Let's put your fixed/assigned/DHCP address in first (remember this _may_ change depending on what network you are on):
  83. * Get the IP of your machine with `ifconfig` (on \*X) or `ipconfig` on (Win\*):
  84. ```bash
  85. $ ifconfig
  86. ```
  87. and make the change accordingly:
  88. ```python
  89. if __name__ == "__main__":
  90. app.run(_your_ip_address_, port=5200)
  91. ```
  92. ### Set DEBUG mode ON
  93. Finally, let's make sure we run in debug mode so if anything crazy happens, we can find out what ... using the `debug` parameter makes that a snap ...
  94. And now update your `app.run()` accordingly:
  95. ```python
  96. app.run(_your_ip_address_, port=5200, debug=True)
  97. ```
  98. With this, others will be able to see your server (on the same network) and you will be able to make changes to your code and **dynamically update your code without stopping and starting the server**.
  99. ## Step 4: Let's make a route
  100. In our final step, we're going to implement something interesting (well sort of anyway). Let's say we have an application which will respond to `/status` by returning the following json:
  101. ### INGREDIENT A: Some JSON response spec
  102. ```json
  103. {
  104. "appname": "My awesome app",
  105. "version": "0.1",
  106. "creator": "BigBird007"
  107. }
  108. ```
  109. Because Python dictionaries are *very* similar to JSON objects, we can specify the above JSON like this in Python:
  110. ```python
  111. data = {
  112. "appname": "My awesome app",
  113. "version": "0.1",
  114. "creator": "BigBird007"
  115. }
  116. ```
  117. To convert this Python dictionary to JSON so we can return it over the wire, we use the `json` library. The two methods you should just remember are `json.dumps(a_python_object)` and `json.loads(a_json_string)` ... these two methods allow you to go back and forth from Python to a JSON string (`dumps`) and from a JSON object (as a string) back to a Python object (`loads`).
  118. ### INGREDIENT B: A `/status` route and handler
  119. The syntax for specifying a route is
  120. ```python
  121. @app.route("your/route/here")
  122. def the_handler_for_the_route():
  123. # your awesome implementation here
  124. # return some cool JSON usually ...
  125. return
  126. ```
  127. This pattern will be used over and over in your code, study it and remember it. To learn how to use this pattern to specify things like your `GET` or `POST` handlers, read the docs further here: [http://flask.pocoo.org/docs/0.11/api/#flask.Flask.route](http://flask.pocoo.org/docs/0.11/api/#flask.Flask.route) and here: [http://flask.pocoo.org/docs/0.11/api/#url-route-registrations](http://flask.pocoo.org/docs/0.11/api/#url-route-registrations). Everything you'd want to know will be there (including handling variable parts of your route, defaults, method handling, etc.).
  128. Let's finish off our `/status` route ... make sure you place the route somewhere before the `if __name__ ...` line of code:
  129. ```python
  130. @app.route("/status")
  131. def status_endpoint():
  132. data = {
  133. "appname": "My awesome app",
  134. "version": "0.1",
  135. "creator": "BigBird007"
  136. }
  137. return json.dumps(data)
  138. ```
  139. Your final code should look like something like the sample file provided in [app.py](./app.py).