{"id":287196,"date":"2019-05-10T12:54:40","date_gmt":"2019-05-10T19:54:40","guid":{"rendered":"http:\/\/css-tricks.com\/?p=287196"},"modified":"2019-05-25T06:51:07","modified_gmt":"2019-05-25T13:51:07","slug":"deploying-a-client-side-rendered-create-react-app-to-microsoft-azure","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/deploying-a-client-side-rendered-create-react-app-to-microsoft-azure\/","title":{"rendered":"Deploying a Client-Side Rendered create-react-app to Microsoft Azure"},"content":{"rendered":"

Deploying a React app to Microsoft Azure is simple. Except that… it isn\u2019t. The devil is in the details. If you’re looking to deploy a create-react-app \u2014 or a similar style front-end JavaScript framework that requires pushState<\/code>-based routing \u2014 to Microsoft Azure, I believe this article will serve you well. We\u2019re going to try to avoid the headaches of client and server side routing reconciliation.<\/p>\n

First, a quick story.<\/p>\n

<\/p>\n

Back in 2016, when Donovan Brown, a Senior DevOps Program Manager at Microsoft, had given a “but it works on my machine speech”<\/a> at Microsoft Connect that year, I was still in my preliminary stages as a web developer. His talk was about micro-services and containers.<\/p>\n

[…] Gone are the days when your manager comes running into your office and she is frantic and she has found a bug. And no matter how hard I try, I can’t reproduce it and it works perfectly on my machine. She says: fine Donovan, then we are going to ship your machine because that is the only place where it works. But I like my machine, so I’m not going to let her ship it…<\/p><\/blockquote>\n

I had a similar sort of challenge, but it had to do with routing. I was working on a website with a React front end and ASP.NET Core back end, hosted as two separate projects that were deployed to Microsoft Azure. This meant we could deploy both apps separately and enjoy the benefits that comes with separation of concern. We also know who to git blame<\/code> if and when something goes wrong. But it had downsides as well, as front-end vs. back-end routing reconciliation was one of those downsides.<\/p>\n

One day I pushed some new code to our staging servers. I received a message shortly after telling me the website was failing on page refresh. It was throwing a 404 error. At first, I didn\u2019t think it was my responsibility to fix the error. It had to be some server configuration issue. Turns out I was both right and wrong. <\/p>\n

I was right to know it was a server configuration issue (though at the time, I didn\u2019t know it had to do with routing). I was wrong to deny it my responsibility. It was only after I went on a web searching rampage that I found a use case for deploying a create-react-app to Azure under the Deployment tab on the official documentation page<\/a>. <\/p>\n

Building React for production<\/h3>\n

When building a React app for production<\/a> (assuming we\u2019re are using create-react-app<\/a>), it\u2019s worth noting the folders that get generated. Running npm run build<\/code> will generate a build folder where an optimized static version of the application lives. To get the application on a live server, all we need do is feed the server the content of the build folder. If we were working on localhost<\/strong>, there is no live server<\/em> involved, so it is not always equivalent to having the application on a live server.<\/p>\n

Generally, the build folder will have this structure:<\/p>\n

\u2192 build\r\n  \u2192 static\r\n    \u2192 css\r\n      \u2192 css files\r\n    \u2192 js\r\n      \u2192 js files\r\n    \u2192 media\r\n      \u2192 media files\r\n  \u2192 index.html\r\n  \u2192 other files...<\/code><\/pre>\n

Client-side routing with React Router<\/h3>\n

React Router uses the HTML5 pushState<\/code> history API<\/a> internally. What pushState<\/code> does is quite interesting. For example, navigating (or using Link<\/a> in react router) from the page https:\/\/css-tricks.com<\/code> to the page https:\/\/css-tricks.com\/archives\/<\/code> will cause the URL bar to display https:\/\/css-tricks.com\/archives\/<\/code> but won’t cause the browser to load the page \/archives<\/code> or even check that it exists. Couple this with the component-based model of React, it becomes a thing to change routes while displaying different pages based on those routes \u2014 without the all-seeing eye of the server trying to serve a page in its own directory. What happens, then, when we introduce servers by pushing the code to a live server<\/em>? The docs tell it better<\/a>:<\/p>\n

If you use routers that use the HTML5 pushState history API under the hood (for example, React Router with browserHistory), many static file servers will fail. For example, if you used React Router with a route for \/todos\/42, the development server will respond to localhost:3000\/todos\/42 properly, but an Express serving a production build as above will not. This is because when there is a fresh page load for a \/todos\/42, the server looks for the file build\/todos\/42 and does not find it. The server needs to be configured to respond to a request to \/todos\/42 by serving index.html.<\/p><\/blockquote>\n

Different servers require different configuration. Express, for example, requires this:<\/p>\n

app.get('*', (req, res) => {\r\n  res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));\r\n});<\/code><\/pre>\n

