はじめに
Vite + Reactで開発していたアプリケーションで、Supabaseにあるレコードの削除をバッチ処理として実行したい場面が出てきました。
その際バッチを.tsファイルで作成して実行しようとしたものの、実行方法の調査やVite環境特有のエラーの解消で手間取ったため、解決方法をまとめようと思います。
tsファイルの実行ツールについて
tsファイルをコマンドラインから実行しようとしたとき、調べてみるとまず出てくるのはts-nodeになります。
しかし、調べてみると最近はtsxというツールがあり、ts-nodeはパッケージが2年ほど更新されておらず...?ということで、tsxを使用して実行するのが良いようです。
そこで、とりあえずtsxを使用してバッチを実行する方法を模索してみました。
tsxを使用した実行方法
パス解決エラー
tsxをnpm i tsxでプロジェクトにインストール後、以下のコマンドでtsファイルを実行してみました。
npx tsx ./src/batch/index.ts
すると、以下のようにパッケージが見つからないというエラーが発生しました。
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/libs' imported from /myproject/src/batch/index.ts
今回使用したプロジェクトではDBにsupabaseを使用しており、index.ts内ではそのための接続用モジュールをimportしていたのですが、ここでパスが解決できていないようです。
import { supabase } from "@/libs/supabase/supabase";
解決策
tsconfigファイルを明示的に指定したところ、無事にパスが解決できるようになりました。
npx tsx --tsconfig tsconfig.app.json ./src/batch/index.ts
npxはデフォルトだとtsconfig.jsonを読みに行きますが、一般的にnpm create vite@latestで作成したプロジェクトではtsconfig.jsonには設定が書かれておらず、代わりにtsconfig.app.jsonやtsconfig.node.jsonに具体的な設定が書かれています。
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
今回の場合、tsconfig.app.jsonの中にパスの設定が書かれているため、明示的にこのファイルを読む必要があるということでした。
"paths": {
"@/*": ["./src/*"]
}
毎回--tsconfigオプションを指定するのも面倒なので、以下のようにpackage.jsonに記載して簡単に実行できるようにもしています。
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest run",
"tsx": "tsx --tsconfig tsconfig.app.json"
}
これにより、以下のコマンドで簡単にバッチを実行できるようになりました。
npm run tsx ./src/batch/index.ts
import.meta.envを読み込めないエラー
パスは解決できるようになったものの、次に環境変数が読み込めないというエラーが発生しました。
/myproject/src/libs/supabase/supabase.ts:4
import.meta.env.VITE_SUPABASE_URL,
^
TypeError: Cannot read properties of undefined (reading 'VITE_SUPABASE_URL')
原因としては、import.meta.envはVite環境でのみ使用できるオブジェクトのため、純粋なnode.js上で動くツールであるtsxではこのオブジェクトを読み込めない(というより、実行環境上に存在しない)ということになります。
解決策
環境変数を使用しているファイルで、dotenvを導入し、環境変数はprocess.envから取得することで、バッチが実行できるようになりました。
import "dotenv/config";
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
process.env.VITE_SUPABASE_URL as string,
process.env.VITE_SUPABASE_KEY as string
);
注意点として、process.envはnode環境専用のオブジェクトのため、ブラウザ環境でこのコードが実行されるとnot foundのエラーとなってしまいます。
そのため、ブラウザ環境で動くコードから呼ばれるファイルはimport.meta.envを使用するままとし、バッチファイルから呼ばれるファイルとして新たにsupabase.node.tsを作成しています。
元々のsupabase.tsは以下の通りです。
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_KEY
);
これによって、ブラウザ環境でも無事に環境変数を取得して処理を行い、バッチが実行される際も正しく環境変数を取得できるようになりました。
vite-nodeを使用した実行方法
ここまでtsxを使用する方法で、
- 明示的にtsconfigファイルを指定しないとパスが解決できない
- import.meta.envを使用できない
というエラーを解決する必要がありました。
これらはtsxが純粋なnode.js上で実行されるツールであり、Vite特有の設定を読み込めないというのが最大の原因になります。
しかし、さらに調べてみると、Viteでは一番簡単なtsファイルの実行方法が存在していました。
それが、vite-nodeを使用する方法になります。
npm install vite-node
npx vite-node ./src/batch/index.ts
特別な設定なく、これだけでVite上のtsconfigや環境変数なども全て解決し、tsファイルを実行することが可能です。
vite-nodeとは何なのか?
あまり記事がなく情報が少ないのですが、Vitestの中で利用されるために、Viteの設定などを使用しつつTypeScriptのファイルを実行できるツールとして開発されたもののようです。
READMEには「vite-nodeはその使命を終えた」というような記載があるもの、2026年3月現在で最終更新が2ヶ月前となっており、tsファイルの実行ツールとしては使用して問題なさそうです。
追記: nodeのネイティブTypeScript実行を使った方法
コメントで教えていただいた内容ですが、node v22.18.0以降の環境では標準のnodeコマンドによってtsファイルを実行できるとのことでした。
node ./src/batch/index.ts
ただ、本記事のtsxを使用した方法と同様、下記のようなエイリアスを含んだパスを解決できないという問題が発生しました。
// Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/libs'が発生
import { supabase } from "@/libs/supabase/supabase";
tsxの場合は--tsconfigオプションでpathを記載したtsconfigファイルを明示的に指定することで解決できましたが、nodeの公式ドキュメントの記述を読むとThe Node.js TypeScript loader (Amaro) does not need or use tsconfig.json to run TypeScript code.とあり、そもそもtsconfigを使用せず、よって指定するオプションもないようです。
そこで、importの書き方をtsconfigに依存しないように少し工夫する必要がありました。
// エイリアスを使用せずにパスを記述し、ファイルの拡張子も記載する
import { supabase } from "../libs/supabase/supabase.node.ts";
これによって、パス周りのエラーも出ずに実行することができました。
一旦Viteでの書き方から離れて、ただのTypeScriptファイルであることを意識して書いておく必要があるようです。
おわりに
TS/JS周辺は動きが早く、実行ツール一つとっても選択肢があり、次々とベストプラクティスが変わっているのが調べていて分かりました。
そもそもtsファイルでバッチ処理を行うケースが少ない...?という理由もあるかもしれませんが、最新で正しい情報がなかなか見つかりづらかったため、本記事が参考になればと思います。