React is the most popular framework for frontend web applications, and Azure Static Web Apps is the best place to host these applications on Azure. With Azure Static Web Apps’ built-in managed Azure Functions, you can build and host a full-stack web application using only Azure Static Web Apps (and there’s a free plan!). In this article, we’ll detail the steps to build out a full-stack React application with backend Functions that can access databases or backend APIs (GitHub repo).
Prerequisites
This tutorial article requires that you have Node.js, SWA CLI, Function core tools installed locally, an Azure subscription and a GitHub account. For this sample application, we’ll be using an existing Azure CosmosDB for NoSQL to demonstrate how we can access secured backend services such as databases or 3rd party APIs, using Static Web Apps’ managed functions. The schema of the Azure CosmosDB for NoSQL is as follows:
SWAStore (Database)
Items (Container)
Item (Item)
{
id: string
title: string
price: number
}
Sales (Container)
Item (Item)
{
id: string
date: string (date in ISO String format)
items : [
{
id: string
quantity: number
}
]
}
Getting started
We’ll get started by creating a React single-page application with Vite. This application will be a single-page application that will call our API managed Function and display the information, allowing us to manage the items and sales for an e-commerce site. The styling and certain functionality of this sample application will be omitted from this article, but are available in the source code. This GitHub repository contains the complete application for easy access.
We can create the React app and install the required dependencies with the following commands:
npm create vite@latest react-swa-full-stack-app -- --template react
cd react-swa-full-stack-app
npm install react-router-dom
With our React application created, we can also create a Functions project within the react-swa-full-stack-app directory. (This Functions project could be in any of the languages that Static Web Apps supports, in this case we specify JavaScript.) We’ll also create our various HTTP Trigger Functions that will provide our API endpoints.
func init api --worker-runtime javascript --model v3
cd api
func new --template "Http Trigger" --name Items
npm install /cosmos
To run our React and Functions development servers, we can use the SWA CLI, which will start up our React app and Function app. From the react-swa-full-stack-app directory, run the following:
swa start http://localhost:5173 --run "npm run dev" --api-location ./api
With our React app and Functions project scaffolded and working, we should be able to access ` http://localhost:4280/ and http://localhost:4280/api/Items). We can now edit this application to build our desired app. This tutorial will demonstrate how to create the Items editing page (the full application is available in the GitHub repository).
Edit the API
We’ll start by fetching our Items from our Azure CosmosDB database within our Items Function. We’ll add the key to our CosmosDB resource within /api/local.settings.json.
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"COSMOSDB_KEY": "<ENTER COSMOSDB KEY HERE>",
"COSMOSDB_ENDPOINT": "<ENTER COSMOSDB URL HERE>"
}
}
We’ll also update our /api/Items/function.json to allow “put” and “delete” methods, and allow parametrized routes (/api/Items/{id}).
In /api/Items/index.js, we'll create CRUD endpoints for our Items, with the following code snippet showing the GET endpoint and the POST, PUT, and DELETE implementations available in the source code.
const { CosmosClient } = require("@azure/cosmos");
const client = new CosmosClient({ endpoint: process.env["COSMOSDB_ENDPOINT"], key: process.env["COSMOSDB_KEY"] });
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
const database = client.database("SWAStore");
const container = database.container("Items");
if(req.method === "GET"){ //return all items
try {
const { resources } = await container.items.readAll().fetchAll();
context.res = {
status: 200,
body: resources
};
} catch (error) {
context.res = {
status: 500,
body: `Error retrieving items from the database: ${error.message}`
};
}
}
//[ POST, PUT AND DELETE ENDPOINTS OMITTED FOR SIMPLICITY, AVAILABLE IN SOURCE CODE ]
else {
context.res = {
status: 405,
body: "Method Not Allowed"
};
}
}
We could further iterate on this Azure Functions project to add Sales data but we’ll leave it as is for this tutorial. We have a GET endpoint that demonstrates access to a database secured by our CosmosDB key, indicating our backend component of our full-stack web application.
Edit the React app
We can now edit the React application frontend to make calls to our API Functions backend. We’ll start by adding the pages for the Items of our store. To setup our router, we’ll replace the contents of our main.jsx with the following:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import Items from './pages/Items/Items.jsx'
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
{
path: "/items",
element: <Items />,
}
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
We can now create the ItemsPage which will display all of the items in our store. Styling is omitted from this snippet but available in the source code.
import React, { useState, useEffect } from 'react';
const ItemsPage = () => {
const [items, setItems] = useState([]);
useEffect(() => {
fetch("/api/Items")
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
setItems(data);
})
.catch((error) => {
console.error("Error fetching data:", error);
});
}, []);
return (
<div>
<nav>
<div>Contoso</div>
</nav>
<div>
<div>
<div>Items</div>
</div>
</div>
<div>
<div>
<div>Item List</div>
<div>
{items.map((item) => (
<div key={item.id}>
<div>{item.id} - {item.title} - ${item.price}</div>
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default ItemsPage;
We now have a page that list our Items from our database. This Items page makes an API call to our managed function when it is loaded in the users' browser, demonstrating that our frontend is properly connected with our managed functions backend. We can further iterate on this to add full create, read, update, delete (CRUD) functionality for our Items and for our Sales page.
This is our completed React and Azure Functions application, featuring full-stack functionality and access to our database from our Azure Functions, and API calls from our React application to our Azure Functions. In the next step, we’ll deploy both these projects to a single Azure Static Web Apps resource!
Deploy the application
To configure our Static Web Apps project for deployment, we’ll add a staticwebapp.config.json file in the root of the project to specify the Node version required for our APIs and the navigation fallback for our React application:
{
"platform": {
"apiRuntime": "node:18"
},
"navigationFallback": {
"rewrite": "/index.html"
}
}
To deploy our project, start by creating a GitHub repository for the react-swa-full-stack-app project, which contains the React application in the root and the Azure Functions project in the api folder.
Once the GitHub repository has been created, navigate to the Azure Portal and create a new Static Web Apps resource. In the deployment details, you should specify that the app location is ‘/’, the API location is ‘api’, and that the output location is ‘dist’. This will create a GitHub Actions workflow file to build and deploy your application to your Static Web Apps resource.
Finally, set your database environment variables for your Static Web Apps resource. From your Azure Static Web Apps resource in the Azure Portal, navigate to Environment variables and set your COSMOSDB_KEY as well as your COSMOSDB_ENDPOINT.
We've now successfully built and deployed a full-stack React application with Azure Functions APIs to Azure Static Web Apps: https://kind-mud-0ff8cb503.4.azurestaticapps.net/
Conclusion
In this article, we demonstrated how we can build a full-stack application using React, Azure Functions, and CosmosDB! We deployed our React application and our Functions to Azure Static Web Apps, leveraging Static Web Apps’ built-in managed functions for simpler deployment and management across environments. We can further iterate on this sample application, by leveraging Static Web Apps’ authentication and authorization, as well as preview environments.
Get started deploying full-stack web apps for free on Azure with Azure Static Web Apps!