# Querying APIs for Composable Data

To query data from Zephyr there are two options: using the standardized GrapQl API, or setting up a querying endpoint through a sererless function. The second option is what will be covered in this chapter and has the following advantages.

## Advantages of building your API

We already talked about these advantages [before](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-apis-serverless-functions/pages/0UhoXOwNU3pBLxHjxSRf#id-2.-querying-and-composing-stored-data), now let's be more specific:

* Full customization: you can configure your endpoint to accept and return data in your preferred format, and aggregate it within the function as needed.
* Combine table and ledger queries within the same query/execution: for example, query an address from a table and use it as a parameter to retrieve a ledger entry.
* This level of flexibility allows for minimizing the amount of data stored in the database, along with costs. You only need to save key data in your tables, which can then be used to retrieve additional required information from the current state of the ledger as needed.
* Lastly, as previously discussed, working with Zephyr enables you to directly parse table, event, and ledger data within the program using Rust and the Soroban type system. This approach allows you to format the return data as desired, thereby reducing the workload on the caller.

## Building your custom API

The setup remains consistent: create a serverless function and handle a request. Within the function, you have the freedom to combine various functions learned so far to create a custom API that will return some specific data. Retrieve ledger/table data, aggregate and modify it, and return the result in your desired format. Here’s a simple example of how you can create an API that composes data previously stored in tables and ledger data:

<pre class="language-rust"><code class="lang-rust"><strong>#[no_mangle]
</strong>pub extern "C" fn get_signers_by_address() {
    let env = EnvClient::empty();
    let request: Request = env.read_request_body();
    let borrower: Borrower = env.read_filter().column_equal_to("borrowers", request.borrower).read().unwrap();
    let res: i128 = env.read_contract_entry_by_key([0;32], DataKey::Balance(env.from_scval(&#x26;borrower.address))).unwrap().unwrap();

    env.conclude(&#x26;res)
}
</code></pre>

In the request body, we have a `borrower` field of type `Borrower` (which of course, as seen [here](/zephyr-full-customization/learn/working-with-contract-custom-types.md), needs to be imported into the program). With the `read:filter()` function, we're accessing our tables, and more specifically the "borrowers" columns, where a list of `Borrower` objects is stored. We're taking just the borrower that matches our request.&#x20;

For the sake of simplicity we are omitting some repetitive passages, but make sure to have defined the `Request` type in your program beforehand (look at [General Concepts](/zephyr-full-customization/learn/custom-apis-serverless-functions/general-concepts.md#reading-the-request-body) for reference). Then, by using the `borrower.address` field of the object we just found, we read from the ledger the `Balance` entry for that address for a certain contract and return it.

As you can see, there's no limit to what can be done with Zephyr. This was just a simple demonstration of how a more complex data aggregation can be done to create an API with Zephyr, but for a more complex example, you can take a look at Blend's indexing program.

***

## Resources

* <https://blog.xycloo.com/blog/indexing-blend-ybx-pool>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mercurydata.app/zephyr-full-customization/learn/custom-apis-serverless-functions/querying-apis-for-composable-data.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
