2.3 just serve and just stop

Read the tutorial. Build it yourself is the other way to read this part.

This walk goes through rahuldave/cheesecracker on GitHub. This part starts from the code at acee14f7dff4, partway through step-02. The code before this part

To look at this code on your machine, run these commands. The first one makes a clone, if this folder has none:

[ -d cheesecracker ] || git clone https://github.com/rahuldave/cheesecracker
cd cheesecracker
git fetch --quiet --tags --force origin
git switch --detach acee14f7dff4
just setup

The whole tutorial then adds FastAPI, a library for web APIs, and writes the app with it. It also adds uvicorn, the server that runs the app. Two recipes start the server and stop it.

2.3 just serve and just stop, the server on this machine

The app does nothing until a server runs it. The recipe serve starts uvicorn at port 9000, in the background, and waits until the server answers. The recipe stop stops it. A server is a fixture of the commands that need it. We start it before them, and stop it after them.

± See the change to justfile

± See the change to .gitignore

The recipe serve runs uvicorn api.service:app with &, so the server runs in the background. --app-dir tells uvicorn where the package api is, and --reload restarts the server when a file of the code changes. The recipe keeps the number of the process in .serve.pid, and asks /health once a second until the server answers. stop stops that process, and waits until the port is free. .gitignore now lists .serve.log and .serve.pid, which git never takes.

just serve

The recipe prints serve: http://localhost:9000, its log in .serve.log. Ask the server for its health.

curl -s localhost:9000/health

curl sends an HTTP request from the terminal, and prints the answer. macOS and most Linux systems have it. The server answers {"status":"healthy"}. Open http://localhost:9000/docs in a browser to see the pages that FastAPI makes for each route.

just stop

The recipe prints stop: stopped. Ask for the health again.

curl -s localhost:9000/health

curl prints nothing, and fails with the code 7, because no server answers.

What changed

  • .gitignore: the log of the server and the number of its process. 4 lines added. The file
    • look at .serve.pid
  • justfile: the recipes serve and stop. 26 lines added. The file
    • look at uv run uvicorn api.service:app --app-dir src/api-service --host 0.0.0.0 --port 9000 --reload > .serve.log 2>&1 &

The diff of .gitignore
@@ -9,6 +9,10 @@ __pycache__/
 htmlcov/
 .ruff_cache/
 
+# What just serve writes: the log of the server, and its process id
+.serve.log
+.serve.pid
+
 # Secrets: never commit them
 secrets/
 .env

The diff of justfile
@@ -14,3 +14,29 @@ setup:
 # Run the tests. Extra arguments go to pytest: just test tests/unit
 test *args:
     uv run pytest "$@"
+
+# Start the server on this machine, in the background, at http://localhost:9000. It restarts when a file of the code changes. just stop stops it
+serve:
+    #!/usr/bin/env sh
+    if curl -sf localhost:9000/health > /dev/null; then echo "serve: a server already answers at http://localhost:9000" >&2; exit 1; fi
+    uv run uvicorn api.service:app --app-dir src/api-service --host 0.0.0.0 --port 9000 --reload > .serve.log 2>&1 &
+    echo $! > .serve.pid
+    for i in $(seq 1 30); do
+        if curl -sf localhost:9000/health > /dev/null; then echo "serve: http://localhost:9000, its log in .serve.log"; exit 0; fi
+        sleep 1
+    done
+    echo "serve: no answer after 30 seconds; read .serve.log" >&2
+    exit 1
+
+# Stop the server of just serve
+stop:
+    #!/usr/bin/env sh
+    if [ ! -f .serve.pid ]; then echo "stop: no server of just serve runs here" >&2; exit 1; fi
+    kill "$(cat .serve.pid)" && rm .serve.pid
+    # Wait until the server no longer answers, so that the port is free for the next one
+    for i in $(seq 1 30); do
+        if ! curl -s localhost:9000/health > /dev/null; then echo "stop: stopped"; exit 0; fi
+        sleep 1
+    done
+    echo "stop: the server still answers after 30 seconds" >&2
+    exit 1

This change on GitHub

This part ends at the code at step-02. The code after this part