JavaScript、MongoDB初心者です。わからないところはググりながら書きました。間違いなどありましたら教えていただけるとありがたいです。
↑こちらのリンクで、express、mongodbのチュートリアルがあったので、それを自分なりにTypeScriptで書き直してみたという投稿になります。よろしくお願いいたします。m(_ _)m
こう言った感じのチャットが作成できました。
環境
- macOS Mojave 10.14.5
- node 12.7.0
- MongoDB 4.0.3
私が実際に作成してみたコードはこちらです↓
MongoDBのインストール
こちらのリンクを参考にMongoDBをインストールしました。m(_ _)m
#参考リンクにあるように、こちらでmongodbを起動している状態にしておきます。
sudo mongod --dbpath /var/lib/mongodb --logpath /var/log/mongodb.log
インストール
yarn add express mongoose socket.io bootstrap
yarn add -D webpack webpack-cli webpack-merge webpack-node-externals nodemon-webpack-plugin typescript ts-loader @types/express @types/mongoose @types/node @types/socket.io @types/socket.io-client @types/webpack css-loader style-loader
- express
- mongoose
- socket.io
- bootstrap
- webpack
- webpack-cli
- webpack-merge
- webpackの共通設定をマージしてくれて、見やすくなる。
- webpack-node-externals
- サーバー側をバンドルする時に使う、node_modulesを無視して外部関数として扱うようにバンドルしてくれる
- nodemon-webpack-plugin
- 開発時に便利なnodemonのwebpack用プラグイン
- typescript
- ts-loader
- @types/express
- @types/mongoose
- @types/node
- nodeのhttpモジュールも一部使うときに使用する型定義
- @types/socket.io
- socket.ioのサーバーで動く機能の型定義
- @types/socket.io-client
- socket.ioのブラウザで動く機能の型定義
- @types/webpack
-
webpack.config.js
で補完が効くようにするためだけなので、なくてもOK
-
- css-loader
- bootstrapのcssをjsから読み取るので必要
- style-loader
- bootstrapのcssをjsから読み取るので必要
webpack.config.js
webpack.config.js
const merge = require("webpack-merge");
const nodeExternals = require("webpack-node-externals");
const NodemonPlugin = require("nodemon-webpack-plugin");
/** @type import("webpack").Configuration */
const baseConfig = {
mode: process.env.NODE_ENV || "development",
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
},
],
},
resolve: {
extensions: [".js", ".ts"],
},
};
module.exports = [
// ブラウザで動く機能をバンドル
merge(baseConfig, {
entry: "./src/client",
output: {
filename: "client.js",
path: `${__dirname}/dist`,
},
module: {
rules: [
{
// bootstrapのcssを読み込む際に必要
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
}
}),
// Nodeサーバーで動く機能をバンドル
merge(baseConfig, {
entry: "./src/server",
output: {
filename: "server.js",
path: `${__dirname}/dist`,
},
target: "node",
node: {
// expressを使うときにはこの設定をしないと失敗します
// 参考:https://medium.com/@binyamin/creating-a-node-express-webpack-app-with-dev-and-prod-builds-a4962ce51334
__dirname: false,
__filename: false,
},
externals: [
// webpackで生成したファイルを、`node bundle.js` と実行する場合に
// node_modulesのファイルを一緒にバンドルしている必要はないので、
// node_modulesを無視して外部関数として扱うようにバンドルしてくれる
nodeExternals()
],
plugins: [
// nodeのサーバ用のjsファイルを編集した時に自動でサーバを再起動してくれる
// nodemonのwebpack用のプラグイン
new NodemonPlugin(),
],
}),
];
tsconfig.json
こちらを参考にして設定しました。間違っていたら教えていただきたいです。m(_ _)m
- https://webpack.js.org/guides/typescript/
- https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"strict": true,
"allowJs": true,
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
サーバー側を作成
src/server.ts
import * as express from "express";
import * as http from "http";
import * as net from "net";
import * as socketio from "socket.io";
import * as mongoose from "mongoose";
import { ISendMsg } from "./global";
interface ISendMsgModel extends mongoose.Document, ISendMsg {}
const app: express.Express = express();
const server: http.Server = http.createServer(app);
const io: socketio.Server = socketio(server);
let Message: mongoose.Model<ISendMsgModel>;
(async () => {
try {
await mongoose.connect("mongodb://localhost/simple-chat");
Message = mongoose.model<ISendMsgModel>("Message", new mongoose.Schema({ name: String, message: String }))
} catch (err) {
console.log(err)
}
})();
app.use(express.static(`${__dirname}/../`));
app.get("/messages", async (req: express.Request, res: express.Response) => {
try {
const messages = await Message.find({});
res.send(messages);
} catch (err) {
console.log(err)
}
})
io.on("connection", (socket: socketio.Socket) => {
socket.on("message", async (msg: ISendMsg) => {
try {
const message = new Message(msg);
await message.save();
io.emit("message", msg);
} catch (err) {
console.log(err)
}
});
})
server.listen(3000, () => {
console.log(`server is running on port ${(server.address() as net.AddressInfo).port}`);
});
メッセージの内容は共通の設定なのでglobal.d.ts
に定義してみました。
src/global.d.ts
export interface ISendMsg {
name: string;
message: string;
}
ブラウザ側を作成
src/client.ts
import * as io from "socket.io-client";
import "bootstrap/dist/css/bootstrap.min.css";
import { ISendMsg } from "./global";
const input: HTMLInputElement = <HTMLInputElement>document.getElementById("name");
const message: HTMLTextAreaElement = <HTMLTextAreaElement>document.getElementById("message");
const send: HTMLButtonElement = <HTMLButtonElement>document.getElementById("send");
const messages: HTMLDivElement = <HTMLDivElement>document.getElementById("messages");
const socket: SocketIOClient.Socket = io();
(async () => {
const res = await fetch("http://localhost:3000/messages");
const data: ISendMsg[] = await res.json();
data.forEach(addMessages);
})();
send.addEventListener("click", () => {
socket.emit("message", { name: input.value, message: message.value });
});
socket.on("message", addMessages)
function addMessages({ name, message }: ISendMsg) {
messages.insertAdjacentHTML("beforeend", `
<h4> ${name} </h4>
<p> ${message} </p>
`);
}
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Hello, world!</title>
<script src="dist/client.js" defer></script>
</head>
<body>
<div class="container p-5">
<div class="jumbotron">
<h1 class="display-4">Send Message</h1>
<br>
<input id="name" class="form-control" placeholder="Name">
<br>
<textarea id="message" class="form-control" placeholder="Your Message Here"></textarea>
<br>
<button id="send" class="btn btn-success">Send</button>
</div>
<div id="messages"></div>
</div>
</body>
</html>
実行
yarn webpack -w #http://localhost:3000/ をブラウザで開く
簡易チャットが動かすことができました。
MongoDBのGUIクライアントでStudio 3Tを使用しながら作成させていただきました。m(_ _)m
最後まで読んでいただいてありがとうございました。m(_ _)m