as documented in the create-react-app docs<\/a>. Keep in mind though, this assumes that we\u2019re hosting create-react-app at the server root<\/strong>, which is making use of a wildcard route (*<\/code>) that catches all route and respond to all route request by serving the index.html<\/code> file in the build folder which sits at the root of the server application. Also, this is tightly coupled with the back-end. If that\u2019s the case, we would most likely have this kind of folder structure (assuming the back-end is in NodeJS):<\/p>\n

\u2192 Server\r\n  \u2192 Client (this is where your react code goes)\r\n    \u2192 build (this is the build folder, after you npm run build)\r\n    \u2192 src\r\n    \u2192 node_modules\r\n    \u2192 package.json\r\n    \u2192 other front-end files and folders\r\n  \u2192 Other back-end files and folders<\/code><\/pre>\n

Since my front-end (create-react-app) and back-end (ASP.NET) were two different projects, serving static files by navigating the directory was sort of an impossibility<\/em>.<\/p>\n

In fact, since we are deploying a static app, we do not need the back-end. As Burke Holland put it: “Static” means that we aren\u2019t deploying any server code; just the front-end files.<\/p>\n

I keep mentioning ASP.NET here because during the course of my research, I figured configuring Azure required a configuration file in a wwwroot<\/code> folder and ASP.NET’s folder structure typically has a wwwroot<\/code> folder. Remember the application\u2019s back-end was in ASP.NET? But that\u2019s just about it. The wwwroot<\/code> folder seemed to be hidden somewhere on Azure. And I can\u2019t show you without deploying a create-react-app<\/code>. So let\u2019s go do that.<\/p><\/blockquote>\n

Getting started with App Services on Microsoft Azure<\/h3>\n

