Trace and Secure a Feedback API
Build a FastAPI feedback API with validation and safe header handling.
Introduction
30 Second Summary
Product feedback can look complete while important details are missing. When the path stays hidden, those gaps are hard to spot.
In this project, you will build Request Lens, a product-feedback API that makes each request visible from submission to response. You will strengthen its contract before publishing the evidence on GitHub.
What You'll Build
Your finished demo turns feedback submitted through interactive documentation into a receipt with accepted data plus safely handled request details.
By the end of this project, you'll have:
- A feedback submission flow where structured JSON produces a visible receipt for the product area, sentiment, message, and priority.
- A contract test where incomplete feedback returns 422. A valid submission returns 201 with the Authorization value replaced by [REDACTED].
- A published request map that shows the runnable API beside its public HTTPS path and local HTTP path.
- Secret Mission: Require a client-name header for every feedback request. Include the safe client identity in each successful receipt. Authorization stays redacted.
Are there any prerequisites?
You need a device running Windows 10 or newer with internet access. You also need Visual Studio Code, an existing GitHub account, and permission to install Python plus its extension.
Before We Start
Before the hands-on work begins, lock in the purpose of Request Lens. You are building a product-feedback API that accepts structured feedback so you can trace each request from the client to the response.
Set Up the Windows API Workspace
Visual Studio Code can organize Request Lens, but it cannot execute Python or isolate third-party packages on its own. Your API needs both capabilities before it can handle a request.
You'll install the Microsoft Python extension before creating a virtual environment. That environment keeps FastAPI and Pydantic versions reproducible.
In this step, get ready to:
- Install Python 3.15.0 on Windows.
- Equip VS Code with the Microsoft Python extension.
- Prepare the request-lens workspace with an isolated environment plus pinned dependencies.
Install Python and the Python extension
The Python interpreter runs your code. The Microsoft Python extension connects that interpreter to VS Code's environment tools.
Start by checking whether the required Python version is already available on your device.
- Press the Windows key to open Windows search.
- Type Windows PowerShell into the search field.
- Press Enter to open Windows PowerShell.
You'll see a PowerShell prompt where you can check the installed interpreter.
- Check your installed Python version by running this command:
py -3 --version
What does this command check?
The py launcher finds your installed Python 3 interpreter. The version output determines which setup path you need.
✔️ I see the required Python version
You should see Python 3.15.0. Your interpreter is ready for the Request Lens environment.
ⓧ I see an older version
Your current interpreter is below the project's required version. Install Python 3.15.0 alongside it from the official release page.
- Open the Python 3.15.0 release page.
- Select Windows installer (64-bit) from the download table.
Your browser downloads the installer for the required Windows release.
Windows may ask for permission when the installer starts. This allows Python to complete its local setup.
- Launch the downloaded installer.
- Approve the Windows permission request if it appears.
- Complete the installer with its recommended settings.
The installer confirms when Python 3.15.0 is available.
- Close the current Windows PowerShell window.
- Reopen Windows PowerShell through Windows search.
- Run the version check above again.
You should now see Python 3.15.0. The version gap is resolved.
ⓧ Command not found
Windows cannot currently find the Python launcher. Install Python 3.15.0 from its official release page.
- Open the Python 3.15.0 release page.
- Select Windows installer (64-bit) from the download table.
Your browser downloads the recommended installer for a 64-bit Windows device.
Windows may request permission when you launch the installer. This gives the installer access to finish the local setup.
- Launch the downloaded installer.
- Approve the Windows permission request if it appears.
- Complete the installer with its recommended settings.
The completion screen confirms that Python is installed.
- Close the current Windows PowerShell window.
- Reopen Windows PowerShell through Windows search.
- Run the version check above again.
You should now see Python 3.15.0. Windows can now reach the interpreter.
Python handles execution. The extension now needs to add Python environment management to VS Code.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter to open VS Code.
You'll see the VS Code window open.
- Press Ctrl+Shift+X to open the Extensions view.
The Extensions view opens with a search field for the Visual Studio Marketplace.
- Type Python into the extension search field.
- Select the extension named Python that lists Microsoft as its publisher.
The extension details page identifies Microsoft as the publisher.
- Select Install on the extension details page.
The Install button changes to a Manage gear when the extension is ready. That's the editor support in place for your project environment.
Extension not installing?
Confirm that the selected extension is named Python and published by Microsoft. A network restriction can also prevent VS Code from downloading Marketplace extensions.
Help me troubleshoot the Microsoft Python extension installation in VS Code.
Create the request-lens environment
A workspace folder gives VS Code a defined boundary for your files. The project-specific environment then keeps installed packages inside that boundary.
- Select File from the top menu in VS Code.
- Select Open Folder from the menu.
You'll see the Windows folder picker.
- Navigate to your Desktop in the folder picker.
- Select the New folder control.
The folder picker gives the new folder an editable name.
- Type request-lens as the folder name.
- Press Enter to confirm the name.
You'll see the request-lens folder on your Desktop.
- Select the request-lens folder.
- Click Select Folder to open it as the VS Code workspace.
The Explorer sidebar now shows request-lens as the workspace folder. This confirms that future files land in the correct place.
- Confirm that you trust the folder if VS Code displays a workspace trust prompt.
The interpreter picker can feel crowded. The full 3.15.0 version identifies the interpreter this project needs.
- Press Ctrl+Shift+P to open the Command Palette.
- Type Python: Create Environment into the Command Palette.
You'll see the Python environment command in the results.
- Select Python: Create Environment.
- Select Venv as the environment type.
VS Code displays the Python interpreters available on your device.
- Select the interpreter showing Python 3.15.0.
You'll see a .venv folder appear in the Explorer sidebar when creation finishes. Your project now has its own isolated Python environment.
Why use a virtual environment?
The .venv folder holds a project-specific Python interpreter plus its installed packages. Changes inside it do not alter the dependencies used by your other Python projects.
- Press Ctrl+Shift+P to reopen the Command Palette.
- Type Python: Select Interpreter into the Command Palette.
The interpreter command appears with the environments detected for this workspace.
- Select the interpreter whose path contains .venv\Scripts\python.exe.
The VS Code status bar now identifies the .venv interpreter. That environment becomes active in each fresh terminal created for this workspace.
Can't find the .venv interpreter?
Confirm that .venv appears directly inside the request-lens folder. Re-run the environment creation flow if the folder is absent.
Help me make VS Code detect my .venv interpreter.
Add project safeguards and install dependencies
A requirements file records the exact packages that Request Lens uses. An ignore file keeps the local environment plus generated Python files out of the repository you publish later.
- Select the New File control in the Explorer toolbar.
An editable file name appears inside the request-lens workspace.
- Type requirements.txt as the file name.
- Press Enter to create the file.
The empty requirements.txt file opens in the editor.
- Add the pinned project dependencies to requirements.txt by pasting this code:
fastapi[standard]==0.143.0
pydantic==2.14.0
What do these requirements provide?
- The fastapi[standard]==0.143.0 requirement installs FastAPI 0.143.0 with the development server plus FastAPI command-line tools.
- The pydantic==2.14.0 requirement installs Pydantic 2.14.0 for the request contract you build later.
- The exact pins make another installation use the same package versions.
- Save requirements.txt by pressing Ctrl+S.
You'll see both pinned requirements in the saved file.
Requirements file looks different?
Confirm that the filename is exactly requirements.txt. Check that each dependency appears on its own line.
Help me compare my requirements.txt file with the required package pins.
- Select the New File control in the Explorer toolbar.
Another editable file name appears inside the workspace.
- Type .gitignore as the file name.
- Press Enter to create the file.
The empty .gitignore file opens in the editor.
- Add the project exclusions to .gitignore by pasting this code:
.venv/
__pycache__/
*.pyc
What does this file protect?
- The .venv/ rule keeps the local virtual environment outside version control.
- The __pycache__/ rule excludes generated Python cache folders.
- The *.pyc rule excludes generated compiled Python files.
- Save .gitignore by pressing Ctrl+S.
You'll see all three exclusion patterns in the saved file.
Ignore rules not matching?
Confirm that the filename begins with a dot. Make sure Windows did not add a hidden .txt extension.
Help me check my .gitignore filename and Request Lens exclusion rules.
Use the tabs below to confirm that both workspace files match the target artifacts.
✔️ Awesome, I've got everything!
Both project files are saved inside request-lens. Your dependency pins plus ignore rules are ready.
ⓧ I'd like to double check the full code
Compare your complete requirements.txt file with this reference.
fastapi[standard]==0.143.0
pydantic==2.14.0
Requirements checkpoint
The file contains exactly two dependency pins. Each requirement occupies one line.
Compare your complete .gitignore file with this reference.
.venv/
__pycache__/
*.pyc
Ignore file checkpoint
The file contains exactly three exclusion patterns. These patterns cover the environment plus generated Python files.
The pinned files define the environment. Installing them turns that definition into packages your API can use.
- Select Terminal from the VS Code top menu.
- Select New Terminal.
A fresh terminal opens inside request-lens. Its prompt should show that .venv is active.
- Install the pinned dependencies into .venv by running this command:
python -m pip install -r requirements.txt
What does this command do?
The command runs pip through the Python interpreter selected for the terminal. It reads every exact package pin from requirements.txt.
The standard FastAPI package includes the development server plus the FastAPI command used in the next step.
You'll see the package installation finish without an error. That setup hurdle is cleared because FastAPI plus Pydantic now live inside the project environment.
Dependency installation failed?
Confirm that the terminal prompt identifies .venv. Check that requirements.txt is visible in the Explorer sidebar.
A network restriction can interrupt package downloads. Re-run the installation after confirming your internet connection.
Help me troubleshoot the dependency installation in my Request Lens .venv. Share the non-sensitive error text in the NextWork community if your Windows setup behaves differently.
Before you run the final checks, which Python path should prove that the fresh terminal activated your project environment?
- Select Terminal from the VS Code top menu.
- Select New Terminal to open a fresh terminal.
The new terminal inherits the interpreter selected for the workspace.
- Verify the installed Python version by running this command:
py -3 --version
What does the version check prove?
The launcher reports the Python 3 version installed on Windows. Request Lens requires Python 3.15.0 for this workspace.
You should see Python 3.15.0 in the terminal output.
- Verify which Python executable the fresh terminal uses by running this command:
where python
What does the path check prove?
The path output shows which Python executable the terminal can reach. A path containing .venv\Scripts confirms that the workspace environment is active.
You should see a Python path containing .venv\Scripts. Your editor, interpreter, environment plus dependencies are now connected.
Your Windows API workspace is reproducible and ready to run code. Next up, you'll launch Request Lens and see its first JSON health response.
Launch the First Visible API
Your isolated Windows workspace can now run the pinned FastAPI packages.
Request Lens now needs one route that a browser can reach. A health route gives the service a clear ready signal.
The returned JSON proves that the full local request-response cycle works. That visible result is the foundation for the feedback flow.
In this step, get ready to:
- Create a FastAPI application with a health route.
- Start the development server to view the health response.
- Confirm the route in the interactive API documentation.
Create the health route
A health route is a small endpoint that reports whether a service can respond. The app object connects that route to FastAPI.
- In the VS Code file sidebar, select the file creation control.
- Enter main.py as the file name.
Good, the empty main.py file now sits inside your open request-lens folder.
- Build the health service by pasting this code into main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def health():
return {"service": "request-lens", "status": "ready"}
What does this code do?
- FastAPI provides the class used to create the application.
- app = FastAPI() creates the application that receives requests.
- @app.get("/") connects GET requests at the base path to health().
- health() returns the service name plus its ready state.
- Save main.py.
- Confirm main.py remains listed inside the request-lens folder in the VS Code file sidebar.
Can't find main.py?
- Confirm request-lens is the folder shown in the VS Code file sidebar.
- Move main.py directly inside the request-lens folder if it was created elsewhere.
Help me place main.py in the correct folder.
✔️ Awesome, I've got everything!
Your saved main.py now contains the complete health service.
ⓧ I'd like to double check the full code
Compare your complete main.py file with this version:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def health():
return {"service": "request-lens", "status": "ready"}
Launch the development server
The development server loads main.py before accepting local requests. The command stays attached to the terminal while the service runs.
- Switch back to the fresh VS Code terminal from the previous step.
- Start the development server by running this command:
fastapi dev
What does this command do?
The command finds main.py. It starts FastAPI's local development server.
The terminal remains busy while the server handles requests. Keep the process running for the rest of this step.
Before you open the page, predict the JSON fields that the health route returns.
- Open http://127.0.0.1:8000/ in your browser.
You'll see {"service":"request-lens","status":"ready"} in the browser.
You have your first live API response. Request Lens can now complete a request-response cycle on your computer.
Health page not loading?
- Confirm the server process is still running in the VS Code terminal.
- Save main.py before reloading the health URL.
- Confirm the selected Python environment is the project .venv environment.
Help me troubleshoot my local FastAPI health route.
Inspect the interactive documentation
FastAPI builds interactive API documentation from registered routes. This gives you a browser-based view of the service contract.
Before you check the documentation, predict which method and path it lists for the health route.
- Open http://127.0.0.1:8000/docs in a new browser tab.
You'll see a GET operation for /. This confirms the health route is included in the interactive documentation.
GET route missing?
- Confirm the browser address ends with /docs.
- Save main.py before refreshing the documentation.
- Confirm the development server is still running in the VS Code terminal.
Help me find the health route in the FastAPI documentation.
The health route now responds in the browser and appears in the interactive documentation. Next, you'll add the first feedback intake route and send it a request.
Build a Naive Feedback Intake
Your health route already proves that a browser can reach Request Lens. The ready JSON confirms that FastAPI can complete a request-response cycle.
A feedback endpoint must decide which body shape it trusts. It must also decide which request metadata can safely appear in a receipt.
This first version uses a broad dictionary. It also passes the Authorization header into the receipt so the running API can reveal the consequence.
In this step, get ready to:
- Add a POST route at /feedback that accepts a feedback dictionary.
- Return a receipt containing the submitted body plus request metadata.
- Use the interactive API documentation to submit one incomplete body with demonstration Authorization text.
Add the request-aware feedback route
A POST route represents a client submitting data through HTTP. FastAPI can inject the raw Request object plus an optional header declared with Header().
- Replace the contents of main.py with the code below:
from typing import Annotated
from fastapi import FastAPI, Header, Request
app = FastAPI()
@app.get("/")
def health():
return {"service": "request-lens", "status": "ready"}
@app.post("/feedback", status_code=201)
def create_feedback(
request: Request,
feedback: dict,
authorization: Annotated[str | None, Header()] = None,
):
return {
"accepted": True,
"feedback": feedback,
"request": {
"method": request.method,
"path": "/feedback",
"authorization": authorization,
},
}
What does this code do?
- The Annotated type connects the optional string value to FastAPI's header handling.
- The @app.post("/feedback", status_code=201) decorator creates the feedback route with creation status 201.
- The feedback: dict parameter accepts any object-shaped JSON body.
- The request.method value records the method used for the request.
- The authorization value enters the returned receipt unchanged.
- Save main.py.
- Switch back to the interactive docs page from earlier.
- Refresh the page.
You should see a POST section for /feedback. Good progress. Request Lens now exposes a second route for feedback receipts.
Don't see the feedback route?
- Confirm that main.py is saved.
- Check that the development server from earlier remains active in the VS Code terminal.
- Refresh the interactive docs after saving the file.
Help me find why the POST feedback route is missing from my FastAPI docs.
✔️ Awesome, I've got everything!
Your saved main.py now contains the health route plus the naive feedback route.
ⓧ I'd like to double check the full code
- Compare your saved main.py with this full reference:
from typing import Annotated
from fastapi import FastAPI, Header, Request
app = FastAPI()
@app.get("/")
def health():
return {"service": "request-lens", "status": "ready"}
@app.post("/feedback", status_code=201)
def create_feedback(
request: Request,
feedback: dict,
authorization: Annotated[str | None, Header()] = None,
):
return {
"accepted": True,
"feedback": feedback,
"request": {
"method": request.method,
"path": "/feedback",
"authorization": authorization,
},
}
What should match?
This reference keeps the existing health route intact. It adds the imports plus the complete create_feedback() function needed for the naive receipt.
Test the weak boundary
The interactive documentation turns the new route signature into a request form. One request can now reveal what the dictionary accepts plus what the receipt returns.
- Switch back to the interactive docs page from earlier.
- Expand the POST section for /feedback.
You should see request controls for a JSON body plus the optional Authorization header.
- Click Try it out.
The request fields become editable.
- Replace the request body with the JSON below:
{"message":"Export is slow"}
What are you testing?
- The message field holds the single piece of feedback in this request.
- The body omits product area, sentiment, and priority.
- Enter Bearer demo-token-not-a-secret for the Authorization header.
Before you send it, do you think a single message field produces a receipt or a client error?
- Click Execute.
You will see status 201. The returned JSON contains the single message field inside feedback.
The authorization field also repeats Bearer demo-token-not-a-secret in full.
Do you see a different result?
- Confirm that your saved main.py matches the full reference above.
- Refresh the interactive docs to load the current route definition.
- Repeat the request with the exact demonstration JSON plus demonstration Authorization value.
Help me reproduce the expected naive feedback response.
You found the weak boundary. The API creates a receipt from incomplete feedback.
It also places the demonstration Authorization value in the response.
Why is this result risky?
A dictionary requires object-shaped JSON. It cannot require product area, sentiment, or priority.
Authorization headers can carry credentials. Returning the value makes that material easier to expose.
Safe request metadata should prove useful context without replaying credential material.
Screenshots preserve every visible field. The demo token is safe to capture because it is demonstration text.
You have clear evidence from the running API. Next, you'll make the body contract trustworthy while keeping credential material out of the receipt.
Enforce the Contract and Redact the Secret
Your naive /feedback endpoint gave you clear evidence of a weak boundary. Its receipt accepted incomplete JSON while exposing credential material.
Now you'll turn that boundary into a strict API contract with Pydantic. The contract gives every feedback request a trusted shape.
You will also replace the Authorization value with a safe state. The receipt stays useful without leaking credential material.
In this step, get ready to:
- Define the required feedback fields with fixed choices.
- Update the feedback route with validation plus safe header handling.
- Exercise the rejected request plus successful receipt paths.
Define the feedback contract
A request model describes the data your endpoint is prepared to accept. Literal types narrow selected fields to explicit choices.
- Return to main.py in the Visual Studio Code workspace from earlier.
- Select the existing imports at the top of main.py.
- Replace the selected imports by copying this code:
from typing import Annotated, Literal
from fastapi import FastAPI, Header, Request
from pydantic import BaseModel
What Do These Imports Add?
- Literal lets each constrained field accept only the listed values.
- BaseModel provides the foundation for the feedback request model.
- FastAPI can read that model to validate incoming request bodies.
- Save main.py.
- Return to the Visual Studio Code terminal from earlier.
You should see the running server reload without a syntax error.
Does the Server Report an Import Problem?
- Check that Visual Studio Code still uses the .venv interpreter from Step 1.
- Check that the terminal prompt still shows the active virtual environment.
Ask for help with the active interpreter if the import still fails: Help me diagnose a Pydantic import problem in my active request-lens virtual environment.
The imports make the validation types available. The next code defines the exact body shape accepted by the endpoint.
- Place your cursor two blank lines below app = FastAPI().
- Add the four-field contract by copying this code:
class Feedback(BaseModel):
product_area: Literal["onboarding", "analytics", "billing"]
sentiment: Literal["positive", "neutral", "negative"]
message: str
priority: Literal[1, 2, 3]
What Does This Model Enforce?
- product_area accepts one of three named product areas.
- sentiment accepts one of three sentiment values.
- message requires text from the client.
- priority accepts the numeric value 1, 2, or 3.
- Save main.py.
- Return to the running server terminal.
You should see another reload without a syntax error. The server can now load the Feedback model.
Seeing a Syntax Error?
- Check that every field inside Feedback has four leading spaces.
- Check that each Literal choice matches the model above.
Ask for a line-by-line comparison if the server cannot reload: Help me find the syntax problem in my Request Lens Feedback model.
Validate the body and redact the header
The model only protects the route after the endpoint uses it as the body type. The response must also serialize the validated data into a receipt.
An HTTP header can carry sensitive context. A redacted state confirms that the header arrived without returning its value.
- Locate @app.post("/feedback", status_code=201) in main.py.
- Select the complete create_feedback() function with its decorator.
- Replace the selected function by copying this code:
@app.post("/feedback", status_code=201)
def create_feedback(
request: Request,
feedback: Feedback,
authorization: Annotated[str | None, Header()] = None,
):
return {
"accepted": True,
"feedback": feedback.model_dump(),
"request": {
"method": request.method,
"path": "/feedback",
"authorization": "[REDACTED]" if authorization else "not provided",
},
}
How Does the Safer Endpoint Work?
- feedback: Feedback applies the model before the function receives the request body.
- feedback.model_dump() turns the validated model into data that can appear in the receipt.
- [REDACTED] confirms that an Authorization value was supplied while keeping the value hidden.
- not provided records that the request contained no Authorization value.
- Save main.py.
- Return to the running server terminal.
- Refresh the interactive documentation page.
You should see a structured request body with fields for product_area, sentiment, message, and priority.
Still Seeing an Arbitrary Object?
- Refresh the documentation page after the server finishes reloading.
- Confirm that the endpoint parameter says feedback: Feedback.
- Confirm that main.py is saved.
Ask for help if the old request shape remains: Help me work out why my FastAPI docs still show an arbitrary feedback object.
✔️ Awesome, I've got everything!
Your main.py now contains the feedback contract plus safe receipt logic.
ⓧ I'd like to double check the full code
The complete main.py file should match this reference.
from typing import Annotated, Literal
from fastapi import FastAPI, Header, Request
from pydantic import BaseModel
app = FastAPI()
class Feedback(BaseModel):
product_area: Literal["onboarding", "analytics", "billing"]
sentiment: Literal["positive", "neutral", "negative"]
message: str
priority: Literal[1, 2, 3]
@app.get("/")
def health():
return {"service": "request-lens", "status": "ready"}
@app.post("/feedback", status_code=201)
def create_feedback(
request: Request,
feedback: Feedback,
authorization: Annotated[str | None, Header()] = None,
):
return {
"accepted": True,
"feedback": feedback.model_dump(),
"request": {
"method": request.method,
"path": "/feedback",
"authorization": "[REDACTED]" if authorization else "not provided",
},
}
This reference includes the health route plus the completed feedback route.
Test rejection and safe success
The documentation now gives you two paths through the contract. The first path tests a malformed body.
- Expand POST /feedback in the interactive documentation.
- Click Try it out.
- Replace the generated request body by copying this incomplete payload:
{
"message": "Export is slow"
}
What Does This Payload Test?
This body supplies only message. The server compares it with the four required model fields when you execute the request.
- Enter Bearer demo-token-not-a-secret in the authorization field.
Before you execute this request, do you think the new contract will accept a body with only message?
- Click Execute.
You'll see status 422. The request stops before the endpoint logic receives it.
That failed request is the contract doing its job.
Still Getting Status 201?
- Confirm that create_feedback() uses feedback: Feedback.
- Save main.py.
- Refresh the interactive documentation after the server reloads.
Ask for help if the incomplete body still succeeds: Help me diagnose why my incomplete feedback request still returns status 201.
The second path proves that valid data still reaches the endpoint. It also checks that the receipt hides the supplied Authorization value.
- Replace the request body with this complete payload:
{
"product_area": "analytics",
"sentiment": "negative",
"message": "Export takes too long",
"priority": 1
}
Why Does This Payload Pass?
Every required field is present. Each constrained value appears in its model's allowed choices.
- Keep Bearer demo-token-not-a-secret in the authorization field.
Before you execute the complete request, what do you expect the receipt to reveal about the Authorization value?
- Click Execute.
What Should I See?
- The response shows status 201.
- The receipt contains the accepted feedback fields.
- The request method shows POST.
- The request path shows /feedback.
- The authorization state shows [REDACTED].
You have closed both gaps from the naive endpoint. Your API now enforces the feedback contract.
Missing the Safe Receipt?
- Compare every request field with the allowed model values.
- Confirm that the response uses feedback.model_dump().
- Confirm that the authorization response uses [REDACTED] when a value is present.
Ask for help if the response still exposes the demonstration value: Help me diagnose why my Request Lens receipt is not redacting the Authorization header.
Your feedback boundary now rejects malformed data and protects header values. Next, you'll trace the wider request journey and publish the evidence.
Trace the Journey and Publish the Evidence
Your validated FastAPI endpoint now rejects malformed feedback. It also protects the Authorization value.
A technical PM also needs to explain what happens before endpoint logic runs. The public journey adds network layers that the local response does not show.
Windows PowerShell makes DNS evidence visible. It also reveals TCP connectivity. A README gives that evidence a shareable home on GitHub.
In this step, get ready to:
- Observe name resolution with Windows PowerShell.
- Compare the public HTTPS journey with the local Request Lens journey.
- Publish the project files in a GitHub repository.
Trace DNS and TCP
DNS translates a host name into a network address. TCP tests whether your computer can establish transport connectivity to a destination.
- Create a second Windows PowerShell terminal using the plus button in VS Code's terminal panel.
- Leave the terminal running fastapi dev untouched.
Before you run the first check, consider which address type the name lookup might return.
- Query the network address for Bing by running this command:
Resolve-DnsName -Name www.bing.com
What does this command show?
- Resolve-DnsName asks the Windows DNS client to perform a name lookup.
- -Name www.bing.com identifies the host name to resolve.
- The returned records show the addresses your computer can use to reach that host.
You should see DNS records containing one or more network addresses. This proves that name resolution can turn a public host name into a destination.
No DNS records in the result?
Check that your device has an internet connection. A restricted network may also block the lookup.
Help me diagnose a Windows DNS lookup that returns no records.
Before you run the transport check, consider whether port 80 will be reachable from your current network.
- Run a detailed TCP connectivity test with this command:
Test-NetConnection -Port 80 -InformationLevel "Detailed"
What does this command show?
- Test-NetConnection collects network connectivity diagnostics.
- -Port 80 targets the standard port used for an HTTP connection.
- -InformationLevel "Detailed" requests a detailed report that includes the TCP test result.
You should see a detailed connection report with a TCP test result. A successful result proves that your computer can establish transport connectivity through the tested port.
Where do TLS and HTTP fit?
A public HTTPS request uses TLS to protect traffic from eavesdropping or tampering. The client then sends the HTTP message that carries the method, path, headers, and optional body.
Request Lens uses loopback TCP with local HTTP. Its shorter path keeps the application boundary visible without adding local certificates.
TCP test unsuccessful?
A firewall or managed network may block the tested port. The reported result still gives you evidence about transport connectivity from your current environment.
Help me interpret my Windows TCP connectivity test.
Document both request journeys
The Markdown README connects your network observations to the working API. Its two maps show where the public journey differs from your local development path.
- Create README.md inside the request-lens folder using the new-file button in VS Code's file sidebar.
- Paste the project overview and both request maps into README.md using this content:
# Request Lens
Request Lens is a small product-feedback API that makes the path from HTTP request to validated response visible.
## What it demonstrates
- A JSON request body with an explicit product contract
- Header handling without returning credential material
- Validation failures and successful creation responses
- The difference between a public HTTPS journey and a local HTTP journey
## Public HTTPS journey
Client -> DNS -> TCP -> TLS -> HTTP request -> server -> HTTP response
## Local Request Lens journey
Client -> loopback TCP -> HTTP request -> FastAPI -> Pydantic validation -> endpoint logic -> JSON response
What does this documentation explain?
- The opening defines Request Lens as a visible request-to-response demonstration.
- The public map includes DNS, TCP, and TLS before the HTTP request reaches a server.
- The local map follows the request through FastAPI and Pydantic before endpoint logic returns a JSON response.
- Save README.md.
- Open the Markdown preview using the preview control in the top-right corner of the editor.
You should see a Request Lens heading, a four-item demonstration list, and two request maps.
Request maps missing from the preview?
Confirm that you pasted both journey sections into README.md. Save the file before checking the preview again.
Help me fix missing sections in my Request Lens README preview.
- Place your cursor one blank line below the local journey map.
- Add the Windows run instructions using this content:
The local development server uses HTTP at `http://127.0.0.1:8000`. TLS is not configured in this project.
## Run locally on Windows
1. Create and select a `.venv` environment in VS Code.
2. Install dependencies:
```powershell
python -m pip install -r requirements.txt
```
3. Start the development server:
```powershell
fastapi dev
```
4. Open `http://127.0.0.1:8000/docs`.
What do these instructions add?
- The opening sentence states that the local server uses HTTP without TLS.
- The numbered instructions explain how another Windows user can prepare the virtual environment.
- The command blocks show how to install dependencies and start the development server.
- Save README.md.
- Return to the Markdown preview using the editor's preview control.
You should see a numbered Windows setup guide. The two PowerShell commands should appear in separate formatted code blocks.
Commands rendering as plain text?
Check the opening and closing code fences around each command. Keep the indentation shown beneath the numbered list.
Help me repair the PowerShell code blocks in my Markdown README.
- Place your cursor one blank line below the documentation URL.
- Add the valid feedback example and credential warning using this content:
## Valid feedback example
```json
{
"product_area": "analytics",
"sentiment": "negative",
"message": "Export takes too long",
"priority": 1
}
```
Use `Bearer demo-token-not-a-secret` only as demonstration text. Never place a real token in source code, screenshots, or a public repository.
Why include this example?
- The JSON example gives readers a complete body that satisfies the feedback contract.
- The final warning separates demonstration text from real credential material.
- Together, these details make the repository runnable without encouraging unsafe header handling.
- Save README.md.
- Return to the Markdown preview using the editor's preview control.
You should see a formatted JSON example followed by the warning about real tokens.
JSON example formatting broken?
Confirm that the JSON sits between the opening and closing code fences. Check that each quotation mark and comma matches the reference.
Help me fix the JSON example in my Request Lens README.
✔️ Awesome, I've got everything!
Great. Double check that you saved README.md and can see both request maps in the Markdown preview.
ⓧ I'd like to double check the full code
Compare your complete README.md with this reference:
# Request Lens
Request Lens is a small product-feedback API that makes the path from HTTP request to validated response visible.
## What it demonstrates
- A JSON request body with an explicit product contract
- Header handling without returning credential material
- Validation failures and successful creation responses
- The difference between a public HTTPS journey and a local HTTP journey
## Public HTTPS journey
Client -> DNS -> TCP -> TLS -> HTTP request -> server -> HTTP response
## Local Request Lens journey
Client -> loopback TCP -> HTTP request -> FastAPI -> Pydantic validation -> endpoint logic -> JSON response
The local development server uses HTTP at `http://127.0.0.1:8000`. TLS is not configured in this project.
## Run locally on Windows
1. Create and select a `.venv` environment in VS Code.
2. Install dependencies:
```powershell
python -m pip install -r requirements.txt
```
3. Start the development server:
```powershell
fastapi dev
```
4. Open `http://127.0.0.1:8000/docs`.
## Valid feedback example
```json
{
"product_area": "analytics",
"sentiment": "negative",
"message": "Export takes too long",
"priority": 1
}
```
Use `Bearer demo-token-not-a-secret` only as demonstration text. Never place a real token in source code, screenshots, or a public repository.
How should I use this reference?
Compare the headings, request maps, commands, JSON example, and safety warning in order. This reference represents the complete README that you publish.
Publish the project on GitHub
The web upload path keeps this project focused on request journeys. It also avoids adding command-line identity or remote authentication setup.
Why web upload instead of Git?
The Git command line introduces identity configuration and remote authentication. GitHub's web upload flow produces the same shareable project evidence with fewer unrelated steps.
GitHub may ask for your existing sign-in. No new credential is created or shared in this step.
- Navigate to GitHub in your browser.
- Sign in with your existing account if GitHub asks.
- Select New repository.
- Enter request-lens in the repository name field.
- Choose the visibility that fits how you plan to share the project.
- Leave the repository initialization options clear.
- Select Create repository.
Good progress. Your empty repository is ready to receive the exact files that explain and run Request Lens.
Publishing files deserves a careful pause. You will select only the four project files, which keeps .venv and any real credentials on your computer.
- Select Add file in the repository.
- Select Upload files.
- Choose main.py, requirements.txt, .gitignore, and README.md from the request-lens folder.
- Confirm that .venv does not appear in the upload list.
- Enter Publish Request Lens evidence in the commit message field.
- Select Propose changes.
Before the final check, consider which four files you expect the repository to display.
- Return to the repository's main page after the upload finishes.
You should see main.py, requirements.txt, .gitignore, and README.md in the repository. The page should also render the README with both the public HTTPS path and the local Request Lens path.
Missing a file after the upload?
Use Add file and Upload files again for the missing project file. Keep .venv and real secrets out of every upload.
Help me verify my Request Lens GitHub upload.
You have turned a working local API into published technical evidence. Your repository now shows the journey from network lookup to validated response.
Secret mission
Require a Client Identity Header
Your feedback receipt shows what arrived, but it cannot identify the client application that sent it. Extend the endpoint with a required client-name header that returns safe operational context while keeping Authorization redacted.
Clean Up Your Resources
Clean Up Your Resources
Choose whether to keep your resources running, pause your local server, or delete the project copies entirely. This project has no ongoing usage charges.
Resources you used:
- The local request-lens folder.
- The .venv environment inside request-lens.
- The local FastAPI development server.
- The GitHub repository named request-lens.
Keep everything running
No action is needed. Choose this while you are still extending Request Lens.
- Keep the local FastAPI server available for further testing.
- Retain the local request-lens folder.
- Retain the .venv environment with its installed dependencies.
- Keep the GitHub repository as your published project evidence.
Pause - I'll come back to this later
Stop the running process to free local resources. Your code remains ready for another session.
- Switch back to the VS Code terminal that is running the FastAPI server.
- Press Ctrl+C to stop the server.
- Leave the local request-lens folder in place.
- Leave the GitHub repository available for future work.
Delete - I don't want to use this again
Removing each saved copy is permanent. GitHub stops showing the repository after the deletion completes.
- Save any project files you want to keep outside the request-lens folder.
- Switch back to the VS Code terminal that is running the FastAPI server.
- Press Ctrl+C to stop the server.
- Close VS Code.
- Find the local request-lens folder in File Explorer.
- Delete the local request-lens folder.
- Empty the Recycle Bin.
That removes the local project files. It also removes the .venv environment stored inside the folder.
- Return to the request-lens repository on GitHub.
- Open the repository settings page.
- Use the deletion controls to permanently delete the request-lens repository.
- Complete GitHub's confirmation prompt.
- Refresh the former repository page.
You should no longer see the project files on GitHub. The local server also stays offline.
Nice Work!
Nice Work!
You did it! Request Lens is now a working product-feedback API built with FastAPI. It validates incoming feedback while keeping Authorization values out of returned receipts.
You've learned how to:
- Build a FastAPI service that accepts structured JSON feedback through interactive documentation. Its receipt makes the request method visible. It also shows the route plus safe header metadata.
- Replace an intentionally weak API boundary with a Pydantic validation contract. Malformed bodies now return a client error. Authorization values remain redacted.
- Trace the request journey across DNS, TCP, TLS, and HTTP. Publish the runnable code with both request maps in a GitHub repository.
- Complete the Secret Mission by requiring X-Client-Name for client identity. Missing headers return status 400. Successful receipts include the client name while Authorization stays [REDACTED].
Ready to quiz yourself?