Publish safely
- Lesson 20
- 45 min
- Module 7: Deploy and verify
Lesson 20 of 21 · Build and Deploy Your First APIPublish safely
1. Understand the web request
2. Build a small FastAPI service
3. Design a usable contract
4. Document the API with OpenAPI and Swagger UI
5. Explore and test with Postman
6. Test and harden the service
7. Deploy and verify
What you will be able to do
Put the API on the internet, with an HTTPS address that any program can call, and with its key protecting it from the first second. Nothing secret is in the code, in a file, or in your terminal history.
Before you startWhat to know and to have ready
What you need to know
- Production mode and
pyproject.toml, from Hosting bridge: Development mode and production mode and Hosting bridge: Worked example: describe the project for a platform. - Secrets, from Configuration and secrets: Secrets.
ClassifierUnavailableand its503response, from Automated endpoint tests: The case that only a test can reach: a failing dependency.
What you need on your computer
pyproject.toml, and the API runs in production mode, from Hosting bridge: Guided practice: ready for hosting..gitignorewith.venv/,__pycache__/,.envand.fastapicloud/, from Environment and first endpoint: Files that stay on your computer. You checked it in step 7 of that guided practice.- The fixture
make_clientintests/conftest.py, from Configuration and logging: Tests with their own settings. The new test uses it. - The collection in Postman and in
postman, with the request Check that the service runs, from Tests and repeatable runs: Save the collection in the project. - A free FastAPI Cloud account. The Hobby plan costs nothing, needs no payment card, and allows 3 apps. FastAPI Cloud is made by the team behind FastAPI, and it is in beta: its commands and screens can change. If you do not want an account, use the local deployment simulation from Hosting bridge: Worked example: production mode on your computer as your evidence, and read this lesson.
- If your project is missing or broken, copy the snapshot
end-of-m06, the project at the end of Module 6, as in Use a snapshot. Then do Hosting bridge: Guided practice: ready for hosting again. To use the collection in the Postman app, import the files inpostman/(Import).
The steps follow FastAPI Cloud’s documentation and the help of fastapi-cloud-cli 0.26.
A disposable practice service
Section titled “A disposable practice service”This deployment is for practice. Treat it as disposable: you make it, check it, and delete it at the end of the next lesson. Three rules follow from that:
- It gets its own API key, made for it, never one that you use elsewhere.
- It has no real data. The classifier is the keyword mock.
- You know exactly what you made, so you can remove all of it.
Liveness and readiness
Section titled “Liveness and readiness”A platform must know two different things about your app:
| Check | Question | Endpoint | If it fails |
|---|---|---|---|
| Liveness | Is the process running at all? | /health |
Restart the app. |
| Readiness | Can it do its job now? | /ready |
Do not send it requests yet, but do not restart it. |
The difference matters when the classifier is down. The process is fine, so restarting it does not help, and a restart loop makes things worse. But a client should know that classification does not work now. /health stays 200, and /ready gives 503.
/ready checks the real work: it asks the classifier for one prediction. Add it to main.py, next to /health:
@app.get( "/ready", tags=["health"], summary="Check that the service can classify tickets", responses={503: {"model": ErrorResponse, "description": "Not ready."}}, ) def ready() -> Readiness: app.state.classifier.predict("readiness check") return Readiness(status="ready")class Readiness(BaseModel): status: Literal["ready"]Import ErrorResponse and Readiness in main.py. If the classifier raises ClassifierUnavailable, the handler from Module 6 answers 503, in the standard shape. Add a test, then update the reviewed contract:
def test_ready_and_not_ready(make_client): assert make_client().get("/ready").status_code == 200 not_ready = make_client(classifier_mode="unavailable").get("/ready") assert not_ready.status_code == 503Step 1: sign in and create the app
Section titled “Step 1: sign in and create the app”-
Make sure that your virtual environment is active. The
fastapicommand that deploys comes withfastapi[standard]. -
Sign in:
Terminal window fastapi loginA browser window opens. Create your account, or sign in, and allow the command line to use it. The command waits, then says that you are signed in.
-
In the project folder, create the app, and link the folder to it:
Terminal window fastapi cloud apps create --name ticket-api --linkIf the name is taken, choose another one, for example
ticket-api-yourname. The command writes the app’s ID to.fastapicloud/cloud.json, so that the next commands know which app to use.
You create the app before the first deployment on purpose: then you can give it its key before any code runs. If you deploy first, the API is public for a few minutes without a key. The warning API_KEY is not set in its log tells everyone who reads the log so.
Step 2: a new key, stored as a secret
Section titled “Step 2: a new key, stored as a secret”Make a long random key with Python’s secrets module. Send it to the platform without typing it, so it is not in your terminal history:
KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")printf %s "$KEY" | fastapi cloud env set API_KEY --secret --value-stdin --no-redeployfastapi cloud env set SHOW_DOCS false --no-redeployecho "$KEY"$key = python -c "import secrets; print(secrets.token_urlsafe(32))"$key | fastapi cloud env set API_KEY --secret --value-stdin --no-redeployfastapi cloud env set SHOW_DOCS false --no-redeploy$keyKEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")printf %s "$KEY" | fastapi cloud env set API_KEY --secret --value-stdin --no-redeployfastapi cloud env set SHOW_DOCS false --no-redeployecho "$KEY"| Part | Why |
|---|---|
secrets.token_urlsafe(32) |
32 random bytes as text, about 43 characters. Nobody can guess it. |
KEY=$(...) |
Keeps the key in a variable of this terminal only. |
--value-stdin |
Reads the value from the ` |
--secret |
FastAPI Cloud encrypts the value. Nobody can read it again, not even you, in the dashboard or the CLI. |
--no-redeploy |
Saves the setting without a deployment. There is nothing to deploy yet. |
SHOW_DOCS false |
The help desk system is the only client, so the documentation pages are off in production, as decided in Module 4. Not a secret. |
| The last line | Shows the key once, so you can copy it into Postman in the next lesson. Copy it now, then close this terminal. |
Because the secret cannot be read again, if you lose the key, you set a new one. That is also how you replace a key that leaked.
Step 3: deploy
Section titled “Step 3: deploy”fastapi deployThe command packs the project folder, sends it, and waits while the platform builds and starts the app. It leaves out .venv, caches, .git, every .env file, and the files that .gitignore lists. At the end, it prints the app’s address, which ends in .fastapicloud.dev. Write the address down.
Then check it, with HTTPS, from your terminal (replace the address with yours):
curl -i https://ticket-api.fastapicloud.dev/healthcurl -i https://ticket-api.fastapicloud.dev/readycurl -i https://ticket-api.fastapicloud.dev/docsYou expect 200, 200 and 404. HTTPS works without any setup: the platform has a certificate for its addresses. Each response has an X-Request-ID header, from your middleware.
Guided practice: publish the API
Section titled “Guided practice: publish the API”- Add
/ready, its model and its test. Run the tests, review the contract difference (a new endpoint), and export the contract. - In Postman, duplicate Check that the service runs as Check that the service can classify tickets, with the URL
{{baseUrl}}/ready. Give it these tests: status is200, andstatusisready. Export the collection again intopostman/. - Check
.gitignore: it lists.venv/,__pycache__/,.envand.fastapicloud/. - Sign in with
fastapi login, and create the linked app withfastapi cloud apps create --name ticket-api --link. - Make the key, and set
API_KEY(secret) andSHOW_DOCS, both with--no-redeploy. Copy the key that the last command shows, and close the terminal. - Open a new terminal, activate the environment, and run
fastapi deploy. Write down the address. - Call
/health,/readyand/docswith curl. Check the three status codes and theX-Request-IDheader. - Send a classify request without a key:
401. The key protects the API from the first deployment.
Your turn: the dashboard
Section titled “Your turn: the dashboard”Open FastAPI Cloud’s dashboard in your browser, and find your app. Without changing anything, find:
- The app’s address.
- The environment variables. Can you see the value of
API_KEY? OfSHOW_DOCS? - The latest deployment and its status.
Show what you should see
The address is the one that fastapi deploy printed. Under Environment Variables, SHOW_DOCS shows false, and API_KEY shows that it is a secret, without its value: secrets cannot be viewed after they are saved. The deployment is listed with its status. The dashboard can do the same as the CLI commands, and the next lesson uses it to delete the app.
Common mistake
Deployed, but every classify request gets 401, even with the key
Symptom: /health works, but classify requests get 401 with the key that you copied.
Cause: the key that the app has is not the key that you send. There are some common reasons. The key was set after the deployment with --no-redeploy, so the running app does not have it yet. Or a space or line break was copied with it. Or a second env set replaced it with a new random key.
Fix:
- Check the app’s log for the start message
API_KEY is not set(next lesson:fastapi cloud logs). If it is there, deploy again:fastapi deploy. - If you are not sure which key the app has, set a new one, and use the new key. Use step 2, without
--no-redeploythis time, so the app restarts with it. - Do not set the key without
--secret, to make it visible. A key that can be read is a key that can leak.
Check your understanding
Why are the app and its secret created before the first fastapi deploy, instead of deploying first and adding the key afterwards?
Show the answer
The app reads API_KEY at start. Without it, /v1/classify accepts requests from anyone, and the address is public as soon as the deployment ends. Creating the app and its secret first means that the very first running version is already protected. There is never a window, however short, where the API is open.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
The classifier is down. What do /health and /ready return, and what should the platform do?
Show the answer
/health returns 200: the process runs, so restarting it would not help. /ready returns 503 with classifier_unavailable: the service cannot do its job now. The platform, or a monitor, should stop sending classify traffic or alert someone, but not restart the app in a loop.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
- A practice deployment is disposable: its own key, no real data, and you delete it afterwards.
/healthis liveness (restart if it fails)./readyis readiness (it can do its job now).- Create the app, then set its secrets with
--secret --value-stdin, then deploy. The first running version is already protected. - Secrets on the platform cannot be read again. To replace a key, set a new one.
- The platform gives you HTTPS. Check
/health,/readyand a request without a key after each deployment.
Next, you check the deployed API with the Postman collection, read its logs, and remove it.
Next: Verify and clean up