At the end of Part 3, we had a working RAG pipeline sitting entirely inside SQL Database in Fabric — embeddings stored as native VECTOR columns, similarity search running through `VECTOR_DISTANCE`, and the whole thing wrapped in a stored procedure, `find_products_api`, that I said was "ready to sit behind a GraphQL API." Time to make good on that.
This post is short and to the point: take the stored procedure we already built and expose it as a GraphQL endpoint using Microsoft Fabric's API for GraphQL — no separate API layer, no custom resolvers, no server code.
Let's get into it.
## Task 1: Creating the GraphQL API item
Back in the Fabric workspace, open the same SQL database you've been working in for Part 3 (the one with `SalesLT.Product`, the `embeddings` and `chunk` columns, and the `find_products` / `find_products_api` procedures already in place).
Click **New API for GraphQL** on the toolbar.
In the **New API for GraphQL** dialog, use the **Name** field to name the API — I went with `find_products_chat_api`. Click **Create**.

At this point the API item exists but isn't wired to anything yet. That's the next step.
## Task 2: Choosing what to expose
You'll land on a results screen listing the database objects available to expose. Find `find_products_api` in the list — hover over it with your pointer to confirm you've got the right one; the preview section should read **"Preview data: dbo.find_products_api"**.
Select it, then click **Load**.
Fabric inspects the procedure's `WITH RESULT SETS` definition and generates the GraphQL schema from it automatically — the eight columns we defined back in Part 3 (`product_name`, `product_color`, `category_name`, `model_name`, `product_description`, `list_price`, `product_weight`, `distance`) become the fields of the returned type. This is exactly why we bothered wrapping `find_products` in `find_products_api` with an explicit result shape in the first place — GraphQL needs a strongly typed schema to generate against, and `sp_invoke_external_rest_endpoint`'s underlying output isn't predictable enough on its own.
## Task 3: Query or mutation?
Worth a quick note on this, because it trips people up. Fabric's GraphQL API decides between a **query** and a **mutation** based on what the stored procedure does, not what you call it:
- If the procedure **returns a result set**, it's exposed as a **query**.
- If the procedure **modifies data** (inserts, updates, deletes), it's exposed as a **mutation**.
`find_products_api` only reads and returns rows — it never writes anything — so it lands on the **query** side of the generated schema, as `executefind_products_api`. If you'd built a companion procedure that logged each search term to an audit table, that one would show up as a mutation instead.
## Task 4: Calling it
You'll now be on the GraphQL query editor page. Paste this in and run it:
```graphql
query {
executefind_products_api(text: "I am looking for a red bike") {
product_name
product_color
category_name
model_name
product_description
list_price
product_weight
distance
}
}
```
Behind that single call, Fabric is doing everything Part 3 did by hand: generating an embedding for the search text through the Azure OpenAI credential, running `VECTOR_DISTANCE` against every product's stored vector, filtering by the similarity threshold, and returning the top matches — sorted by distance, shaped exactly the way `WITH RESULT SETS` defined. The client asking for this data doesn't know or care that there's a vector search and an LLM call happening underneath. It just asked a GraphQL question and got back typed JSON.
Run the trail-seat query from Part 3 through the same field and you'll get the same semantic-leap result — foam-padded, shock-absorbing products surfacing for a search that never used those words — except now it's arriving over GraphQL instead of a raw `EXEC`.
The API is now something you could hand straight to an application developer — a typed, queryable endpoint for a RAG-backed product search, with no separate service to stand up or maintain.
## Task 5: Securing it
Two things worth setting up before you point a real application at this:
- **Authentication.** API for GraphQL in Fabric requires Microsoft Entra ID for client authentication — there's no anonymous access mode. Register the calling application (or service principal, for server-to-server scenarios) and grant it the **Run Queries and Mutations** permission on the API item.
- **Access to the underlying data source.** If the API is configured to use saved credentials, permission on the API item alone is enough. If it isn't, the calling identity also needs its own access path to the SQL database — either directly, or by being added as a workspace member with a role that covers both the GraphQL API item and the database.
Neither of these needs deep configuration for a demo, but they matter the moment this stops being a sandbox.
## Honest take
Part 3 was the interesting engineering — getting embeddings, vector storage, and similarity search to live natively inside T-SQL. Part 4, by comparison, was almost anticlimactic, and that's the point worth making: there was no API code to write. No Express server, no resolver functions, no manually mapping SQL columns to GraphQL types. I selected a stored procedure in a checkbox list and got a typed, queryable endpoint.
The query-vs-mutation split based on what the procedure actually *does* — rather than a naming convention I'd have to remember — is a nice bit of design too. It means the schema stays honest about side effects without me annotating anything.
If you've been following this series from Part 1, this is the arc: a plain SQL database, Copilot-assisted querying, a full RAG pipeline built from native VECTOR columns and `VECTOR_DISTANCE`, and now a GraphQL API sitting on top of all of it — every piece inside the same Fabric SQL database, no additional services provisioned along the way.
That's One SQL Anywhere, wrapped. Happy reading!