# Deploy Next.js app on IIS

## 前言

使用Next.js框架開發React是非常易用的，且全系統都是JavaScript/TypeScript少了語言轉換的煩噪。

比較大的問題在部署。若是部署到Linux 平台就簡單多了，搭配[`PM2`](https://pm2.keymetrics.io/)工具就行了。若是部署到Widows就要繞一大圈，官網沒有直接直達的方案，且不同時期的版本部版指令也可能不同，只能靠善心大佬協助補充了。研究了數天總結如下：

{% hint style="danger" %}
注意：Next.js app若要部署到windows，第一個步驟一定是轉換成 Node.js Server。
{% endhint %}

## 將 Next.js app 部署在 Linux

若是部署到 Linux 平台就簡單多了，搭配[`PM2`](https://pm2.keymetrics.io/)工具就行了。

## 將 Next.js app 部署成 Windows Service

* 使用[`qckwinsvc`](https://www.npmjs.com/package/qckwinsvc)，成功。這套已不再維護然仍有作用。
* 使用[`qckwinsvc2`](https://www.npmjs.com/package/qckwinsvc2)，失敗。部署有成功訊息卻沒有作用。(補充：還有這套[`qckwinsvc2_updated`](https://www.npmjs.com/package/qckwinsvc2_updated)，應為改良版尚未測試留存參考。)
* 使用[`node_windows`](https://www.npmjs.com/package/node-windows)，失敗。這是設計來部署Node.js Server 的。不過部署 Next.js app 轉換出來的 Node.js Server 卻不行。
* 綜合結論：不建議這方式。

## 將 Next.js app 部署到 IIS server

如下面影片有完整流程：

{% embed url="<https://www.youtube.com/watch?ab_channel=ArdiNasiri&v=HLsx0iraA-Y>" %}
deploy next js on iis web server
{% endembed %}

其它參考文件

{% embed url="<https://brooke2010.github.io/2021/03/14/window-IIS-run-nodejs/>" %}

{% embed url="<https://www.twblogs.net/a/5b7e1d882b71776838556286>" %}

## 開發環境

* Visual Studio Code
* `Node.js`平台 v16.14.0
* `next` v12.1.6
* `react` v17.0.2

## 關鍵步驟紀錄

### 一、建立的Next.js app。

### 二、轉換成Node.js Server。

Next.js app若要部署到windows，第一個步驟一定是轉換成 Node.js Server。此步驟將增加`server.js`檔案，這是 Node.js Server 執行的進入點。

{% embed url="<https://nextjs.org/docs/advanced-features/custom-server>" %}

#### Server.js 源碼紀錄

直接抄用即可，指令中指定啟動自`sever.js`，port number 預設3000。

{% code title="server.js" %}

```javascript
// server.js
// ref→[Custom Server](https://nextjs.org/docs/advanced-features/custom-server)
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const port = process.env.PORT || 3000

// when using middleware `hostname` and `port` must be provided below
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  createServer(async (req, res) => {
    try {
      // Be sure to pass `true` as the second argument to `url.parse`.
      // This tells it to parse the query portion of the URL.
      const parsedUrl = parse(req.url, true)
      const { pathname, query } = parsedUrl

      if (pathname === '/a') {
        await app.render(req, res, '/a', query)
      } else if (pathname === '/b') {
        await app.render(req, res, '/b', query)
      } else {
        await handle(req, res, parsedUrl)
      }
    } catch (err) {
      console.error('Error occurred handling', req.url, err)
      res.statusCode = 500
      res.end('internal server error')
    }
  }).listen(port, (err) => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${port}`)
  })
})
```

{% endcode %}

#### package.json 源碼紀錄

並調整 npm run start 指令 script 從 next start 改成 node server.js。

{% code title="package.json" %}

```json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "node server.js",  //改用 node.js 平台執行
    //"start": "next start",    //預設用 next.js 平台執行   
    //// 下面指定port number
    //"start": "next start -p 8080", 
    //"start": "cross-env NODE_ENV=production PORT=8080 node server.js",
    //// windows 環境下語法
    //"start": "set NODE_ENV=production && set PORT=8080 && node server.js",
  },
  "dependencies": {
    ...
  },
  "devDependencies": {
    "cross-env": "^7.0.3",  // 建議安裝此工具模組，讓環境參數設定指令可跨平台。
    ...
  }
}
```

{% endcode %}

### 三、準備IIS環境

在 IIS Server 執行 node.js app 需安裝

(1) 安裝 URL Rewrite

{% embed url="<https://www.iis.net/downloads/microsoft/url-rewrite>" %}

(2) 安裝 Node.js 平台

{% embed url="<https://nodejs.org/zh-tw/download/>" %}

(3) 安裝 iisnode

這個比較麻煩，不同時期版本可能不用。本期安裝位置：

**Installing for IIS 7.x/8.x**

* Install iisnode for IIS 7.x/8.x: [x86](https://github.com/azure/iisnode/releases/download/v0.2.21/iisnode-full-v0.2.21-x86.msi) or [x64](https://github.com/azure/iisnode/releases/download/v0.2.21/iisnode-full-v0.2.21-x64.msi) - choose bitness matching your system

{% embed url="<https://github.com/Azure/iisnode>" %}
Hosting node.js applications in IIS on Windows&#x20;
{% endembed %}

### 四、編寫 web.config

參考自這裡，只是多一行`<add segment="node_modules"/>`。

{% embed url="<https://github.com/MRCollective/nextjs-server-azuresiteextension>" %}

此段直接抄用即可。

{% code title="web.config" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<!--
參考自：[Next.js Server Azure Site Extension](https://github.com/MRCollective/nextjs-server-azuresiteextension)
-->
<!--
     This configuration file is required if iisnode is used to run node processes behind
     IIS or IIS Express.  For more information, visit:
     https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->

<configuration>
  <system.webServer>
    <!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
    <webSocket enabled="false" />
    <handlers>
      <!-- 設定處理常式對應：引用 iisnode 模組處理 server.js。 -->
      <!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
      <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <!-- 設定 URL rewrite 條件與規則 -->
      <rules>
        <!-- Do not interfere with requests for node-inspector debugging -->
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^server.js\/debug[\/]?" />
        </rule>

        <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
        <rule name="StaticContent">
          <action type="Rewrite" url="public{REQUEST_URI}"/>
        </rule>

        <!-- All other URLs are mapped to the node.js site entry point -->
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="server.js"/>
        </rule>
      </rules>
    </rewrite>
    
    <!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
    <security>
      <requestFiltering>
        <hiddenSegments>
          <!-- <remove segment="bin"/> -->
          <add segment="node_modules"/>
        </hiddenSegments>
      </requestFiltering>
    </security>

    <!-- Make sure error responses are left untouched -->
    <httpErrors existingResponse="PassThrough" />
    <iisnode node_env="production" />

    <!--
      You can control how Node is hosted within IIS using the following options:
        * watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
        * node_env: will be propagated to node as NODE_ENV environment variable
        * debuggingEnabled - controls whether the built-in debugger is enabled
      See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
    -->
    <!--<iisnode watchedFiles="web.config;*.js"/>-->
  </system.webServer>
</configuration>
```

{% endcode %}

### 五、設定 IIS 網站。

這段沒有特別之處，一樣指定網站實體位置、port等即可。順利的話就能成功啟動了。

EOF
