# Microsoft SQL Server

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 50 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Microsoft SQL Server by building a REST API layer on top of your database — an Azure Function using the mssql Node.js library is the natural fit — then calling that API from FlutterFlow's API Calls panel. FlutterFlow cannot open a direct TDS connection to port 1433 from a client app. All connection strings, Windows Authentication credentials, and SQL Server passwords must stay on the API host, never in FlutterFlow or Dart.

## Connecting FlutterFlow to SQL Server Through a REST API Layer

Microsoft SQL Server communicates using the TDS (Tabular Data Stream) protocol over port 1433. Web browsers and mobile apps — including apps built with FlutterFlow — cannot open raw TCP sockets to this port. This is not a FlutterFlow limitation; it applies to all client-side frameworks. The solution is a thin REST API layer that sits between FlutterFlow and SQL Server, accepts HTTP requests, runs parameterized T-SQL queries, and returns JSON. Unlike Retool, which runs a server-side proxy for you automatically, FlutterFlow requires you to build and host this layer yourself.

The most natural choice for a SQL Server REST wrapper is an Azure Function using the mssql Node.js package (or pyodbc in Python). Azure Functions have native affinity with SQL Server because they are both Microsoft products: managed identity authentication, private VNet connectivity, and Azure SQL's serverless tier all work out of the box. Alternatively, if your company already has an ASP.NET or Node.js API exposing SQL Server data, you can point FlutterFlow directly at those existing endpoints.

On pricing, SQL Server Express Edition is free with a 10 GB database size limit — suitable for many small apps. Azure SQL's serverless tier starts at around $5 per month and scales to zero when idle (verify current pricing on the Azure portal). Azure Functions Consumption plan gives you 1 million free invocations per month. Windows Authentication and stored procedures live entirely behind the REST layer, which means your FlutterFlow app can call enterprise SQL Server instances without any changes to your database schema or stored procedures.

## Before you start

- A FlutterFlow project created and open in the browser
- A SQL Server instance accessible from the internet or from Azure (Azure SQL, on-premises with firewall rule, or RDS SQL Server)
- An Azure account to host the Azure Function REST wrapper (free tier is sufficient for prototyping)
- The SQL Server connection string (server name, database name, username, password) ready to paste into Azure Function settings
- Basic familiarity with FlutterFlow's API Calls panel

## Step-by-step guide

### 1. Build the REST wrapper: Azure Function with mssql

The first step happens entirely outside FlutterFlow. You need a publicly accessible HTTPS endpoint that accepts HTTP requests and runs T-SQL against your SQL Server. The Azure Portal's Function App interface lets you create this without the CLI. Go to portal.azure.com → Function Apps → Create → choose a Consumption (Serverless) plan with Node.js 18+ runtime. Name it something like 'sqlserver-api'. Once deployed, go to Functions → Create → HTTP trigger → name it 'query' → Authorization level: Function.

In the Function editor, paste the mssql-based handler below. The connection string goes into Application Settings (Configuration → New application setting), never in source code. Add a setting named SQL_CONNECTION_STRING with the value: Server=yourserver.database.windows.net;Database=yourdb;User Id=youruser;Password=yourpassword;Encrypt=true;TrustServerCertificate=false. For Windows Authentication on an on-premises SQL Server, use an App Service Managed Identity or a service account — the connection string format changes, but the principle of keeping it in Application Settings remains the same.

