はじめに
NetSuiteではSuiteScriptを使ってカスタマイズが可能ですが、外部ライブラリを使用した開発もやりようによっては可能です。
今回はVite+Reactで作ったアプリをNetSuiteで動作させてみたので、その手順をご紹介します。
手順概要と結論
- Reactアプリを作る
- webpackで1ファイルにまとめる
- [2]を読み込むSuiteletを作る
- [2]と[3]をNetSuiteにデプロイする
- Suiteletにアクセスする
- Reactアプリが動作する!
ちなみにですが、Next.jsのようなフレームワークを使用した開発は多分無理です。よりモダンな開発を行いたい場合は、Next.js等のフレームワークで作成したアプリは外部にデプロイし、NetSuiteのAPIを使用してデータをフェッチするのがいいと思います。
実装手順
0. 前提/環境
- 開発環境
- VS Code/Cursor
- SuiteCloud Extension for Visual Studio Code
- Node.js 18.20.4
- npm 10.7.0
- NetSuite環境と機能の有効化
- 2024.2
- クライアントSuiteScript
- サーバーSuiteScript
- SuiteCloud開発フレームワーク
1. SDFプロジェクトの作成
まずはSDFプロジェクトを作成してください。もしも既存のプロジェクトがある場合は、そのプロジェクトを使用しても大丈夫です。
また、SDFプロジェクト内のディレクトリ構造は任意で定義していただいて大丈夫ですが、この記事では以下のようなディレクトリ構造を想定しています。雰囲気で感じ取ってください。
.
└── New SuiteScript Project/
└── src/
└── FileCabinet/
└── SuiteScripts/
├── 0001/
│ ├── react_app_sl.js
│ └── netsuite_react_app/
│ ├── src/
│ │ ├── App.jsx
│ │ ├── App.css
│ │ ├── main.jsx
│ │ ├── index.css
│ │ └── ...
│ ├── ...
│ ├── webpack.config.js
│ └── dist/
│ └── bundle.js
├── 0002/
│ └── ...
└── ...
ポイントは、プロジェクト内のSuiteScriptsディレクトリに、各カスタマイゼーション用のディレクトリを配置し、その中に今回のReactアプリを配置することです。
2. Reactプロジェクトの作成
まずは新規Reactプロジェクトを作成します。今回はviteを使います。
npm create vite@latest
# プロジェクト名を入力: netsuite-react-app
# フレームワークとしてReactを選択
# バリアントとしてJavaScriptを選択
cd netsuite-react-app
npm install
また、svgファイル等は不要なので、public
ディレクトリとApp.jsx
内のsvgファイルのインポートは削除しておきます(というか、svgファイルをNetSuite上で正常に参照できませんでした)。
import { useState } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<h1>Vite + React on NetSuite</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>
</>
)
}
export default App
一旦ここでnpm run devを実行して、Reactアプリが動作することを確認いただいても大丈夫です。
3. 必要な依存関係のインストール
webpackでバンドルするために必要なパッケージをインストールします:
npm install --save-dev webpack webpack-cli babel-loader @babel/core @babel/preset-env @babel/preset-react style-loader css-loader
4. webpack設定ファイルの作成
プロジェクトのルートディレクトリ(今回の場合はSuiteScripts\0001\netsuite-react-app\
)にwebpack.config.js
を作成します:
import path from 'path';
import webpack from 'webpack';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
entry: './src/main.jsx',
plugins: [
new webpack.ProvidePlugin({
React: 'react',
ReactDOM: 'react-dom',
}),
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'var',
library: 'MyApp',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
}
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
mode: 'production'
};
5. package.jsonの更新
package.jsonのビルドスクリプトを編集します:
{
"scripts": {
"build": "webpack --config webpack.config.js"
}
}
5. Suiteletスクリプトの作成
NetSuiteでReactアプリを表示するためのSuiteletスクリプトを作成します:
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
* @NModuleScope SameAccount
*
*/
define(["N/ui/serverWidget", 'N/file', 'N/url'],
/**
*
* @param {serverWidget} serverWidget
* @param {file} file
* @param {url} url
*/
(serverWidget,file,url) => {
const onRequest = async ({ request, response }) => {
const action = request.parameters.action;
switch (request.method) {
case "GET":
switch (action) {
default:
const suiteletUI = await createUI(request, response);
response.writePage(suiteletUI);
}
break;
case "POST":
break;
default:
throw {
name: "INVALID_REQUEST_METHOD",
message: 'Request method request.method is invalid.',
};
}
};
const createUI = async ({ parameters }) => {
const form = serverWidget.createForm({ title: " " });
const field = form.addField({
id: "custpage_hidden",
label: "not shown - hidden",
type: "INLINEHTML",
});
// Load the bundle.js file from the File Cabinet
const bundleFile = file.load({
id: 'SuiteScripts/0001/netsuite-react-app/dist/bundle.js'
});
// Get the URL for the bundle.js file
const bundleUrl = bundleFile.url
field.defaultValue = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Widget Example</title>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script defer src="${bundleUrl}"></script>
<script>
// フォームのデフォルトの送信を防ぐ
document.addEventListener('DOMContentLoaded', function() {
const forms = document.getElementsByTagName('form');
for (let form of forms) {
form.addEventListener('submit', function(e) {
e.preventDefault();
return false;
});
form.onsubmit = function() { return false; };
}
});
</script>
</body>
</html>`;
return form;
};
return { onRequest };
});
6. デプロイ手順
-
Reactアプリをビルド:
npm run build
-
生成された
dist/bundle.js
をNetSuiteのファイルキャビネットにアップロード- VS Code上でアップロードしたいファイルを選択しつつ、コマンドパレットを開き(ctrl+shift+p)、
SuiteCloud: Upload File
を行うことで、SuiteScripts配下のディレクトリ構造のままファイルがアップロードされます
- VS Code上でアップロードしたいファイルを選択しつつ、コマンドパレットを開き(ctrl+shift+p)、
-
Suiteletスクリプトをアップロードし、スクリプトレコードとデプロイメントレコードを作成
-
デプロイメントのURLにアクセスしてReactアプリの動作を確認
7. できた
無事にReactアプリをNetSuite上にデプロイすることができました。
このままだとindex.css
のスタイルが悪さして気持ち悪いので、index.css
内のbody
スタイルを以下のようにコメントアウトか削除しちゃいましょう。
body {
margin: 0;
/* display: flex;
place-items: center; */
min-width: 320px;
min-height: 100vh;
}
編集した後は、再度npm run build
を行い、NetSuiteにbundle.js
を再アップロードすることをお忘れなく。
こんなことができる!
このアプローチを使うことで、以下のようなことが実現可能です:
- モダンなUI/UX
- Reactの豊富なエコシステムを活用
- インタラクティブなユーザーインターフェース
- 再利用可能なコンポーネント
- NetSuiteとの連携
- Suiteletからのデータフェッチ
- NetSuiteのレコード操作
- 既存機能との統合
これらの要素は、以下のような業務アプリケーションの開発で活用できます:
- データ可視化ダッシュボード
- カスタムトランザクション入力フォーム
- インタラクティブなレポーティングツール
備考
Next.jsのような、フレームワークを使用したフルスタックアプリの開発は多分できないです。データフェッチの部分はSuiteletやRestletから渡してあげる必要があります。
Tailwind CSS等のCSSフレームワークは使用できます。つまるところ、一つのファイルにバンドルでき、データフェッチの部分をNetSuiteに頼れば、あとは何でもござれといった感じです。
ただ、WebpackでバンドルしてNetSuiteにアップロードして初めてデータ連携の部分を確認できるので、ローカル環境で開発中はデータ連携の部分はテストしづらいです。スタブデータを作成する必要があります。
まとめ
今回はReactをNetSuiteで活用する方法をご紹介しました。
このアプローチを使うことで、従来のERPシステムの概念を超えた、現代的なWebアプリケーションの開発が可能になります。
特に、複雑なUIやインタラクティブな要素が必要なカスタマイズで真価を発揮するかもです。