Roadmap › React (with Hooks, React 19 & Next.js basics) › Module: Fundamentals
Lessons in this course (17)
6. Connecting a React app to SQL Server (through an API)
Intermediate★ Must knowLesson 6 of 17
A browser app never connects to SQL Server directly — it would expose the connection string and SQL Server doesn't speak HTTP. The pattern is: React → HTTP API (ASP.NET Core or Node.js) → SQL Server.
text
React (browser) ──fetch JSON──▶ ASP.NET Core Web API / Node Express ──ADO.NET/EF/Dapper or mssql──▶ SQL Server1. Backend (ASP.NET Core minimal API + Dapper):
csharp
// Program.cs
builder.Services.AddCors(o => o.AddPolicy("spa", p => p.WithOrigins("http://localhost:5173").AllowAnyHeader().AllowAnyMethod()));
var cs = builder.Configuration.GetConnectionString("Shop"); // appsettings.json → "Server=.;Database=Shop;Trusted_Connection=True;TrustServerCertificate=True"
var app = builder.Build();
app.UseCors("spa");
app.MapGet("/api/products", async () =>
{
await using var conn = new SqlConnection(cs);
return await conn.QueryAsync<ProductDto>("SELECT Id, Name, Price FROM dbo.Products ORDER BY Name");
});2. React side:
ts
// src/lib/apiClient.ts
const BASE = import.meta.env.VITE_API_URL ?? ""; // "" in dev when using the Vite proxy
export async function api<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${BASE}${path}`, { ...init, headers: { "Content-Type": "application/json", ...init?.headers } });
if (!res.ok) throw new Error(`API ${res.status}`);
return res.json() as Promise<T>;
}
// src/features/products/api.ts
import { api } from "../../lib/apiClient";
export type Product = { id: number; name: string; price: number };
export const getProducts = () => api<Product[]>("/api/products");
// src/features/products/useProducts.ts
import { useQuery } from "@tanstack/react-query";
import { getProducts } from "./api";
export const useProducts = () => useQuery({ queryKey: ["products"], queryFn: getProducts });
// src/features/products/ProductList.tsx
export default function ProductList() {
const { data, isLoading, error } = useProducts();
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Could not load products.</p>;
return <ul>{data!.map((p) => <li key={p.id}>{p.name} — ₹{p.price}</li>)}</ul>;
}ts
// vite.config.ts — dev proxy avoids CORS during development
export default defineConfig({
plugins: [react()],
server: { proxy: { "/api": { target: "https://localhost:7001", changeOrigin: true, secure: false } } },
});Checklist: CORS on the API for the SPA origin, HTTPS, JWT/cookie auth, the connection string only on the server (appsettings/Key Vault), parameterised SQL on the server, and VITE_ variables contain no secrets (they are public).