React on Azure App Service
Published: 2022-01-10 14:07:30

# Summary
In this article, I want to share the experiments of React app deployed on Azure App Service. The two patterns work correctly, **1) Root folder with node_modules**, **2) Build folder with Startup commands**. The startup command should be carefully chosen depending on your zip folder structure that is deployed to Azure App Service.

**Code samples**
- [azuredeploy.json](https://github.com/koheikawata/authApiSample/blob/main/arm-templates/azuredeploy.json)
- [react-pipeline.yml](https://github.com/koheikawata/authApiSample/blob/main/azure-devops/react/react-pipeline.yml)
- [frontend](https://github.com/koheikawata/authApiSample/tree/main/frontend)

**React on App Service deployment experiments**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhdgw8l51eonsiinn3g8.png)
 
# TOC
- [Successful configuration](#successful-configuration)
- [Experiments](#experiments)
  - [Responses](#responses)
  - [Run from files or ZIP package](#run)
  - [Deployed folder inside App Service](#deployed)
  - [Results: Root folder](#root-folder)
  - [Results: Build folder](#build-folder)

# Successful configuration <a name="successful-configuration"></a>
The contents below show how you configure React and Azure App Service to make it work correctly. 

## Uploaded folder <a name="uploaded-folder"></a>

`root` is React root folder. `build` is generated with `npm run build` for React app.

**root**
```
.
├── README.md
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── react-app-env.d.ts
│   ├── reportWebVitals.ts
│   └── setupTests.ts
└── tsconfig.json
```

**root + node_modules**

```
.
├── node_modules
├── README.md
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── react-app-env.d.ts
│   ├── reportWebVitals.ts
│   └── setupTests.ts
└── tsconfig.json
```

**build**
```
upload.zip
├── asset-manifest.json
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── robots.txt
└── static
    ├── css
    │   ├── main.073c9b0a.css
    │   └── main.073c9b0a.css.map
    ├── js
    │   ├── 787.9750ae9a.chunk.js
    │   ├── 787.9750ae9a.chunk.js.map
    │   ├── main.e0162b9f.js
    │   ├── main.e0162b9f.js.LICENSE.txt
    │   └── main.e0162b9f.js.map
    └── media
        └── logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg
```

## Startup command and WEBSITE_RUN_FROM_PACKAGE <a name="startup-command"></a>

You can see the sample [ARM template sample](https://github.com/koheikawata/authApiSample/blob/main/arm-templates/azuredeploy.json), whici sets up startup command `npm install -g serve && serve -s .` and appsettings `"WEBSITE_RUN_FROM_PACKAGE":"1"`. The reason behind this is discussed later in this article.

```
{
  "type": "Microsoft.Web/sites",
  "kind": "app",
  "properties": {
    "siteConfig": {
      "linuxFxVersion": "NODE|16-lts",
      "appCommandLine": "npm install -g serve && serve -s ."
    }
  }
},
{
  "type": "Microsoft.Web/sites/config",
  "properties": {
    "WEBSITE_RUN_FROM_PACKAGE":"1"
  }
}
```

# Experiments <a name="experiments"></a>
In this chapter, I am trying describe what trials I did and what were those results.

## Responses <a name="responses"></a>

**Application Error**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwp80ke1k0dgrbvkftjk.png)

**Hey, Node developers! (hostingstart.html)**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmfq9mai2zvgrnj3uzv5.png)

**404**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sobnlf7tg3cj5ruq6dn.png) 

**Success**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tguc32i2j4ma2k2i5xnv.png)

## Run from files or ZIP package <a name="run"></a>
When you set appsettings `WEBSITE_RUN_FROM_PACKAGE=1`, the uploaded zip file and `packagename.txt` are generated. When you update the deployment source, zip files are added but `packagename.txt` is updated so it points to the newest zip file.
```
home
└── Data
    └── SitePackages
        ├── 20220110085830.zip
        ├── 20220110094211.zip
        ├── 20220110100120.zip
        ├── 20220110100833.zip
        └── packagename.txt
```

**packagename.txt**
```
20220110100833.zip
```

## Deployed folder inside App Service <a name="deployed"></a>

Red mark means newly added after deployment. Deployment is done through Azure CLI.

**Azure CLI Powershell command example**
```
az webapp config appsettings set --resource-group {Resource Group name} --name {App Service name} --settings WEBSITE_RUN_FROM_PACKAGE="1"
az webapp deployment source config-zip --resource-group {Resource Group name} --name {App Service name} --src .\upload.zip
```

**WEBSITE_RUN_FROM_PACKAGE=0**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kdy21oa7faqwozdpaqj.png) 

**WEBSITE_RUN_FROM_PACKAGE=1**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dcayyiynejq0h9ockp7.png) 

# Experiments: Root folder <a name="root-folder"></a>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aff7x3r4pxlj45dvv64d.png) 

I expected Root folder without node_module works when setting up `npm install` as Azure App Service Startup command. However, it did not work. I tried `npm install` and `npm install && npm start` but neither worked. The only way is to deploy together with `node_modules` folder. Usually `node_modules` folder is large and you do not want to upload it as deployment. So, deploying Root folder is not an actual option as workflow.

# Experiments: Build folder <a name="build-folder"></a>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t64arlg9wcy1kpm8fm54.png)

In this experiment, I create `upload.zip` folder and deploy to Azure App Service by CLI command. `upload.zip` includes `index.html` directly under upload.zip level. So, `serve -s .` works correctly. `serve -s build` does not work like [the official documentation](https://create-react-app.dev/docs/deployment/). Also, from the stack over flow [Default documents not serving on node web app hosted on Azure](https://stackoverflow.com/questions/46105003/default-documents-not-serving-on-node-web-app-hosted-on-azure), `pm2 serve /home/site/wwwroot --no-daemon --spa` works too for Startup command. 

When you do not set up any Startup command, it will show you "Hey, Node Developers!" page, because it points to `/opt/startup/hostingstart.html`.

```
opt
├── startup
│   └── hostingstart.html
home
└── site
    └── wwwroot
        └── hostingstart.html
```