DetaにNestJS、Angularの成果物をプッシュする手順
Detaとは
当方はHeroku無料枠廃止に伴うニュースから代替となるサービスの記事で知りました。感謝です。
Detaは無料でアプリをデプロイできるサービスです。
Deta Space
(Google翻訳)
...
Space では開発は無料です。私たちは、開発者が他の人のために自分のアイデアを実行するコンピューターを構成、管理したり、料金を支払ったりする必要はないと考えています。また、エンドユーザーはデフォルトで自分のものに対して自由に開発できるべきだと考えています。
...(原文)
...With Space, development is free. We don’t think developers should have to configure, manage, or pay for the computer that runs their idea for other people. We also think that end-users should have the freedom to develop against their own stuff, by default.
...
For doers & dreamers
パーソナルクラウドなる標語を提唱しており、当方の語彙力ではお伝え出来ないですが、AWS、Azure、GCPとは異なる思想を持っているようです。失礼を承知で申し上げると、そこはかとなく漂うB級感がとても好みです。まだまだマイナーな存在のようで、Googleでdeta
を検索しても検索結果に公式が上がってきません。
下図はhttps://deta.space/motivation/の画面の抜粋です。
当記事のきっかけ
The Space Runtime
上記Detaのドキュメントに
(Google翻訳)Deta Space には、ほぼすべての種類のアプリを実行できる独自のランタイムである Space Runtime があります。たとえば、Space Runtime は以下をサポートします。
- React、Vue、Svelteなどのフレームワークで構築されたフロントエンド サイト
- Next、Nuxt、SvelteKitなどのフルスタック フレームワーク
- Node.js、Python、さらには Go、Rust、またはよりカスタムなもので構築されたバックエンド アプリ
個々の Space アプリは、これらのさまざまなテクノロジーを組み合わせて構築できます。たとえば、SvelteKit アプリと Go API、Next.js アプリと Python API など、最大 5 つの異なる言語とフレームワークを組み合わせて構築できます。
(原文)Deta Space has its own runtime, the Space Runtime, that can run almost any type of app. For example, the Space Runtime supports:
- frontend sites built with frameworks like React, Vue, and Svelte
- full-stack frameworks like Next, Nuxt, or SvelteKit
- backend apps built with Node.js, Python and even Go, Rust or something more custom
An individual Space app can be built by combining these different technologies, for example, a SvelteKit app with a Go API or a Next.js app with a Python API — really any combination of up to 5 different languages and frameworks.
とあります。NestJSはNode.jsで構築されたバックエンドに含まれると思います。かなしいかなAngularが列挙されていません。
当記事は自分なりに試してサーバーサイドNestJS、フロントサイドAngularの成果物をDetaにプッシュした手順を記します。
注意
Detaのアカウントの作成が済んでいる状態からの手順になります。
当記事では、成果物を外部に公開するところまでは試していません。
当記事の手順で進めたのち、
space release
コマンドを実行すると、公開できるはずです。
試しに実行してみたところ、以下のように聞かれました。
Releasing makes your app available via a unique link for others to install and use.
If you only want to use this app yourself, use your Builder instance instead.
? Are you sure you want to release this app to others? (y/n) n
Aborted releasing this app.
(Google翻訳) リリースすると、他の人が独自のリンクを介してアプリをインストールして使用できるようになります。
このアプリを自分だけで使用したい場合は、代わりに Builder インスタンスを使用してください。
実行したとてAngularのテンプレートアプリが表示されるだけなので、それを公開するというのも忍びなく、キャンセルしてしましました。いずれ自信をもって公開できるもの作ろうと思います。
以上の通り、公開までは試しておりません。それでもよろしければ読み進めていただければと思います。
環境
Windows10
WSLストア版
手順
まずは理屈はおいておいて、当方がDetaにNestJS+Angularの成果物をDetaにプッシュできた手順を記し、後に設定の根拠を記します。
Dockerfile
FROM node:16.20.1
ENV LANG=C.UTF-8
ENV LANGUAGE=en_US:
RUN apt-get update && apt-get install nano
# Space CLIのインストールと環境変数の設定
RUN curl -fsSL https://deta.space/assets/space-cli.sh | sh
ENV PATH $PATH:/root/.detaspace/bin
RUN echo $PATH
RUN mkdir /app
WORKDIR /app
Dockerイメージ作成
イメージ名deta
、タグnode-16.20.1
でDockerイメージを作成しました。
docker build -t deta:node-16.20.1
Dockerコンテナ作成
docker run --name deta-nest-angular -v /development/deta-nest-angular:/app -w /app -p 4200:4200 -p 3000:3000 -itd --dns 8.8.8.8 deta:node-16.20.1
DetaSpaceのアクセストークンを取得
生成されたトークンが表示されます。トークンが表示されるタイミングはこの時のみです。外部に漏れないように控えてください。
開発環境とDetaを連結
以下はコンテナ内の作業です。
space login
コマンドを実行し、DetaSpaceにログインします。
space login
# 以下のようにトークンを聞かれるので、上の手順で取得したアクセストークンをコピペします。
To authenticate the Space CLI with your Space account, generate a new access token in your Space settings and paste it below:
? Enter access token (0 chars) >
# トークンを入力してアクセスに成功すると以下の表記になりました。
? Enter access token (41 chars) > *****************************************
👍 Login Successful!
Checking for new Space CLI version...
i New Space CLI version available, upgrade with space version upgrade
コンテナ内での作業
VSCodeで作成したコンテナ内に入ります。
以下のように構成していきます。
app/
├front-side/ # Angularのプロジェクトディレクトリ
│ ├node_modules/
│ ├package.json
│ ...
├server-side/ # NestJSのプロジェクトディレクトリ
| ├app/ # Angularのデプロイ先
│ ├node_modules/
│ ├package.json
│ ...
├node_modules/ # プロジェクトのルートディレクトリに@angular/cli、@nestjs/cliをインストール
├package.json
└Spacefile # DetaSpaceの設定ファイル
# プロジェクト初期化
npm init -y
# プロジェクトのルートディレクトリで@angular/cliと@nestjs/cliをインストールします。
npm install --save-dev @angular/cli @nestjs/cli
# Angularのテンプレートプロジェクトを生成します。
npx ng new front-side
# NestJSのテンプレートプロジェクトを生成します。
npx nest new server-side
Angluarのビルド先をNestJSのプロジェクトディレクトリ直下に設定します。
{
...
"projects": {
"front-side": {
...
"architect": {
"build": {
...
"options": {
"outputPath": "../server-side/app" #←ここ
}
}
}
}
}
}
NestJSでAngularのビルド結果を提供できるように、@nestjs/serve-static
をインストールし、設定します。
cd /app/server-side
npm install @nestjs/serve-static
app.module.ts
を修正します。
...
import { ServeStaticModule } from '@nestjs/serve-static'
import { join } from 'path';
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '../app'),
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ブラウザでNestJSで作成したサーバーにアクセスし、Angularで作成した画面が表示されるか確認します。
フロントサイドをビルドします。先ほどのangular.json
でビルド先を変更したので、server-side/app
ディレクトリにビルドされます。
cd /app/front-side
npm run build
localhost:3000
にアクセスしたとき、Hello world!
の表示ではなくAngularの成果物が表示されるように、server-side/src/app.controller
を修正します。
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
/* この部分をコメントアウトします。
@Get()
getHello(): string {
return this.appService.getHello();
}
*/
}
またサーバーを起動する際のポートは環境変数から取得するようにserver-side
修正します。
Detaで実行する為に必要です。
(Google翻訳) アプリがPORT環境変数で定義されたポートをリッスンするように構成されていることを確認してください。
(原文💡 Make sure your app is configured to listen on the port defined by the PORT environment variable.)
Deta公式(Run a Node.js App)
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// ポートを環境変数から取得するようにする。
const port = process.env.PORT || 3000;
await app.listen(port);
}
NestJSのテストサーバーを起動します。
cd /app/serve-side
npm run start:dev
ブラウザでhttp://localhost:3000
にアクセスしAngularの画面が表示されます。
NestJSのビルド時、Webpackを使用して単一ファイルになるように設定します。
※デフォルトの状態でビルドしたものだと、Detaにアップロード後実行できませんでした。単一ファイルになる様にし、node server-side/main.js
で起動できるようにすることで実行できました。
{
...
"scripts": {
"build": "nest build --builder webpack",
"start:dev": "nest start --watch --builder webpack",
...
}
}
const { NODE_ENV = 'production' } = process.env;
module.exports = {
target: 'node',
mode: NODE_ENV,
externals: [
{
'@nestjs/websockets/socket-module':
'commonjs2 @nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module':
'commonjs2 @nestjs/microservices/microservices-module',
'@fastify/static': 'commonjs2 @fastify/static'
},
],
optimization: {
minimize: false,
},
};
プロジェクトのルートディレクトリから、ビルド、開発サーバーでの起動、通常起動が出来るようにpackage.json
のscripts
を設定します。ここで設定したスクリプトをこの後にでてくるSpacefile
に設定します。
{
...
"scripts": {
"build": "npm run build:front-side && npm run build:server-side",
"build:front-side": "cd front-side && npm run build",
"build:server-side": "cd server-side && npm run build",
"start:dev": "npm run dev:front-side & npm run dev:server-side",
"dev:front-side": "cd front-side && ng serve --host 0.0.0.0 --poll 2000",
"dev:server-side": "cd server-side && npm run start:dev",
"start": "node server-side/dist/main.js"
},
...
}
space new
コマンドを実行
コンテナ内の作業です。
プロジェクトルートディレクトリで、space new
コマンドを実行します。
# プロジェクトルートディレクトリに移動
cd /app
space new
# 生成するプロジェクト名を聞かれるので入力します。
# 当例ではfirst-deta-appとしました。
? What is your projects name? > first-deta-app
# yを選択します。
? Do you want to setup "first-deta-app" with this configuration? (Y/n)
処理が完了すると、.space
ディレクトリ、Spacefile
が作成され、.gitignoreに.space
が追記されます。
Spacefile
を以下のように修正します。
v: 0
micros:
- name: first-deta-app
src: ./server-side
engine: nodejs16
include:
- app
- dist
run: node dist/main.js
commands:
- npm run build
dev: npm run start:dev
primary: true
public: true
space push
コマンドを実行
いよいよ、Detaにpushします。
space push
# 成功するとURLが表示されます。
Validating your Spacefile...
Your Spacefile looks good, proceeding with your push!
✓ Successfully started your build!
✓ Successfully pushed your Spacefile!
...
省略
...
✓ Successfully pushed your code and updated your Builder instance!
Builder instance: https://firstdetaapp-1-t7712186.deta.app
表示されるURLにアクセスすると、Angularの画面が表示されるはずです。
またdeta.spaceのキャンバス画面を確認すると、開発中のアプリのアイコンが追加されています。
以上です。
設定の根拠
Dockerfile
FROM node:16.20.1
ENV LANG=C.UTF-8
ENV LANGUAGE=en_US:
RUN apt-get update && apt-get install nano
# Space CLIのインストールと環境変数の設定
RUN curl -fsSL https://deta.space/assets/space-cli.sh | sh
ENV PATH $PATH:/root/.detaspace/bin
RUN echo $PATH
RUN mkdir /app
WORKDIR /app
Dockerイメージにnode:16.20.1を選定した理由は、公式ドキュメントに以下のような記載がありました。
(Google翻訳) 💡 現在、Space は Node.js 16 のみをサポートしていますが、間もなく Node.js 18 のサポートを追加する予定です。nvmなどのノード バージョン マネージャーを使用して、コンピューターに Node.js と npm をインストールすることをお勧めします。これにより、さまざまなバージョンの Node.js をすばやくインストールして使用できるようになります。
(原文)💡 Currently, Space only supports Node.js 16, with plans to add support for Node.js 18 soon. We recommend using a Node version manager like nvm to install Node.js and npm on your computer. This will allow you to quickly install and use different versions of Node.js.
DockerHubでNode.js16系統で一番新しいバージョンナンバーのイメージを選びました。
Space CLIのインストールと環境変数の設定
の部分は公式サイトのこちらにあるようにDeta Space用のアプリを構築するのに必要なため、Dockerコンテナ生成時にSpace CLIがインストールされるように設定しました。
下に続く環境変数の設定ですが、公式にある通りRUN curl -fsSL https://deta.space/assets/space-cli.sh | sh
コマンドでインストールすると、以下のように環境変数の設定を促すメッセージが表示されます。
inflating: /root/.detaspace/bin/space
Deta Space CLI was installed successfully to /root/.detaspace/bin
Manually add /root/.detaspace/bin to your path:
export PATH="/root/.detaspace/bin:$PATH"
Run 'space --help' to get started
そのため環境変数の設定を含めました。
NestJSについてWebpackでビルドする理由
通常通りビルドしてDetaにプッシュすると、下図のようなエラーが発生しました。
server-side/package.json
とプロジェクトルートのpackage.json
のscripts
を元の状態に近い以下のようにしてpushしてみます。
{
...
"scripts": {
//"build": "nest build --webpack",
"build": "nest build",
...
}
}
{
...
"scripts": {
...
//"start": "node server-side/dist/main.js",
"start": "cd server-side && nest start",
...
}
}
space push
プッシュ自体は成功しますが、URLにアクセスすると、以下の表示になります。
webpackを用いて単一ファイルになる様にビルドし、node server-side/dist/main.js
コマンドで起動するようにしたところ、回避されました。
webpackの設定は下記のサイトを参考にさせていただきました。
NestJSアプリケーションをwebpackでBundle【> 404 motivation not found】
webpack.confi.jsのexlternalsへの記載方法
webpack.config.js
を再掲します。
先述の参考サイトに載っていたものを元に作っております。
当方、externalsに載っている項目はどのように設定しているのかわかりませんでした。
const { NODE_ENV = 'production' } = process.env;
module.exports = {
target: 'node',
mode: NODE_ENV,
externals: [
{
'@nestjs/websockets/socket-module':
'commonjs2 @nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module':
'commonjs2 @nestjs/microservices/microservices-module',
'@fastify/static': 'commonjs2 @fastify/static'
},
],
optimization: {
minimize: false,
},
};
ChatGPT大先生に伺ったところ、ビルドしてエラーになったものを記述する、とのことでした。
試しにexternals
を空にしてビルドを実行してみます。
...
module.exports = {
target: 'node',
mode: NODE_ENV,
externals: [
{
//空
},
],
...
server-sideディレクトリでnpm run build
を実行します。
npm run build
# 以下のエラーが発生します。
> server-side@0.0.1 build
> nest build --builder webpack
ERROR in ./node_modules/@nestjs/core/nest-application.js 19:107-150
Module not found: Error: Can't resolve '@nestjs/websockets/socket-module' in '/app/server-side/node_modules/@nestjs/core'
@ ./node_modules/@nestjs/core/index.js 26:21-50
@ ./src/main.ts 3:15-38
ERROR in ./node_modules/@nestjs/core/nest-application.js 20:124-177
Module not found: Error: Can't resolve '@nestjs/microservices/microservices-module' in '/app/server-side/node_modules/@nestjs/core'
@ ./node_modules/@nestjs/core/index.js 26:21-50
@ ./src/main.ts 3:15-38
ERROR in ./node_modules/@nestjs/serve-static/dist/loaders/fastify.loader.js 19:113-139
Module not found: Error: Can't resolve '@fastify/static' in '/app/server-side/node_modules/@nestjs/serve-static/dist/loaders'
@ ./node_modules/@nestjs/serve-static/dist/index.js 20:13-48
@ ./node_modules/@nestjs/serve-static/index.js 6:9-26
@ ./src/app.module.ts 13:23-54
@ ./src/main.ts 4:21-44
エラー内容から、
@nestjs/websockets/socket-module
@nestjs/microservices/microservices-module
@fastify/static
を拾い、それをwebpack.config.js
のexternals
に列挙します。
エラーのあったモジュール名
: commonjs2 ○○(○○はエラーのあったモジュール名)
という形式で列挙します。
...
externals: [
{
'@nestjs/websockets/socket-module':
'commonjs2 @nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module':
'commonjs2 @nestjs/microservices/microservices-module',
'@fastify/static': 'commonjs2 @fastify/static'
}
]
...
以上です。
感想等
Detaの公式を読むと(原文は英語で私読めないのでGoogle翻訳して読んでいます。)、なにかこう、とても応援したくなるサービスです。
記事を書いている2023/07/05現在、公式ブログの最新は2023/2/23で、What's newの最新は2023/04/27なので、あまり活発とは見受けられないのですが、どんどん発展して欲しいと願っています。