Fortune #
In this lab we will create a server for a fortune teller. We will delve deeper into design constraints and functionalities of an API.

[0] Set Up #
๐ป First, clone the repository
in your unit03_networking
folder. Be sure to change yourgithubusername
to your actual Github username.
cd ~/desktop/making_with_code/unit03_networking/
git clone https://github.com/the-isf-academy/lab_fortune_yourgithubusername
cd lab_fortuneyourgithubusername
poetry install
poetry shell
[1] Add new fortunes #
๐ Look at the definition of the SQL database table in fortunes.sql
Notice the data types of each column
CREATE TABLE IF NOT EXISTS fortunes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
last_updated DATETIME DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%S', 'now', '+8 hours')),
statement TEXT NOT NULL,
is_happy BOOLEAN NOT NULL,
num_updates INTEGER DEFAULT 0,
archive BOOLEAN DEFAULT 0
);
๐ป Open the database file to add a few fortunes: open database.db
.
๐ป Add 3 new fortunes using the DB Browser, then save with โ+s
. It’s important to have a variety of testing data to ensure the api works as expected.
- Notice how
booleans
are represented as a0
or a1
[2] Running the Fortune Server #
๐ป Start your local server.python api.py
๐ป Test the server using HTTPie
: 127.0.0.1:5000/fortune
๐ Look at the JSON response in the help
key and make requests to all of the endpoints.
{
"help": {
"GET /all": {
"description": "returns all fortunes",
"payload": ""
},
"POST /new": {
"description": "adds new fortune to database",
"payload": "statement: string, is_happy: boolean"
}
},
"overview": "This is the fortune server."
}
[3] API #
When building an API, it is important to consider how users will interact with it. It is the responsiblity of the developer to design an API with user interaction in mind.
We want users to be able to:
- filter by the
is_happy
column - add on to each other’s fortunes
- delete fortunes if user has the admin key
๐ป Open the api.py
file. This server has 2 endpoints:
/new
/all
For each each feature, you will write a helper function to run the SQL commands and then write the endpoint.*
๐ป It is up to you to write the add the following features:
/is_happy
- filters riddles byis_happy
payload/update_statement
- add to the end of an exisitng fortune/delete
- delete a fortune with a specificid
if thekey
is valid
/is_happy
#
๐ป In helpers.py
write the get_all_ishappy()
function. It should use SQL to fetch all riddles filtered on the is_happy
parameter.
- parameter:
is_happy: boolean
โ CHECKPOINT:๐ป Test the function in bottom of
helpers.py
Be sure to test with
False
andTrue
. Look atdatabase.db
to confirm it is working.all_fortunes = get_all_ishappy(False) for fortune in all_fortunes: print(fortune['id'])
๐ป In api.py
write the /is_happy
endpoint. You should get_all_ishappy()
function.
- HTTP method:
GET
- Params/Payload:
is_happy
โ CHECKPOINT:๐ป Test the endpoint in the
HTTPie desktop app
http://127.0.0.1:5000/fortune/is_happy is_happy=True
โ๏ธ It should return
json
like:{ "fortunes": [ { "fortune": "you will win money everyday", "id": 1, "is_happy": true, "last_updated": "2025-09-15 04:06:14", "num_updates": 1 },
/update
#
๐ป In helpers.py
write the update_fortune()
function.
- parameter:
id: int, update_string: string
- It should
- add
update_string
to the end of the existingstaetment
- increase
num_updates
by 1 - updated
last_updated
to the current datetime - return the updated fortune
- add
โ CHECKPOINT:๐ป Test the function in bottom of
helpers.py
Refresh the
database.db
file to confirm the fortune is properly updated.updated_fortune = update_fortune(1, 'money') print(updated_fortune['statement'], updated_fortune['num_updates'], updated_fortune['last_updated'] )
๐ป In api.py
write the /update
endpoint.
- HTTP method:
PUT
- Payload/args:
id:integer
,update_text:string
- if the fortune exists
- call the
update_fortune()
function
- call the
- else
- a helpful error message communicating the fortune does not exist
โ CHECKPOINT:๐ป Test the endpoint in the
HTTPie desktop app
http://127.0.0.1:5000/fortune/update id=6 update_text=non-stop
โ๏ธ It should return
json
like:{ "message": "Fortune updated successfully", "question": { "fortune": "tomorrow will rain non-stop", "id": 5, "is_happy": false, "last_updated": "2025-09-16 14:47:33", "num_updates": 2 } }
/search
#
๐ป In helpers.py
write the serach()
function.
- parameter:
keyword: string
- return the fortunes from the database that contains the keyword
โ CHECKPOINT:๐ป Test the function in bottom of
helpers.py
all_fortunes = search("win") for fortune in all_fortunes: print(fortune['statement'])
๐ป In apy.py
write the /search
endpoint.
- HTTP method:
get
- Payload/args:
keyword
- It should return all fortunes that contain the keyword.
- If no fortunes exist, provide a helpful error message
๐ค Consider:
- Which existing endpoint is similar?
- How should you format the json that you return?
- What should you return to the user if no fortunes match their search term?
โ CHECKPOINT:๐ป Test the endpoint in the
HTTPie desktop app
http://127.0.0.1:5000/fortune/search keyword="surprise"
โ๏ธ It should return
json
like:{ "fortunes": [ { "id": 1, "statement": "A surprise pizza is coming", "num_updates": 1, "is_happy": true, "last_updated": "2025-09-15 14:06:14", }, { "id": 6, "statement": "A surprise typhoon day is in your future", "num_updates": 1, "is_happy": true, "last_updated": "2025-09-13 15:20:13", } ] }
API documentation
#
When designing an API, it is important to write helpful documentation so others can use it properly.
๐ป Look at the current help
section by making a GET
request to /
endpoint in the HTTPie desktop app
{
"help": {
"GET /all": {
"description": "returns all fortunes",
"payload": ""
},
"GET /new": {
"description": "adds new fortune to database",
"payload": "statement: string, is_happy: boolean"
}
},
"overview": "This is the fortune server."
}
๐ป Add /is_happy
, /update
, and /search
to the "help"
section of the JSON.
โ CHECKPOINT:๐ป Test the endpoint in the
HTTPie desktop app
โ๏ธ It should return
json
like:{ "help": { "GET /all": { "description": "returns all fortunes", "payload": "" }, "POST /new": { "description": "adds new fortune to database", "payload": "statement: string, is_happy: boolean" }, "GET /is_happy": { "description": "returns all fortunes filtered by is_happy", "payload": "is_happy: boolean" }, "PUT /update": { "description": "adds on to an existing fortune statement", "payload": "id: integer, update_text: string" }, "GET /search": { "description": "returns all fortunes filtered by a keyword in the statement", "payload": "keyword: string" } }, "overview": "This is the fortune server." }
[4] Deliverables #
โกโจOnce you’ve successfully completed the worksheet be sure to fill out this Google form.
๐ป Push your work to Github:
- git status
- git add -A
- git status
- git commit -m “describe your code and your process here”
be sure to customize this message, do not copy and paste this line
- git push
[5] Extensions #
Error Handling #
๐ป Try to break your server.
๐ป Now, add in proper error handling so the server never crashes, but instead provides helpful error messages.
Archive #
Currently there is no feature to delete a fortune. It can be risky to permanently delete an item from the database, so instead let’s utilize the archive column.
๐ป Write a method toggle_archive()
that toggles the archive
field to True
or False
, depending on its current state.
๐ป Write a new route /change_archive
to change the archive
field of a fortune with a given id
.
๐ป Change your exisitng routes (/all
, /search
, and /all/is_happy
) to only return fortunes with archive
set to True
.