# APIs

Source: /docs/reference/python/apis.md

An API handles synchronous request/response work and returns a JSON-serializable value. Define one synchronous `@vxfy_api` entrypoint per resource; helpers and tests can be separate modules. Import SDK declarations from `vortexify` and declare third-party dependencies in `requirements.in`.

The registered resource name, Python function name, and decorator route are distinct. The CLI and React SDK select APIs by registered resource name. Use [backend operations](/docs/building/backend-operations) for the project workflow.

## Entrypoint and inputs [#entrypoint-and-inputs]

```text
vxfy_api(path, *, engine="pandas", inputs=None, compute=None)
```

The route starts with `/`. Engines are pandas (default) and duckdb; PySpark is not
supported for APIs. Default inputs are empty, compute is None. There is no schedule
argument. The function must accept injected keyword arguments and return a
JSON-serializable value. Do not use async functions, generators, framework response
objects or HTTP status tuples as a replacement for the SDK return contract.

| Declaration | Runtime input |
| --- | --- |
| `Input(str/int/float/bool, required=True, default=None)` | Caller-supplied scalar |
| `Table("connector.sync")` | Synced tabular data |
| `Table("_upload.table_name")` | Uploaded tabular data |
| `TransformOutput("transform")` or `TransformOutput("transform.output")` | Published upstream output |
| `DatabaseTable("database.table")` | Read-only database table; one reference argument |
| `Database("database", tables=["orders"], mode="read_write")` | Scoped Postgres connection |
| `Secret("secret_name")` | Managed value resolved inside the runtime |

Declare resource references in code, not as caller-supplied credentials or table
bodies. Input keys name local arguments. Supported scalars are exactly str, int,
float, bool; defaults have the exact declared type. Optional scalars without a
default may be None. Database mode is read or read_write (default); operational
SELECT/INSERT/UPDATE/DELETE belongs here, schema changes belong in migrations.
Database inputs follow execution environment selection; see [database environments](/docs/reference/python/databases#environments-and-execution).

For pandas, each tabular input is a pandas DataFrame argument. For DuckDB, it is a
relation registered under its declaration key in injected `conn`. Receive `conn`
and non-tabular arguments; do not also request tabular DataFrame arguments. `conn`
is reserved. Scalars, secrets and Database connections are ordinary injected values.
Apply request-specific filters in the function; a tabular declaration selects the dataset to load.

```python
from vortexify import ApiCompute, Input, vxfy_api

@vxfy_api(
    "/normalize",
    inputs={"value": Input(str, default="Hello")},
    compute=ApiCompute(size="xsmall"),
)
def normalize(value: str) -> dict:
    normalized = value.strip()
    return {"value": normalized, "length": len(normalized)}
```

An alternative DuckDB API (one entrypoint per resource):

```python
from vortexify import Input, Table, vxfy_api

@vxfy_api(
    "/customer-revenue",
    engine="duckdb",
    inputs={"orders": Table("shopify.orders"), "minimum_amount": Input(float, default=0.0)},
)
def customer_revenue(conn, minimum_amount):
    rows = conn.execute(
        "SELECT customer_id, SUM(amount) AS revenue FROM orders "
        "WHERE amount >= ? GROUP BY customer_id ORDER BY customer_id",
        [minimum_amount],
    ).df()
    return {"customers": rows.to_dict("records")}
```

This assumes those resources and columns exist. Inspect them first. Parameterize
values; validate dynamic SQL identifiers against known schemas separately.

## Response contract [#response-contract]

Return JSON dictionaries, lists, scalar values or null. Convert DataFrames, DuckDB
results, cursors, timestamps, decimals and NumPy scalars explicitly. Normalize NaN, infinity, and missing values before returning them so the result can be transported as JSON. APIs return responses; they do not publish named
Parquet outputs like transforms. Keep expensive background work in transforms.
Keep authorization credentials out of browser inputs, responses, source and logs.

## Inspect a request in the platform [#inspect-a-request-in-the-platform]

Use **Playground → Run** to submit JSON scalar inputs against committed preview code. **History** selects an execution; its Output, Processing, Logs, and JSON views show the response and execution evidence. Prepare the runtime or run tests from Configure. Overview shows parsed upstream/downstream dependencies; Code can open a read-only saved version.

For a user-facing call, the React SDK returns your JSON response directly. Hosted preview and deployed calls use different version/publication selection, as described in [React configuration](/docs/reference/react/configuration#preview-lifecycle).

## Compute [#compute]

`ApiCompute(size=..., memory=..., storage=...)` describes requested runtime resources. See [compute presets](/docs/reference/python/compute-and-schedules#api-compute) for fields, units, and defaults. APIs have no schedule declaration.

## Tests and validation [#tests-and-validation]

Use `@vxfy_mocks` and `@vxfy_test(mocks=...)` for source-controlled mock tests. Optional `@vxfy_validate()` checks a JSON response; deployed API calls skip automatic validation. Required runtime checks belong in the API implementation. See [testing and validation](/docs/reference/python/testing-and-validation).

## Saved runtime workflow [#saved-runtime-workflow]

Commit the intended source. Prepare its runtime when needed, then run saved tests or invoke the API:

```sh
vortexify commit --type api --resource normalize
vortexify build api normalize --wait
vortexify test api normalize --wait
```

A compatible ready runtime can be reused by a newer committed source version. Prepare again when its dependency image/allocation is not ready. Runs and tests consume saved source; local edits are included after committing them. Dependency preparation, assertions, and live invocation are separate stages. APIs can perform external writes, so use mocks for those effects in tests. A manual preview does not disable writes. Keep the job ID to inspect results or resume waiting instead of submitting duplicate work.
