Existing stack

[ AxiomCore · Documentation ]

FastAPI

Keep a FastAPI service, extract its contract, and call it from a browser client generated for atmx-web.

Keep FastAPI as your executable backend and ordinary TypeScript as your frontend. AxiomCore extracts a contract from the FastAPI app and generates a typed web client from it. This guide runs a real Python service and a real browser request, with no mock responses.

PathStatus
FastAPI extractionAvailable (the published extractor binary is for macOS arm64)
Vanilla web client (atmx-web)Available

Requirements

ToolVersion used by the example
Axiom CLI0.149.1
FastAPI extractor0.1.4, macOS arm64
Python3.12, to run the service (FastAPI 0.123.5, Pydantic 2.13.4, Uvicorn 0.38.0)
Node.js and npmNode.js 22.12 or later
Web packagesatmx-cli 0.146.7, atmx-web 0.147.5, Vite 7.3.7, TypeScript 5.9.3

You also need two terminals and free ports 8083 and 5175.

1. Get the example

Terminal 1
git clone --depth 1 https://github.com/AxiomCore/AxiomCore.git
cd AxiomCore/examples/documentation/fastapi-web

The example has backend/ (the FastAPI app, its Acore source and manifest) and frontend/ (a Vite page that calls the API).

2. Install the FastAPI extractor

Install FastAPI extractor 0.1.4 with the checksummed steps in backend extractors. The CLI runs ~/.acore/bin/axiom-fastapi-extractor, or the binary at the path in AXIOM_FASTAPI_EXTRACTOR. In CLI 0.149.1, axiom install <extractor> --module does not find the published build, so install it directly.

3. Build the contract and start the API

The backend source is two lines. amends tells the CLI to extract the FastAPI app app from main.py, then apply anything else declared in this file:

backend/axiom.acore
amends "axiom-fastapi:main.py:app"

project(id: "docs.fastapi.greeting", version: "1.0.0")

Create a virtual environment for the service, build the contract, and start the API:

Terminal 1
python3 -m venv .venv
.venv/bin/python -m pip install -r backend/requirements.txt
cd backend
axiom build axiom.acore --out backend.axiom
../.venv/bin/python -m uvicorn main:app --host 127.0.0.1 --port 8083
Expected build output
Built backend contract: backend.axiom

Extraction imports main.py, so keep its import path free of migrations, worker startup, production connections and destructive setup. See how extraction works for what runs.

4. Review the extracted contract

Before anything consumes the contract, review method and path, required inputs, response models, validation, authentication and errors. Two ways to look:

  • axiom inspect backend.axiom opens an interactive endpoint and model browser (it needs a terminal).
  • axiom explain axiom.acore prints the extracted contract as JSON.

For this example the contract holds one endpoint, greet: GET /greet/{name} with a required string path parameter and a Greeting response with one message string. The 40-character limit on name is not in the extracted contract; FastAPI enforces it and returns 422. Extraction cannot infer every rule a handler applies, so add the facts that matter in Acore or test them directly.

To start your own app without an Acore file, run this from its backend directory, replacing main.py:app with your module and app variable:

Your backend directory
axiom init main.py:app --module axiom-fastapi

axiom init writes axiom.acore and a backend AxiomDeps.toml. It refuses to overwrite an existing axiom.acore. Without both arguments it opens an interactive wizard.

5. Generate the web client and run it

Open a second terminal in the example root:

Terminal 2
cd frontend
npm ci
axiom pull ../backend/backend.axiom --framework atmx-web --name greetings --out generated
npm run dev

axiom pull records the contract in frontend/AxiomDeps.toml, copies it to public/greetings.axiom, and runs the generator into generated/. The predev script copies the atmx-web runtime Wasm into public/, so both assets are served from the frontend origin.

Install the generator before you pull

axiom pull runs the project's local atmx-cli when node_modules has one. Otherwise it runs npx atmx-cli@<CLI version>, and no atmx-cli 0.149.x is published, so npm fails with ETARGET. The example pins atmx-cli 0.146.7 as a dev dependency; npm ci installs it. Pin and test the combination for your own contract.

Open http://127.0.0.1:5175 and select Say hello. The page shows Hello, Ada! from the running Python service.

6. Initialize before querying

frontend/main.ts overrides the generated config with the API base URL and the Wasm location, awaits atmx.init, and enables the button only when ready.ok is true. A query returns a handle that you subscribe to, not a value you await:

frontend/main.ts (query excerpt)
const query = atmx.query('greetings.greet', { name: 'Ada' });
const unsubscribe = query.subscribe((state) => {
  if (state.error) output.textContent = state.error.message;
  else if (state.data) output.textContent = state.data.message;
});
// When you replace the query or the page goes away:
unsubscribe();
query.dispose();

The operation name is the --name alias plus the endpoint name. Use your own generated names and types. The web SDK guide covers caching and authentication.

Verify

Call the API directly from any terminal:

Another terminal
curl -fsS http://127.0.0.1:8083/greet/Ada
Expected response
{"message":"Hello, Ada!"}

Then check:

  • a name longer than 40 characters returns 422, both directly and in the page;
  • missing or invalid input shows a browser-visible error;
  • base URL, CORS allowlist, credentials where used, loading and empty states, and cache privacy behave as intended;
  • a fresh browser session still starts correctly (a stored cache must not hide a startup failure).

Use contract tests and mocks for declared behavior during frontend work. They complement the real request; a mock does not prove the FastAPI handler's behavior.

Security and deployment

  • The example API is public and stateless, with no login or database. Add your application's identity and data policies separately.
  • Local CORS allows only http://127.0.0.1:5175 and http://localhost:5175, method GET, and the headers content-type, traceparent and x-axiom-cache-mode, which the web runtime sends.
  • Extraction and contract builds do not deploy the API or enforce the contract around each handler. Use a server adapter when you need that boundary.
  • For release review, keep the source, contract, dependency lock, test output and semantic diff together.

Clean up

Stop both servers with Ctrl-C. Delete the example directory to remove its virtual environment, node_modules, generated SDK and built contract.

Next steps