Skip to content

Verify and clean up

  • Lesson 21
  • 45 min
  • Module 7: Deploy and verify

What you will be able to do

Prove that the deployed API works, with the same checks that you run on your computer. Send a valid request, an invalid one, and a dependency that fails in a controlled way. Then remove everything that you made, so nothing keeps running or waiting to be misused.

Before you startWhat to know and to have readyAbout 45 minutes

What you need to know

What you need on your computer

The steps follow FastAPI Cloud’s documentation and the help of fastapi-cloud-cli 0.26. The Postman CLI steps were tested with version 1.69.0.

A smoke check is a short set of requests that you send right after a deployment. It shows that the main paths work: if there is smoke, there is fire. It does not test every detail. That is pytest’s job, before the deployment. It answers one question: is this deployment, on this server, with these settings, working?

Your Postman collection is a good smoke check: it sends a valid ticket, the invalid ones, a request without a key, and checks /health. You only need a Postman environment that points to the server.

In Postman, Amira opens the Deployed environment from Module 5:

  1. She sets baseUrl to her app’s address, for example https://ticket-api.fastapicloud.dev (no / at the end).
  2. She pastes the deployed key into the local value of apiKey. The shared value stays empty.
  3. She chooses Deployed in the environment selector, and sends Check that the service runs: 200.
  4. She exports the environment as postman/deployed.postman_environment.json, and checks that apiKey is empty in the file. The address is not a secret, so it may stay.

From the terminal, she gives the key to the command without typing it into the command line:

Terminal window
read -s TICKET_API_KEY
curl -s https://ticket-api.fastapicloud.dev/health
postman collection run postman/ticket-api.postman_collection.json -e postman/deployed.postman_environment.json --env-var "apiKey=$TICKET_API_KEY"

read -s waits for her to paste the key, and does not show it (-s is “silent”). Read-Host does the same in PowerShell, except that it shows what you paste. Neither one saves the key in the terminal history, because the key is typed as input, not as part of a command.

The curl to /health first wakes the app up. On the Hobby plan, an app that had no requests for a while is stopped (scale to zero). The first request starts it again, which takes a few seconds. Without this, the collection’s test “answers in under 500 ms” can fail on the first request, for a reason that is not a bug.

The run ends like the local run: 8 requests, all tests passed, exit code 0. The same collection, the same tests, a different environment.

The module’s last check: the deployed API must handle a broken dependency as well as the local one. Amira simulates it with the setting from Module 6. Setting a variable redeploys the app with it:

Terminal
fastapi cloud env set CLASSIFIER_MODE unavailable

When the new deployment is ready, she calls the two health endpoints and sends a ticket, with her key:

Request Expected
GET /health 200: the process runs
GET /ready 503 {"error":{"code":"classifier_unavailable",...}}
POST /v1/classify with the key 503, the same code, with a request ID, and no internal details

The collection run now fails on Classify a support ticket, with exit code 1, as it must. Then she removes the setting, which redeploys the normal classifier, and runs the collection again: all pass.

Terminal
fastapi cloud env delete CLASSIFIER_MODE

The app writes the same log lines as on your computer, and the platform keeps them (for 1 day on the Hobby plan). To see the recent ones:

Terminal
fastapi cloud logs --no-follow --since 10m

Without --no-follow, the command keeps showing new lines as they arrive, until you press Ctrl+C. Each request has its line from your middleware, for example:

A line from your app
INFO ticket_api: POST /v1/classify 503 2ms id=5d0b7e1c94aa

To follow up a problem, take the request_id from the error body, and search the logs for it. On macOS and Linux: fastapi cloud logs --no-follow --since 1h | grep 5d0b7e1c94aa. In PowerShell, use Select-String 5d0b7e1c94aa instead of grep.

A deployed API that nobody can find is not finished. Add a short section to the project’s README:

README.md (add)
## Deployed practice service
- Address: https://ticket-api.fastapicloud.dev (deleted after the course)
- Smoke check:
```sh
postman collection run postman/ticket-api.postman_collection.json -e postman/deployed.postman_environment.json --env-var "apiKey=<the key>"
```
- The API key is not in this project. Ask the owner, or set a new one with
`fastapi cloud env set API_KEY --secret --value-stdin`.

When you have your evidence, remove the practice service. The Hobby plan does not charge you, but a service that nobody watches is still a risk. A key that leaks later still works, and the service keeps using one of your 3 apps.

  1. Open FastAPI Cloud’s dashboard, select the app, and find the option to delete it in the app’s settings. Confirm.
  2. In the project folder, remove the link to the deleted app: fastapi cloud unlink. This deletes .fastapicloud/cloud.json.
  3. In Postman, delete the local value of apiKey in the Deployed environment.
  4. Check that the service is gone: curl -i https://ticket-api.fastapicloud.dev/health no longer returns 200.
  5. In the README, mark the address as deleted.