To get started, if you do not already have a Azure account, get a free trial<\/a> then head over to the Azure portal<\/a>.<\/p>\n

    \n
  1. Navigate to All services<\/kbd> \u2192 Web<\/kbd> \u2192 App Services<\/kbd>
    \nNavigating on the Azure portal from All services, to Web, to App services<\/p>\n
    \"\"<\/figure>\n<\/li>\n
  2. We want to add a new app, give it a name, subscription (should be free if you\u2019re on a free trial, or if you already have one), resource group (create one or use existing), then click on the Create button down at the bottom of the panel.
    \n
    \"\"
    Creating a new App service on the Azure portal.<\/figcaption><\/figure>\n<\/li>\n
  3. We should get a notification that the resource has been created. But it won\u2019t immediately show up, so hit “Refresh” \u2014 I have other resources, but the AzureReactDemo2 is what I\u2019m using here. You\u2019ll click on the name of your newly created app, which is AzureReactDemo2 in my case.
    \n
    \"\"
    Displaying all App Services on the Azure portal.<\/figcaption><\/figure>\n<\/li>\n
  4. The blade shows you information about your app, the navigation to the left has everything you need to manage your application (overview, activity log, deployment center…).<\/li>\n<\/ol>\n

    For example, the Deployment<\/strong> Center<\/strong> is where the app deployment is managed, Slots<\/strong> is where things like staging, production, test are managed. Configuration<\/strong> is where things like environmental variables, node versions and \u2014 an important one \u2014 Kudu<\/a> are managed<\/a>.<\/p>\n

    The overview screen shows a general view of the application Status, URL… Click on the URL to see the live site.<\/p>\n

    \"\"
    Showing the various parts of an App Service on the Azure CLI<\/abbr>.<\/figcaption><\/figure>\n

    The app is up and running!<\/p>\n

    \"\"
    Showing the default live page of an App Service.<\/figcaption><\/figure>\n

    What we\u2019ve done is create a new App Service<\/a>, but we have none of our code on Azure yet. As said earlier, all we need to do is to feed Azure the content of the build folder generated by building React for production, but we don\u2019t have one yet. So let\u2019s go local and get some React app.<\/p>\n

    Going local<\/h3>\n

    We need to create a new React app<\/a>, and install react-router<\/a> as a dependency.<\/p>\n

    npx create-react-app azure-react-demo\r\ncd azure-react-demo<\/code><\/pre>\n

    We also want to install react-router (react-router-dom<\/code>, actually)<\/p>\n

    npm i react-router-dom<\/code><\/pre>\n

    All things being equal, starting the app with npm start<\/code>, we should get the default page.<\/p>\n

    \"\"
    Showing the default page generated by React.<\/figcaption><\/figure>\n

    Because this will be about testing routes, I needed to make some pages. I\u2019ve modified my local version and uploaded it to GitHub. I\u2019m banking on the fact that you can find your way around react and react-router. Download a demo.<\/a><\/p>\n

    My folder looks like this:<\/p>\n

    \"\"
    Showing the folders and files in the modified create-react-app app.<\/figcaption><\/figure>\n

    The changed files have the following code:<\/p>\n

    \/\/ App.js\r\nimport React, { Component } from \"react\";\r\nimport \".\/App.css\";\r\nimport Home from \".\/pages\/Home\";\r\nimport Page1 from \".\/pages\/Page1\";\r\nimport Page2 from \".\/pages\/Page2\";\r\nimport { BrowserRouter as Router, Switch, Route } from \"react-router-dom\";\r\n\r\nclass App extends Component {\r\n  render() {\r\n    return (\r\n      <Router>\r\n        <Switch>\r\n          <Route exact path=\"\/\" component={Home} \/>\r\n          <Route path=\"\/page1\" component={Page1} \/>\r\n          <Route path=\"\/page2\" component={Page2} \/>\r\n        <\/Switch>\r\n      <\/Router>\r\n    );\r\n  }\r\n}\r\n\r\nexport default App;<\/code><\/pre>\n
    \/\/ Page1.js\r\nimport React from \"react\";\r\nimport { Link } from \"react-router-dom\";\r\n\r\nconst Page1 = () => {\r\n  return (\r\n    <div className=\"page page1\">\r\n      <div className=\"flagTop\" \/>\r\n      <div className=\"flagCenter\">\r\n        <h1 className=\"country\">Argentina (PAGE 1)<\/h1>\r\n        <div className=\"otherLinks\">\r\n          <Link to=\"\/page2\">Nigeria<\/Link>\r\n          <Link to=\"\/\">Home<\/Link>\r\n        <\/div>\r\n      <\/div>\r\n      <div className=\"flagBottom\" \/>\r\n    <\/div>\r\n  );\r\n};\r\n\r\nexport default Page1;<\/code><\/pre>\n
    \/\/ Page2.js\r\nimport React from \"react\";\r\nimport { Link } from \"react-router-dom\";\r\n\r\nconst Page2 = () => {\r\n  return (\r\n    <div className=\"page page2\">\r\n      <div className=\"flagTop\" \/>\r\n      <div className=\"flagCenter\">\r\n        <h1 className=\"country\">Nigeria (PAGE 2)<\/h1>\r\n        <div className=\"otherLinks\">\r\n          <Link to=\"\/page1\">Argentina<\/Link>\r\n          <Link to=\"\/\">Home<\/Link>\r\n        <\/div>\r\n      <\/div>\r\n      <div className=\"flagBottom\" \/>\r\n    <\/div>\r\n  );\r\n};\r\n\r\nexport default Page2;<\/code><\/pre>\n
    \/* App.css *\/\r\nhtml {\r\n  box-sizing: border-box;\r\n}\r\n\r\nbody {\r\n  margin: 0;\r\n}\r\n\r\n.page {\r\n  display: grid;\r\n  grid-template-rows: repeat(3, 1fr);\r\n  height: 100vh;\r\n}\r\n\r\n.page1 .flagTop,\r\n.page1 .flagBottom {\r\n  background-color: blue;\r\n}\r\n\r\n.page2 .flagTop,\r\n.page2 .flagBottom {\r\n  background-color: green;\r\n}\r\n\r\n.flagCenter {\r\n  display: flex;\r\n  align-items: center;\r\n  flex-direction: column;\r\n  justify-content: center;\r\n  text-align: center;\r\n}\r\n\r\n.page a {\r\n  border: 2px solid currentColor;\r\n  font-weight: bold;\r\n  margin: 0 30px;\r\n  padding: 5px;\r\n  text-decoration: none;\r\n  text-transform: uppercase;\r\n}\r\n\r\n.flags {\r\n  display: flex;\r\n  width: 100%;\r\n}\r\n\r\n.flags > .page {\r\n  flex: 1;\r\n}<\/code><\/pre>\n

    Running the app works locally, so the routes deliver when links<\/code> are clicked and even when the page is refreshed.<\/p>\n

    Step 1: Head to the Deployment Center<\/h4>\n

    On Azure, we need to go to the Deployment Center. There are quite a few options each with its pros and cons. We\u2019ll be using Local Git (which means your local git app straight directly to Azure) for source control, Kudu for Build Provider.<\/p>\n

    Remember to click continue or finish when you select an option, or else, the portal will just keep staring at you.<\/p>\n

    \"\"
    Showing Deployment Center on the Azure portal and choosing a source control as the first step in deploying a new App Service.<\/figcaption><\/figure>\n
    \"\"
    Showing the Build Provider section in the Deployment Center on Azure portal.<\/figcaption><\/figure>\n
    \"\"<\/figure>\n

    After the third step, Azure generates a local git repo for you. And it gives you a remote link to point your react app to.<\/p>\n

    One thing to note at this point. When you push, Azure will ask for your GitHub credentials. It is under the deployment tab. There are two: App and User. App credential will be specific to an app. User will be general to all the apps you as a user has Read\/Write access to. You can do without User Credentials and use App credentials, but I find that after a while, Azure stops asking for credentials and just tells me authentication failed automatically. I set a custom User Credentials. Either way, you should get past that.<\/p>\n

    \"\"
    Showing the Deployment Credentials for the App Service on Azure portal.<\/figcaption><\/figure>\n

    In the React app, after modification, we need to build for production. This is important because what we want to upload is the content of the build folder.<\/p>\n

    We need to tell Kudu what node engine we\u2019ll be using, or else, the build will most likely fail,
    \ndue to the reported fact that react-scripts<\/code> requires a node version higher than the default set on Azure. There are other ways to do that, but the simplest is to add a nodes engine in package.json<\/code>. I\u2019m using version 10.0 here. Unfortunately, we can\u2019t just add what we like, since Azure has Node versions it supports and the rest are unsupported. Check that with the CLI with the command: az webapp list-runtimes<\/code> <\/p>\n

    Add the preferred node version to the package.json<\/code> file, as in:<\/p>\n

    \"engines\": {\r\n  \"node\": \"10.0\"\r\n}<\/code><\/pre>\n
    \"\"
    Displaying a list of Azure runtimes in the Azure CLI<\/abbr>.<\/figcaption><\/figure>\n

    Step 2: Build the App<\/h4>\n

    To build the React app, let\u2019s run npm build<\/code> in the Terminal.<\/p>\n

    Step 3: Initialize the Git repo<\/h4>\n

    Navigate into the build folder and initialize a Git repo in there. The URL to clone the repo is in the overview page. Depending on what credentials you\u2019re using (App or User), it will be slightly different.<\/p>\n

    \"\"
    Showing the overview of the App Service on Azure and the Git clone URL.<\/figcaption><\/figure>\n
    git init\r\ngit add .\r\ngit commit -m \"Initial Commit\"\r\ngit remote add azure <git clone url>\r\ngit push azure master<\/code><\/pre>\n

    Now, visit the live app by using the URL on the overview page. As you can see, the app fails on \/page2<\/code> refresh. Looking at the network tab, a 404 is thrown because the page tried to be fetched from the server \u2014 with client-side routing, as we have already set up, the page shouldn\u2019t even be server fetched at all.<\/p>\n

    \"\"
    Showing the failed page request and the network tab to verify.<\/figcaption><\/figure>\n

    Configuring Azure to reconcile client and server side routing<\/h3>\n

    In the public folder, let\u2019s add a web.config<\/code> XML file with the following content:<\/p>\n

    <?xml version=\"1.0\"?>\r\n<configuration>\r\n<system.webServer>\r\n<rewrite>\r\n<rules>\r\n<rule name=\"React Routes\" stopProcessing=\"true\">\r\n<match url=\".*\" \/>\r\n<conditions logicalGrouping=\"MatchAll\">\r\n<add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" \/>\r\n<add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" negate=\"true\" \/>\r\n<add input=\"{REQUEST_URI}\" pattern=\"^\/(api)\" negate=\"true\" \/>\r\n<\/conditions>\r\n<action type=\"Rewrite\" url=\"\/\" \/>\r\n<\/rule>\r\n<\/rules>\r\n<\/rewrite>\r\n<\/system.webServer>\r\n<\/configuration><\/code><\/pre>\n

    I\u2019ve intentionally decided to not format the code snippet because XML is strict about that. If you miss the formatting, the file has no effect. You can download an XML formatter for your text editor. For VSCode, that would be the XML Tools plugin<\/a>.<\/p>\n

    \"\"
    Showing an XML formatter and an XML formatted file in VSCode.<\/figcaption><\/figure>\n

    The app can be built again at this point, although we\u2019ll lose the Git info in the build folder since the new build overrides the old build. That means it would have to be added again, then pushed.<\/p>\n

    Now the app works as shown below! Whew.<\/p>\n

    Conclusion<\/h3>\n

    There is a lot to Azure, as it can do a lot for you. That\u2019s nice because there are times when you need it to do something that seems super specific \u2014 as we\u2019ve seen here with client and server side routing reconciliation \u2014 and it already has your back.<\/p>\n

    That said, I\u2019ll leave you with a couple of related resources you can turn to as you look to deploying a React app to Azure.<\/p>\n