React has quickly become one of the most popular JavaScript front end frameworks to build production grade applications. Whether it’s using ReactJS for a web app or React Native for a mobile app, this versatile framework is well suited to deliver large scale applications quickly and effectively. In this post, we will cover how to set up and host a ReactJS application from scratch. We will be setting up a new app and configuring DevOps pipelines for continuous integration and delivery to an Azure App Service.
What you will need
- Create React App: The fastest way to build up a new ReactJS project from scratch.
- This requires Node >= 8.10 and npm >= 5.6.
- GIT: Will be used to manage our repository from command line.
- Azure DevOps: To host the project repo and build pipelines for publishing.
- Azure Portal: to create the app service where the project will be deployed
- Visual Studio Code: To manage and edit the source code. Can be replaced with your IDE of choice.
Creating a Repo
We will first create a new Git repository in DevOps. Navigate to your DevOps site, and select the project you would like to work with. If you don’t have a repository setup already, select the Repos tab and click “New Repository” and create a Git project. Next, select the “Clone” button to clone the project to a local directory:
If you have VS Code installed, you can then select Clone in VS Code:
This will open up VS Code and prompt you to map the project to a new location and create an empty Git directory we will use for this project. After this is mapped, we will create a new React project.
Creating a React Project
To create the project, we will use a command line interface (CLI). Open a command prompt or terminal window, and navigate to the directory created in the previous step. Once there, enter the following command:
npx create-react-app react-azure-demo
Replace “react-azure-demo” with whatever name you want for this project. After setup is finished, type the following to start your project:
cd react-azure-demo
npm start
This will open up the default browser, and if successful, you will see the following:
Our First Commit
Now that we know our app works, let’s make our first commit to DevOps and make sure everything is connected. To do this, open up the app in VS Code and click the Source Control tab. Since this project is a fresh setup, you should see all the React project files as newly added items. Add a commit message like “initial commit”, click the checkmark to commit these changes, and push the changes to the repo.
Once successful, you should be able to see the new React project files in the repo on DevOps.
Setting up the web.config for Production
In most web applications you will be using navigation components to go between various pages on your site using client-side URLs. This means having pages like https://root.com/newPage. To do this in React, you could use something like React Router to manage links and URL routing within the app. However, React by nature is a single-page application and navigating to a URL that is not the root will result in a 404 error. To prevent this, we will need to add a web.config file that redirects all URLs back to index.html. The best way to accomplish this is by adding it as part of the source for our project, and to have it packaged as part of the React deployment process.
Open up the project in VS Code and create a new folder in the root of the project called “webconfig”. Inside, create two new files called “copyWebConfig.js” and “web.config”. The project should now look like this:
In copyWebConfig.js, add the following code:
const fs = require("fs-extra");
const webConfigPath = "./build/web.config";
if (fs.existsSync(webConfigPath)) {
fs.unlinkSync(webConfigPath);
}
fs.copySync("./webconfig/web.config", webConfigPath);
In web.config, we want all URLs that the user may type to point back to the root. To do this, add the following to our web.config file:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
</configuration>
The final step is to update the package.json file with an additional script. Append the “scripts” section with the following:
“postbuild”: “node webconfig/copyWebConfig.js”
This will make it so anytime that “npm build” is run, the web.config file will be copied over as part of the process. This will be happening when we create our first Azure Pipeline a little later. Be sure to commit and push these changes to DevOps just like we did before.
Creating the App Service on Azure
Now it’s time to setup the app service to that we will be deploying our app to. Go to the Azure Portal, and click the App Services icon. Next, select “Add” to create a new app service. Take note of the Resource Group that you use (or create a new one if needed) as you will be using this later. Also, you will need to choose and note down a unique the Name as well. Select “Code” under the Publish setting, and choose a Runtime stack and region of your choosing, then select “Review + Create”.
After it’s finished, you can click on the newly created app service to see the details.
If you click on the URL, you will be able to see it live:
Now that the app service is up and running, let’s build our service connection to DevOps and create a pipeline to deploy the React project.
Setting up a Service Connection
To connect to the App Service from DevOps, we will need to first setup a service connection. Start by opening up DevOps, and select “Project Settings”.
In the Pipelines section, select “Service Connections” and hit the “Create service connection” button.
We will select “Azure Resource Manager” as the type, and hit next.
Keep “Service Principle (automatic)” as the Authentication method.
Keep “Subscription” as the scope and make sure the correct one is selected. Then make sure to choose the same Resource Group that you created for the App Service above. We will also need to create and note a “Service Connection Name” that you will be using in the pipeline next. After you are finished, hit “Save” to create your service connection.
Building an Azure Pipeline for Deployment
In order to have continuous integration and continuous deployment (CI/CD) through DevOps, we will setup a new pipeline. This will make it so that whenever code is checked-in to the repo, our React project will automatically build and deploy the latest code to our App Service. To get started, head back over to DevOps and select the “Pipelines” tab:
Click “Create Pipeline” to get started and select Azure Repos Git:
Select the Repo that we made for this project.
And select “Node.js with React” to generate a new YAML file:
Next, edit the file with the following:
# Node.js with React
# Build a Node.js project that uses React.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
- master
pool:
vmImage: 'windows-latest'
# Set variables
variables:
directory: react-azure-demo
stage: development
serviceConnection: REACT_AZURE_DEMO
appServiceName: reactazuredemo
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- script:
npm install
displayName: 'npm install'
workingDirectory: $(directory)
- script:
set "REACT_APP_STAGE=$(stage)" && npm run build
displayName: 'npm build'
workingDirectory: $(directory)
- task: CopyFiles@2
displayName: 'Copy files'
inputs:
sourceFolder: '$(directory)/build'
Contents: '**/*'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
cleanTargetFolder: true
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Build Artifacts'
inputs:
pathtoPublish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
- task: AzureWebApp@1
displayName: 'Deploy to App Service'
inputs:
azureSubscription: '$(serviceConnection)'
appName: '$(appServiceName)'
appType: 'webApp'
package: '$(System.ArtifactsDirectory)/$(Build.BuildId).zip'
You will need to make sure the variables match up with your project settings. They include:
- “directory”: the name of the folder in the repo where your React project is stored. If it is the root, you can delete this variable and all instances of it within the code above.
- “stage”: this will add a custom environment variable that you can use in your React project code by accessing the variable “REACT_APP_STAGE”. If you have multiple environments like dev/test/prod, you can setup multiple pipelines setting this variable accordingly. This is useful to set different configuration settings or API URLs in your project based on the environment.
- “serviceConnection”: This is the service connection name we created in the previous step.
- “appServiceName”: This is the name of the app service we created earlier.
The “trigger” is currently set to “master” but can be updated to your project needs. What this means is that every time source code is pushed to the master branch, this pipeline will trigger automatically and begin the build, package, and deployment process to your app service. You will also be notified of the result should any step fail.
After this is updated, click “Save and run”, keep “Commit directly to the master branch”, and click “Save”. After this, you should see your pipeline begin running and complete in a few minutes.
You should now be able to see the project by navigating to the App Service URL that we used before.
Success!
Conclusion
We have walked through start to finish on creating a React project, housing it in a Git repo through DevOps, setting up a custom web.config that will be part of the React build process, creating a new Azure app service to host the app, setting up a service connection from DevOps to Azure, and building a pipeline to automatically build and deploy our project every time code is pushed by our development team. You have now built the foundation for your new web app that will change the world.
Happy hacking!
1 thought on “How to Deploy a React app to Azure using DevOps Pipelines”
Thank you for your thorough tutorial! Nothing I had come across had the YAML file correct but you did and I thank you!