The key itself no longer works, because the service that checked it is gone. That is the cleanest way to make sure that a practice key can never be misused.

  1. Set up the Deployed environment in Postman, and export it into postman/, without the key.
  2. Wake the app with /health, then run the collection from the terminal with the Deployed environment. Every test passes.
  3. Set CLASSIFIER_MODE to unavailable. Check /health (200), /ready (503), and a classify request with the key (503). Run the collection: it fails on the classify request, with exit code 1.
  4. Find that classify request in the logs by its request ID.
  5. Delete CLASSIFIER_MODE, and run the collection again. Every test passes.
  6. Add the deployed section to the README.
  7. Delete the app, unlink the folder, and remove the key from Postman. Check that the address no longer answers.

Your turn: a smoke check that you can repeat

Section titled “Your turn: a smoke check that you can repeat”

Write a short script, smoke-check.sh (macOS and Linux) or smoke-check.ps1 (Windows), that does steps 2 and 3 of a deployment check in order. It wakes the app with /health, then runs the collection with the Deployed environment. It ends with the collection run’s exit code. The key comes from an environment variable that the person sets before running it, never from the file.

What should the script do if TICKET_API_KEY is not set?

Show one answer for macOS and Linux
smoke-check.sh
#!/bin/sh
# Smoke check for the deployed ticket API. Set TICKET_API_KEY first.
set -e
if [ -z "$TICKET_API_KEY" ]; then
echo "Set TICKET_API_KEY first (read -s TICKET_API_KEY)." >&2
exit 2
fi
BASE_URL="https://ticket-api.fastapicloud.dev"
curl -s -o /dev/null "$BASE_URL/health"
postman collection run postman/ticket-api.postman_collection.json \
-e postman/deployed.postman_environment.json \
--env-var "apiKey=$TICKET_API_KEY"

Without the key, the script stops at once with a clear message and exit code 2 (a wrong command). It does not run the collection and report seven confusing 401 failures. Run it with sh smoke-check.sh. set -e stops the script at the first command that fails. The last command’s exit code becomes the script’s exit code, so a failed check fails the script. The course on CI/CD and safe releases runs a check like this automatically after every deployment.

Common mistake

Only the response-time test fails, and only on the first run

Symptom: the deployed collection run fails one test, answers in under 500 ms, on the first request. Run it again a minute later, and everything passes.

Cause: scale to zero. The app was stopped because nobody used it, and the first request had to wait for it to start. The API is not slow. It was asleep.

Fix:

  1. Wake the app with a request to /health before the run, as in the worked example.
  2. Keep a strict time limit for the warm app, because it finds real slowness.
  3. If you need the first request to be fast too, a paid plan can keep at least one copy of the app running. That is a cost decision, not a code change.

Check your understanding

Why does the smoke check use the Postman collection instead of running pytest against the server?

Show the answer

pytest’s tests use TestClient, which runs the application in memory on your computer. They test the code, not the deployment, and they cannot reach a server. The collection sends real HTTPS requests to the real address, through the platform, with the real settings and secret. That is what can go wrong in a deployment, so that is what a smoke check must test. Both are needed: pytest before the deployment, the collection after it.

Check your understanding

Module check: list the three checks that prove the deployment, with the expected status code of each, and say where each one comes from in the course.

Show the answer

A successful request: POST /v1/classify with the key and a valid ticket gives 200 with the classification (Modules 2 and 6). An invalid request: an empty subject gives 422 with invalid_request and "fields": ["subject"], and a request without a key gives 401 (Modules 3 and 6). A controlled dependency failure: with CLASSIFIER_MODE=unavailable, /ready and classify give 503 with classifier_unavailable and a request ID, while /health stays 200 (Modules 6 and 7). The Postman collection with the Deployed environment shows the first two. The third is shown by setting the variable on the server.

Recap

  • A smoke check after each deployment answers: does this deployment work? The Postman collection, with a Deployed environment, is that check.
  • Give the key to the CLI from input (read -s, Read-Host), never in a command or a file.
  • Wake a scaled-to-zero app before a timed test.
  • Show a controlled failure on the server with a setting, and remove the setting afterwards.
  • Find a request in the platform’s logs by its request ID.
  • Document the address. Delete practice services when you are done, and remove their keys.

You built an API from the first request to a deployed, documented, tested and protected service. Next come the two case studies: complete projects on real data that use the whole course. Complete at least one of them for the certificate. Then the final assignment asks you to change your API as a real team would: a new endpoint, from contract to deployment.

Next: Serve a house-price model