For stored procedures, replace pool.request().query() with pool.request().input('param', sql.VarChar, value).execute('sp_YourProcedure'). For large result sets, implement server-side pagination with OFFSET/FETCH NEXT in your T-SQL — never return unbounded result sets to a mobile client. Enable CORS on the Function App (API → CORS → add https://app.flutterflow.io and your published FlutterFlow domain) for web build compatibility.

```
// Azure Function (Node.js) — SQL Server REST wrapper
const sql = require('mssql');

let pool;
async function getPool() {
  if (!pool) {
    pool = await sql.connect(process.env.SQL_CONNECTION_STRING);
  }
  return pool;
}

module.exports = async function (context, req) {
  const p = await getPool();
  const operation = req.query.op || 'list';

  let result;
  if (operation === 'list') {
    const page = parseInt(req.query.page || '1');
    const pageSize = 20;
    const offset = (page - 1) * pageSize;
    result = await p.request()
      .input('offset', sql.Int, offset)
      .input('pageSize', sql.Int, pageSize)
      .query('SELECT * FROM Items ORDER BY Id OFFSET @offset ROWS FETCH NEXT @pageSize ROWS ONLY');
  } else if (operation === 'create') {
    const body = req.body;
    result = await p.request()
      .input('Name', sql.VarChar, body.name)
      .input('Value', sql.VarChar, body.value)
      .query('INSERT INTO Items (Name, Value) OUTPUT INSERTED.* VALUES (@Name, @Value)');
  }

  context.res = {
    headers: { 'Content-Type': 'application/json' },
    body: { items: result.recordset }
  };
};
```

**Expected result:** Your Azure Function is live at https://{app-name}.azurewebsites.net/api/query. A GET request with ?op=list returns JSON rows from SQL Server. You can test this in your browser or Postman.

### 2. Store the connection string and secure the Function endpoint

Your SQL Server connection string contains the server address, database name, username, and password. These must never appear in your FlutterFlow project or Dart code. In the Azure Portal, navigate to your Function App → Configuration → Application settings → + New application setting. Name it SQL_CONNECTION_STRING and paste the full connection string as the value. Click Save. The Function reads this at runtime as process.env.SQL_CONNECTION_STRING — the Azure Portal interface masks the value after saving, and it is encrypted at rest.

For the Function's HTTP auth, go to Functions → your function → Function Keys → default key. Copy the host key value. This key must be sent by FlutterFlow in the 'x-functions-key' header with every request. It is not a database credential — it is a gateway key that controls who can invoke the Function. You can store this key in FlutterFlow's App Values (Settings → App Values → + Add constant), which keeps it out of individual widget bindings and makes it easy to update. If you need per-user authentication (so users only see their own data), add a bearer token check inside the Function: FlutterFlow passes the user's Firebase or Supabase auth token, the Function verifies it, and filters the SQL query by user ID accordingly.

**Expected result:** The SQL_CONNECTION_STRING is saved in Function App configuration (not visible in source code). Requests to the Function without the x-functions-key header return 401.

### 3. Add API Calls in FlutterFlow per SQL operation

With the Azure Function running, open your FlutterFlow project and click API Calls in the left navigation panel. Click + Add → Create API Group. Name it 'SQL Server API' and set the Base URL to https://{your-app}.azurewebsites.net/api. Under Headers, add 'x-functions-key' with your Function host key value (or a reference to the App Values constant you created), and 'Content-Type: application/json'.

Now add individual API Calls inside the group. Click + Add API Call → name it 'ListItems' → Method: GET → Path: /query?op=list. Add a variable 'page' with a default value of '1', and update the path to /query?op=list&page={{page}}. For pagination to work, users can increment the page variable to load the next page of results. Click the Variables tab to define the 'page' variable, then switch to Response & Test and paste a sample response (e.g., {"items": [{"Id": 1, "Name": "Widget A", "Value": "Blue"}]}) → Generate JSON Paths.

Create a second API Call 'CreateItem' → Method: POST → Path: /query?op=create → Body: {"name": "{{itemName}}", "value": "{{itemValue}}"}. Add the corresponding variables in the Variables tab. Repeat for update and delete operations, each as a separate API Call. This one-call-per-operation pattern keeps your FlutterFlow action flows readable and makes it easy to add error handling per operation.

```
// Sample API Call config (JSON representation)
{
  "group_name": "SQL Server API",
  "base_url": "https://myapp.azurewebsites.net/api",
  "headers": {
    "x-functions-key": "{{functionKey}}",
    "Content-Type": "application/json"
  },
  "calls": [
    {
      "name": "ListItems",
      "method": "GET",
      "path": "/query?op=list&page={{page}}",
      "variables": ["page"]
    },
    {
      "name": "CreateItem",
      "method": "POST",
      "path": "/query?op=create",
      "body": { "name": "{{itemName}}", "value": "{{itemValue}}" }
    }
  ]
}
```

**Expected result:** The API Calls panel shows your 'SQL Server API' group. ListItems returns SQL data in the test tab. CreateItem returns the newly inserted row with its generated Id.

### 4. Map T-SQL results to Data Types and bind to ListViews

SQL Server returns results in PascalCase by default — columns named Id, Name, Quantity, CreatedDate. FlutterFlow's JSON path matching is case-sensitive, so your paths must match the actual column names exactly. After generating JSON Paths in the API Call's Response & Test tab, go to Data Types in the left navigation panel → + Add to create a new Data Type. Name it 'SqlItem' and add fields: id (Integer), name (String), value (String), createdDate (String). Map each field to its JSON path: $.items[0].Id for id, $.items[0].Name for name, and so on.

Back in your API Call → Response & Test tab, set the API Response type to 'List' and select your SqlItem Data Type. FlutterFlow will now treat the response as a typed list. Drag a ListView onto your canvas, set its Data Source to the ListItems API Call, and FlutterFlow will auto-suggest Text widgets pre-bound to sqlItem.name, sqlItem.value, and sqlItem.createdDate.

For detail pages, use a Dynamic Route: in your Page settings, add a URL parameter 'itemId'. When a user taps a row, pass the item's Id to the route. On the detail page, make a GetItemById API Call (GET /query?op=get&id={{itemId}}) that returns a single record, and bind its fields to the widget tree. This avoids passing large objects between pages and keeps your app responsive.

**Expected result:** Your ListView displays rows from SQL Server with correct field values. Tapping a row navigates to the detail page with the correct item data loaded from a separate GetItemById call.

### 5. Handle cold starts and test on device

Azure Functions on the Consumption plan experience cold starts — when the function has been idle for several minutes, the first request takes 2–5 additional seconds to spin up a new instance. This is particularly noticeable with SQL Server because the mssql connection pooling also needs to initialize. Mitigate this in FlutterFlow by showing a loading state on every screen that makes API calls: use a page-level boolean state variable isFetching, set it to true before the API Call action, and set it to false in both success and error branches. Conditionally show a CircularProgressIndicator widget while isFetching is true.

For production apps where cold start latency is unacceptable, consider the Azure Functions Premium plan (Elastic Premium), which keeps at least one instance permanently warm. Alternatively, keep the connection pool warm by calling a lightweight health-check endpoint every few minutes from a background isolate — though this approach is more complex.

For testing, use FlutterFlow's Test mode for initial checks, but run a full device test (Run mode → build APK or use Android emulator) for comprehensive validation. CORS issues often only surface in web-mode testing. On device, check that the 'x-functions-key' header is being sent correctly and that the SQL Server firewall allows inbound connections from the Azure Function's IP range (or use Azure Service Endpoints for private VNet connectivity). If you see 500 Internal Server Error responses, check the Function's Live Metrics in the Azure Portal for the actual error stack trace.

**Expected result:** The app shows a loading spinner on first open, then populates with SQL Server data within 2–5 seconds. Subsequent requests complete in under 500ms once the Function is warm.

## Best practices

- Never put SQL Server connection strings, Windows Authentication credentials, or SQL Server passwords in FlutterFlow variables, App Values, or Dart code — store them exclusively in Azure Function Application Settings.
- Use parameterized queries in the Function (p.request().input()) for every query that includes FlutterFlow-supplied values — this prevents SQL injection even in an internal API.
- Implement server-side pagination with OFFSET/FETCH NEXT in T-SQL — never return an unbounded result set to a mobile client.
- Add a loading indicator on every screen that calls the Azure Function — cold starts on the Consumption plan can add 2–5 seconds to the first request.
- Enable CORS on the Azure Function App before testing in FlutterFlow's web preview — this is the most common source of silent web-build failures.
- Create one API Call per CRUD operation in FlutterFlow (ListItems, GetItem, CreateItem, UpdateItem, DeleteItem) rather than using a single generic endpoint — this makes action flow logic cleaner and errors easier to trace.
- If your SQL Server uses Windows Authentication, configure an Azure Managed Identity for the Function App and grant it SQL Server access — this eliminates plaintext passwords from the configuration entirely.
- RapidDev's team builds Azure Function REST wrappers for SQL Server every week — if setting up the mssql layer feels overwhelming, a free scoping call at rapidevelopers.com/contact can save you days of work.

## Use cases

### Enterprise CRM mobile app on top of an existing SQL Server database

A company has years of customer data in SQL Server and wants a mobile app for the sales team. An Azure Function exposes GET /customers, GET /customers/{id}, and POST /activity REST endpoints that run T-SQL against the existing tables. FlutterFlow API Calls hit these endpoints and display customer records in a searchable ListView.

Prompt example:

```
Build a CRM mobile app where sales reps can browse customer accounts, view contact history, and log new activities — all reading and writing to an existing SQL Server database through an Azure Functions API.
```

### Inventory management app with stored procedure integration

A warehouse management app where staff scan barcodes to update stock levels. The Azure Function calls SQL Server stored procedures (EXEC sp_UpdateInventory @itemId, @quantity) and returns the updated record as JSON. FlutterFlow shows a confirmation dialog after each successful scan.

Prompt example:

```
Create a barcode scanning inventory app that calls SQL Server stored procedures to update stock counts and view low-inventory alerts on a mobile device.
```

### Reporting dashboard reading SQL Server analytics views

A management team needs a mobile dashboard with daily sales figures and KPI charts. The Azure Function queries SQL Server analytical views (aggregated revenue, regional breakdowns) and returns JSON that FlutterFlow binds to Recharts-style chart widgets. The Function caches responses for 5 minutes to keep query costs low.

Prompt example:

```
Build a sales reporting dashboard app that displays daily revenue, top products, and regional performance charts — reading from SQL Server analytics views through an Azure Functions API.
```

## Troubleshooting

### XMLHttpRequest error in FlutterFlow web build when calling the Azure Function

Cause: CORS is not enabled on the Azure Function App, so the browser rejects the response from a different origin.

Solution: In the Azure Portal, go to your Function App → API → CORS. Add https://app.flutterflow.io (for the FlutterFlow editor preview) and your published FlutterFlow app domain. Save the settings and wait 1–2 minutes. Note that native iOS and Android builds do not enforce CORS — this error only appears in web-mode testing.

### 401 Unauthorized response when calling the Function from FlutterFlow

Cause: The 'x-functions-key' header is missing or contains an outdated key value.

Solution: In the Azure Portal, navigate to Function App → Functions → your function → Function Keys → default. Copy the current key. In FlutterFlow's API Calls panel, update the 'x-functions-key' header value in the API Group (or update the App Values constant). Make sure the header name is exactly 'x-functions-key' in lowercase with hyphens.

### SQL Server error: 'Cannot open server requested by the login' or 'Login failed for user'

Cause: The connection string in the Azure Function's Application Settings is incorrect — wrong server name, database name, username, or password. SQL Server on Azure SQL also requires Encrypt=true in the connection string.

Solution: In the Azure Portal, check Function App → Configuration → Application settings → SQL_CONNECTION_STRING. Verify the server name ends with .database.windows.net (for Azure SQL), the database name is correct, and the username/password match a SQL Server login with appropriate permissions. Add Encrypt=true;TrustServerCertificate=false to the connection string for Azure SQL. Test the connection string using SQL Server Management Studio or Azure Data Studio before deploying.

### FlutterFlow widgets show empty data even though the Function returns 200

Cause: SQL Server column names are PascalCase (e.g., ProductName), but the JSON path in FlutterFlow uses a different casing (e.g., $.items[0].productName), causing the mapping to silently fail.

Solution: Open the API Call → Response & Test tab and paste a real response from the Function. Click Generate JSON Paths and verify the capitalization matches exactly. Update the Data Type field mappings to use the correct case. Alternatively, update the SQL query in the Function to alias columns with lowercase names: SELECT ProductName AS productName.

## Frequently asked questions

### Can FlutterFlow connect directly to SQL Server without a REST API?

No. SQL Server communicates over the TDS protocol on port 1433, which cannot be reached from a Flutter client app (mobile or web). This is not a FlutterFlow limitation — all client-side frameworks have this restriction. You must add a REST API layer (Azure Function, ASP.NET endpoint, or any HTTP server) that FlutterFlow calls via standard HTTPS requests.

### Can I use Windows Authentication with FlutterFlow?

Yes, but only on the server side. The Azure Function (or whichever REST wrapper you use) connects to SQL Server using Windows Authentication via a service account or Managed Identity. FlutterFlow never knows about Windows Authentication — it only sends HTTP requests with a bearer token to the Function, which handles the SQL Server connection internally.

### Can I call SQL Server stored procedures from FlutterFlow?

Yes, through the REST wrapper. In your Azure Function, use p.request().execute('sp_YourProcedure') instead of a raw query. The Function accepts HTTP parameters from FlutterFlow, passes them as inputs to the stored procedure, and returns the result set as JSON. FlutterFlow treats this exactly like any other API call.

### How do I prevent SQL injection if FlutterFlow sends user input to the Function?

Always use parameterized queries in the Function — never concatenate FlutterFlow variables into T-SQL strings. The mssql library's p.request().input('paramName', sql.VarChar, value).query('SELECT * WHERE Name = @paramName') pattern handles escaping automatically. Validate and sanitize input in the Function before passing it to the query, even for internal apps.

### What is the cheapest way to host a SQL Server for use with FlutterFlow?

SQL Server Express Edition is free and runs on any Windows Server or VM — but you need to manage the server yourself. Azure SQL Serverless tier starts at approximately $5 per month and scales to zero when idle, making it the most cost-effective managed option. For very low-traffic prototypes, the Azure SQL free offer (available on new Azure accounts) gives you a small database at no cost. Check Azure's current pricing page for the latest numbers.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/microsoft-sql-server
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/microsoft-sql-server
