---
name: azion-query-a-database-from-a-function
description: >-
  Read rows from SQL Database inside a function with the azion:sql runtime module, and return them in the response the function sends.
---

# Query a database from a function

You read rows from a database in [SQL Database](/en/documentation/platform/sql-database/) inside a [function](/en/documentation/platform/functions/), during the request the function handles. The `azion:sql` runtime module opens the connection and runs the SQL.

This page carries one worked function. For every method the module exposes, refer to [SQL Database API](/en/documentation/devtools/runtime/api-reference/sql-database/).

---

## Prerequisites

- An application to instantiate the function on. Refer to [Instantiate a function on an application](/en/documentation/guides/application-development/getting-started/instantiate-functions/).
- A [personal token](/en/documentation/guides/platform/account-and-billing/personal-tokens/) to authorize the API requests.
- The **Edit SQL Database** permission, which grants permission to create and edit databases and their data through the Azion API. Refer to [Teams Permissions](/en/documentation/fundamentals/teams-permissions/).
- SQL Database enabled on your account. The product is in Preview and is not enabled by default, so request access through [Technical Support](/en/documentation/support/).

---

## Create the database and the table

The function reads a table that already exists, so the two calls below are the shortest path to one. To create the database and fill it with rows:

1. **Create the database**

   ```bash
   curl --location --request POST 'https://api.azion.com/v4/workspace/sql/databases' \
   --header 'Accept: application/json' \
   --header 'Content-Type: application/json' \
   --header 'Authorization: Token [TOKEN VALUE]' \
   --data '{"name":"mydatabase"}'
   ```

   The API answers with HTTP `202` and returns the identifier the next call takes in its path:

   ```json
   {
     "state": "pending",
     "data": {
       "id": 1234,
       "name": "mydatabase",
       "status": "creating",
       "active": true,
       "last_modified": "2026-01-01T12:00:00.000000Z",
       "last_editor": "user@example.com",
       "product_version": "1.0"
     }
   }
   ```

   Provisioning takes roughly 15 seconds. Send `GET /databases/{database_id}` until `status` reads `created`.

2. **Create the table and insert the rows**

   ```bash
   curl --location --request POST 'https://api.azion.com/v4/workspace/sql/databases/<database-id>/query' \
   --header 'Accept: application/json' \
   --header 'Content-Type: application/json' \
   --header 'Authorization: Token [TOKEN VALUE]' \
   --data '{"statements":["CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);","INSERT INTO users VALUES (1, '\''user 1'\'');","INSERT INTO users VALUES (2, '\''user 2'\'');","INSERT INTO users VALUES (3, '\''user 3'\'');"]}'
   ```

   The API answers with HTTP `200` and `"state": "executed"`, and `data` carries one entry per statement, in the order you sent them.

The database `mydatabase` holds a `users` table with three rows. The name you set here is the name the function passes to `Database.open`.

> **Note**
>
> A statement that fails does not fail the request. The call still answers HTTP `200`, and the entry for that statement carries `error` in place of `results`, so read `data[].error` before you treat the call as done.

---

## Write the function

`Database.open` opens a connection to the read replica of the database, so a function reads the data and does not write to it. The handler below answers a `GET` request with the contents of `users`:

```javascript
import { Database } from "azion:sql";

async function db_query() {
  let connection = await Database.open("mydatabase");
  let rows = await connection.query("select * from users");
  let column_count = rows.columnCount();
  let column_names = [];
  for (let i = 0; i < column_count; i++) {
    column_names.push(rows.columnName(i));
  }
  let response_lines = [];
  response_lines.push(column_names.join("|"));
  let row = await rows.next();
  while (row) {
    let row_items = [];
    for (let i = 0; i < column_count; i++) {
      row_items.push(row.getString(i));
    }
    response_lines.push(row_items.join("|"));
    row = await rows.next();
  }
  const response_text = response_lines.join("\n");
  return response_text;
}

async function handle_request(request) {
  if (request.method != "GET") {
    return new Response("Method not allowed", { status: 405 });
  }
  try {
    return new Response(await db_query());
  } catch (e) {
    console.log(e.message, e.stack);
    return new Response(e.message, { status: 500 });
  }
}

addEventListener("fetch", (event) =>
  event.respondWith(handle_request(event.request))
);
```

`connection.query` returns a rows handle. `rows.columnCount()` and `rows.columnName(i)` name the columns, `rows.next()` advances one row and returns a falsy value once the result set is exhausted, and `row.getString(i)` reads a cell by its position. The sample joins the cells of a row with `|` and the rows with a line break, then sends the text as the response body. A failure inside the module is logged with `e.message` and `e.stack` and returned as HTTP `500`.

> **Note**
>
> The sample reads data, so it answers `GET` and rejects every other method with `405`. Change the guard to the methods your own logic serves.

---

## Instantiate and run the function

A function runs only after it is bound to an application and a rule on that application selects it. To run the function and read the rows:

1. **Create the function in Azion Console**

   Access [Azion Console](https://console.azion.com/) and create a function that carries the code above. Refer to [Functions](/en/documentation/platform/functions/).

2. **Instantiate the function on an application**

   Bind the function to the application that serves your domain, then add the rule that runs the instance. Refer to [Instantiate a function on an application](/en/documentation/guides/application-development/getting-started/instantiate-functions/).

3. **Request the application**

   ```bash
   curl 'https://<your-application-domain>/'
   ```

The response carries the column names on the first line and one line per row of `users`:

```text
id|name
1|user 1
2|user 2
3|user 3
```

---

## Next steps

- [SQL Database API](/en/documentation/devtools/runtime/api-reference/sql-database.md): Every method the azion:sql module exposes to a function, with its signature.
- [Create tables and query data](/en/documentation/guides/application-development/data/create-tables-sql-database.md): Define the tables the function reads, insert rows, and read them back with SQL.
- [Instantiate a function on an application](/en/documentation/guides/application-development/getting-started/instantiate-functions.md): Bind the function to an application, name the instance, and pass its Args in JSON.
- [Databases and queries](/en/documentation/platform/sql-database/databases-and-queries.md): Every field, operation, envelope, and error code of the SQL Database endpoints.
