Python Web API Endpoints using Jokes
A discussion on Web APIs. This is about creating a Web API (Jokes), and creating API that retains data as long as the Web Server is running. This is done using List and Dictionaries as the backend. Ultimately, this example could be extended by adding a database to the backend. However, for now, we are focussing on interaction of Frontend to Backend, this is called an Endpoint.
Model for API
We will begin our journey into APIs by creating and thinking about data. We have learned about Python Lists and dictionaries. In this data example, we are going to make "the best computer jokes ever ;)" and serve them over the Internet. The ultimate objective is to allow our viewers to provide a like or dislike on each of our jokes.
-
This code planning begins by coming up with some jokes and defining a data "model" to keep and manage the jokes.
- jokes_data contains a list of dictionary records containing joke and reactions:haha or boohoo - joke_list contains collection of jokes we will put into jokes_data
-
Next comes some functions to interact with our jokes
- def initJokes(): initializes jokes_data
- def getJokes(): returns the complete list of jokes
- def getJoke(): returns a single joke from our list
- ... many more function can be examined by reading comments below ...
import random
jokes_data = []
joke_list = [
"If you give someone a program... you will frustrate them for a day; if you teach them how to program... you will "
"frustrate them for a lifetime.",
"Q: Why did I divide sin by tan? A: Just cos.",
"UNIX is basically a simple operating system... but you have to be a genius to understand the simplicity.",
"Enter any 11-digit prime number to continue.",
"If at first you don't succeed; call it version 1.0.",
"Java programmers are some of the most materialistic people I know, very object-oriented",
"The oldest computer can be traced back to Adam and Eve. It was an apple but with extremely limited memory. Just "
"1 byte. And then everything crashed.",
"Q: Why did Wi-Fi and the computer get married? A: Because they had a connection",
"Bill Gates teaches a kindergarten class to count to ten. 1, 2, 3, 3.1, 95, 98, ME, 2000, XP, Vista, 7, 8, 10.",
"Q: What’s a aliens favorite computer key? A: the space bar!",
"There are 10 types of people in the world: those who understand binary, and those who don’t.",
"If it wasn't for C, we’d all be programming in BASI and OBOL.",
"Computers make very fast, very accurate mistakes.",
"Q: Why is it that programmers always confuse Halloween with Christmas? A: Because 31 OCT = 25 DEC.",
"Q: How many programmers does it take to change a light bulb? A: None. It’s a hardware problem.",
"The programmer got stuck in the shower because the instructions on the shampoo bottle said: Lather, Rinse, Repeat.",
"Q: What is the biggest lie in the entire universe? A: I have read and agree to the Terms and Conditions.",
'An SQL statement walks into a bar and sees two tables. It approaches, and asks may I join you?'
]
# Initialize jokes
def initJokes():
# setup jokes into a dictionary with id, joke, haha, boohoo
item_id = 0
for item in joke_list:
jokes_data.append({"id": item_id, "joke": item, "haha": 0, "boohoo": 0})
item_id += 1
# prime some haha responses
for i in range(200):
id = getRandomJoke()['id']
addJokeHaHa(id)
# prime some haha responses
for i in range(50):
id = getRandomJoke()['id']
addJokeBooHoo(id)
# Return all jokes from jokes_data
def getJokes():
return(jokes_data)
# Joke getter
def getJoke(id):
return(jokes_data[id])
# Return random joke from jokes_data
def getRandomJoke():
return(random.choice(jokes_data))
# Liked joke
def favoriteJoke():
best = 0
bestID = -1
for joke in getJokes():
if joke['haha'] > best:
best = joke['haha']
bestID = joke['id']
return jokes_data[bestID]
# Jeered joke
def jeeredJoke():
worst = 0
worstID = -1
for joke in getJokes():
if joke['boohoo'] > worst:
worst = joke['boohoo']
worstID = joke['id']
return jokes_data[worstID]
# Add to haha for requested id
def addJokeHaHa(id):
jokes_data[id]['haha'] = jokes_data[id]['haha'] + 1
return jokes_data[id]['haha']
# Add to boohoo for requested id
def addJokeBooHoo(id):
jokes_data[id]['boohoo'] = jokes_data[id]['boohoo'] + 1
return jokes_data[id]['boohoo']
# Pretty Print joke
def printJoke(joke):
print(joke['id'], joke['joke'], "\n", "haha:", joke['haha'], "\n", "boohoo:", joke['boohoo'], "\n")
# Number of jokes
def countJokes():
return len(jokes_data)
# Test Joke Model
if __name__ == "__main__":
initJokes() # initialize jokes
# Most likes and most jeered
best = favoriteJoke()
print("Most liked", best['haha'])
printJoke(best)
worst = jeeredJoke()
print("Most jeered", worst['boohoo'])
printJoke(worst)
# Random joke
print("Random joke")
printJoke(getRandomJoke())
# Count of Jokes
print("Jokes Count: " + str(countJokes()))
Backend Interface for Web API (Control)
An application programming interface (API) is the medium by which different systems of software interact. In our applications we have two big systems:1. Python Backend that stores data beyond a single Web page2. GH Pages/Fastpages Frontend that is responsible for presenting data
To communicate data between Frontend and Backend, this section Backend code provides and interface to the Frontend using a Web Service Endpoint. Examples of endpoints are listed below and can be typed within a browser, which will return JSON data:
- https://flask.nighthawkcodingsociety.com/api/jokes
- https://flask.nighthawkcodingsociety.com/api/jokes/2
- https://flask.nighthawkcodingsociety.com/api/jokes/random
As you can see, these Endpoints return JSON. They are NOT that readable by normal humans. However, they are very effective in passing requested data across the Internet. The Frontend code is responsible for formatting and presenting and interface that allows the typical computer user to interact with this data.
The next cell of code is Creating Endpoints that return JSON. This allows developers in the Frontend to interact with Backend data. API is a contract between the Frontend and Backend on how to share data.
FYI, there is NO output from this section .
from flask import Blueprint, jsonify # jsonify creates an endpoint response object
from flask_restful import Api, Resource # used for REST API building
import requests # used for testing
import random
# Blueprints allow this code to be procedurally abstracted from main.py, meaning code is not all in one place
app_api = Blueprint('api', __name__,
url_prefix='/api/jokes') # endpoint prefix avoid redundantly typing /api/jokes over and over
# API generator https://flask-restful.readthedocs.io/en/latest/api.html#id1
api = Api(app_api)
class JokesAPI:
# not implemented, this would be where we would allow creation of a new Joke
class _Create(Resource):
def post(self, joke):
pass
# getJokes()
class _Read(Resource):
def get(self):
return jsonify(getJokes())
# getJoke(id)
class _ReadID(Resource):
def get(self, id):
return jsonify(getJoke(id))
# getRandomJoke()
class _ReadRandom(Resource):
def get(self):
return jsonify(getRandomJoke())
# getRandomJoke()
class _ReadCount(Resource):
def get(self):
count = countJokes()
countMsg = {'count': count}
return jsonify(countMsg)
# put method: addJokeHaHa
class _UpdateLike(Resource):
def put(self, id):
addJokeHaHa(id)
return jsonify(getJoke(id))
# put method: addJokeBooHoo
class _UpdateJeer(Resource):
def put(self, id):
addJokeBooHoo(id)
return jsonify(getJoke(id))
# building RESTapi interfaces, these routes are added to Web Server http://<server</api/jokes
api.add_resource(_Create, '/create/<string:joke>')
api.add_resource(_Read, '/') # default, which returns all jokes
api.add_resource(_ReadID, '/<int:id>')
api.add_resource(_ReadRandom, '/random')
api.add_resource(_ReadCount, '/count')
api.add_resource(_UpdateLike, '/like/<int:id>/')
api.add_resource(_UpdateJeer, '/jeer/<int:id>/')
Frontend (View Simulation) and Hacks
This python codes tests endpoints on a server. This can be handy for development and testing when making modifications to the Jokes Web APIs. This code works off of the server endpoint/url, not from code cells above it in this notebook.
To work with this code and make observation for learning...
- Run a local server from flask_portfolio project and the change server variable to be local
- Observe the requests endpoints and the output, see if you can observe what is happening/changing on put requests
- The "requests" are captured into a List, the List is used in the for loop to extract from RESTful API format.
- Try running this with Debugging and observe what data is being created at each step (Required)
- Try to format this data in Python print statements to be more readable (Required)
- Start and stop local server and observe errors
from flask import Blueprint, jsonify # jsonify creates an endpoint response object
from flask_restful import Api, Resource # used for REST API building
import requests # used for testing
import random
# Blueprints allow this code to be procedurally abstracted from main.py, meaning code is not all in one place
app_api = Blueprint('api', __name__,
url_prefix='/api/jokes') # endpoint prefix avoid redundantly typing /api/jokes over and over
# API generator https://flask-restful.readthedocs.io/en/latest/api.html#id1
api = Api(app_api)
class JokesAPI:
# not implemented, this would be where we would allow creation of a new Joke
class _Create(Resource):
def post(self, joke):
pass
# getJokes()
class _Read(Resource):
def get(self):
return jsonify(getJokes())
# getJoke(id)
class _ReadID(Resource):
def get(self, id):
return jsonify(getJoke(id))
# getRandomJoke()
class _ReadRandom(Resource):
def get(self):
return jsonify(getRandomJoke())
# getRandomJoke()
class _ReadCount(Resource):
def get(self):
count = countJokes()
countMsg = {'count': count}
return jsonify(countMsg)
# put method: addJokeHaHa
class _UpdateLike(Resource):
def put(self, id):
addJokeHaHa(id)
return jsonify(getJoke(id))
# put method: addJokeBooHoo
class _UpdateJeer(Resource):
def put(self, id):
addJokeBooHoo(id)
return jsonify(getJoke(id))
# building RESTapi interfaces, these routes are added to Web Server http://<server</api/jokes
api.add_resource(_Create, '/create/<string:joke>')
api.add_resource(_Read, '/') # default, which returns all jokes
api.add_resource(_ReadID, '/<int:id>')
api.add_resource(_ReadRandom, '/random')
api.add_resource(_ReadCount, '/count')
api.add_resource(_UpdateLike, '/like/<int:id>/')
api.add_resource(_UpdateJeer, '/jeer/<int:id>/')
# Pick the server you want to test comment in/comment out
# server = "http://127.0.0.1:5000/" # run local
server = 'https://flask.nighthawkcodingsociety.com/' # run from web server
url = server + "api/jokes/"
responses = [] # responses list
# Get the count of jokes on server
count_response = requests.get(url+"count")
count_json = count_response.json()
count = count_json['count']
# Update likes/dislikes test sequence
num = str(random.randint(0, count-1)) # test a random record
responses.append(
requests.get(url+num) # Get/read joke by id
)
responses.append(
requests.put(url+"like/"+num) # Put/add to like count
)
responses.append(
requests.put(url+"jeer/"+num) # Put/add to jeer count
)
# Get a random joke
responses.append(
requests.get(url+"random") # Get/read a random joke
)
# Cycle through and print responses
for response in responses:
print(response)
try:
print(response.json())
except:
print("data error")