こんにちは、さるまりんです🐒
今回はTypeScript + Expressでサーバーサイドアプリケーションを作り、Docker上に開発環境を構築する手順をメモします。
TypeScriptが初めてでも、そのまま使えるように進めていきます!
なぜExpressを使うの?
ExpressはNode.js上で動く軽量で柔軟なWebフレームワークです。最近周りに使っている人がいるのでやってみたいと思って今回のチャレンジです。
こんな感じで簡単にHello, world!できるようです。
// ExpressでHello World
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('Hello, world!'));
app.listen(3000);
プロジェクト構成
project-root/
├── docker-compose.yml
├── Dockerfile
├── package.json
├── tsconfig.json
├── src/
│ └── index.ts
ステップ1:プロジェクト初期化
mkdir my-ts-server && cd my-ts-server
npm init -y
ステップ2:依存パッケージのインストール
npm install express
npm install --save-dev typescript ts-node-dev @types/node @types/express
以下が実際に実行したものの抜粋です。
% npm install express
added 66 packages in 4s
% npm install --save-dev typescript ts-node-dev @types/node @types/express
added 72 packages, and audited 139 packages in 9s
ステップ3:TypeScript設定(tsconfig.json)
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
}
}
ステップ4:Expressサーバーを作る(src/index.ts)
import express from 'express';
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.get('/', (req, res) => {
res.send('Hello from TypeScript + Express!');
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
🐳 ステップ5:Dockerfileの作成
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npx", "ts-node-dev", "--respawn", "src/index.ts"]
🐳 ステップ6:docker-compose.yml を用意
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
起動と確認
docker-compose up --build
注意:mac上で実行したのですが、実際のコマンドはちょっとだけ違っていて、
docker compose up --build
で動きました。
以下のそログの抜粋です。
% docker compose up --build
Compose can now delegate builds to bake for better performance.
To do so, set COMPOSE_BAKE=true.
[+] Building 81.9s (12/12) FINISHED docker:desktop-linux
=> [app internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 180B 0.0s
=> [app internal] load metadata for docker.io/library/node:18 2.5s
=> [app auth] library/node:pull token for registry-1.docker.io 0.0s
=> [app internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [app 1/5] FROM docker.io/library/node:18@sha256:867be01f97d45cb7d89a8ef0b328d23e8207412ebec4564441ed8cabc8cc4ecd 72.6s
=> => resolve docker.io/library/node:18@sha256:867be01f97d45cb7d89a8ef0b328d23e8207412ebec4564441ed8cabc8cc4ecd 0.0s
=> => sha256:e56a6e3bc5724c51df0a1ad86c6e38aee233bb343457e98c943c3c7c1c9a2a84 6.39kB / 6.39kB 0.0s
=> => sha256:cf05a52c02353f0b2b6f9be0549ac916c3fb1dc8d4bacd405eac7f28562ec9f2 48.49MB / 48.49MB 8.9s
=> => sha256:867be01f97d45cb7d89a8ef0b328d23e8207412ebec4564441ed8cabc8cc4ecd 6.41kB / 6.41kB 0.0s
=> => sha256:a0ee20d21ff7e3199f16644fd076ef1435b42689ca015daf08e47c02fd6cfc38 2.49kB / 2.49kB 0.0s
=> => sha256:63964a8518f54dc31f8df89d7f06714c7a793aa1aa08a64ae3d7f4f4f30b4ac8 24.01MB / 24.01MB 6.1s
=> => sha256:ca513cad200b13ead2c745498459eed58a6db3480e3ba6117f854da097262526 64.39MB / 64.39MB 9.8s
=> => sha256:c187b51b626e1d60ab369727b81f440adea9d45e97a45e137fc318be0bb7f09f 211.36MB / 211.36MB 51.3s
=> => sha256:68b421cb119c75992bb4a52f93da195c7f10de1cf73fdaf9ea82af9dd008e42a 3.32kB / 3.32kB 9.1s
=> => extracting sha256:cf05a52c02353f0b2b6f9be0549ac916c3fb1dc8d4bacd405eac7f28562ec9f2 7.0s
=> => sha256:bf3cd053bbe4b167d7cef55cc32347e55dd140d818cbf4a4bfca50d612fcda39 45.68MB / 45.68MB 19.9s
=> => sha256:e1dbe5f455cde9503d18d04e9a2c641b5aff12cacfa7a3ec093986c828ea178c 1.25MB / 1.25MB 10.6s
=> => sha256:a63cf41c258c6ef4426dea1e73a875cfc00d43547f2fa6085ad9b2077f3197a5 447B / 447B 10.9s
=> => extracting sha256:63964a8518f54dc31f8df89d7f06714c7a793aa1aa08a64ae3d7f4f4f30b4ac8 1.6s
=> => extracting sha256:ca513cad200b13ead2c745498459eed58a6db3480e3ba6117f854da097262526 6.5s
=> => extracting sha256:c187b51b626e1d60ab369727b81f440adea9d45e97a45e137fc318be0bb7f09f 15.3s
=> => extracting sha256:68b421cb119c75992bb4a52f93da195c7f10de1cf73fdaf9ea82af9dd008e42a 0.0s
=> => extracting sha256:bf3cd053bbe4b167d7cef55cc32347e55dd140d818cbf4a4bfca50d612fcda39 4.7s
=> => extracting sha256:e1dbe5f455cde9503d18d04e9a2c641b5aff12cacfa7a3ec093986c828ea178c 0.1s
=> => extracting sha256:a63cf41c258c6ef4426dea1e73a875cfc00d43547f2fa6085ad9b2077f3197a5 0.0s
=> [app internal] load build context 1.7s
=> => transferring context: 31.97MB 1.6s
=> [app 2/5] WORKDIR /app 1.1s
=> [app 3/5] COPY package*.json ./ 0.0s
=> [app 4/5] RUN npm install 4.0s
=> [app 5/5] COPY . . 1.0s
=> [app] exporting to image 0.6s
=> => exporting layers 0.6s
=> => writing image sha256:47cb3af8398cb2578c6c6a113afdb6b454f9661207764ee901094e7ca4473c1d 0.0s
=> => naming to docker.io/library/my-ts-server-app 0.0s
=> [app] resolving provenance for metadata file 0.0s
[+] Running 3/3
✔ app Built 0.0s
✔ Network my-ts-server_default Created 0.1s
✔ Container my-ts-server-app-1 Created 0.4s
Attaching to app-1
app-1 | [INFO] 03:47:03 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.2, typescript ver. 5.8.3)
app-1 | Server is running on port 3000
ブラウザでhttp://localhost:3000
にアクセスすると:
Hello from TypeScript + Express!
と表示されていますね。
curlでAPIを叩く確認
curl
でも叩いてみます。
↓のようになりました。
% curl http://localhost:3000/
Hello from TypeScript + Express!%
大丈夫そうですね。
ts-node-dev
により、コードを編集すると自動でサーバーが再起動されます。これで開発中のホットリロードもOKです!
環境変数やDBとの接続を追加すれば、そのまま使える構成になるかな。
TypeScript + Expressよく使われるようになってきてるみたいですね。
Docker化すれば、どの環境でも同じ動作を再現できます。
さらにこの先のこともできるようにしていきたいと思います。
いろいろやるならTypeScriptのカテゴリー作ろうかな?
読んでくださってありがとうございました。
それではまた!