このアプリについて
このチュートリアルでは、Node.jsとExpressをバックエンドに、GraphQLをデータ取得に、htmxをフロントエンドに使用したリアルタイムチャットアプリを作成します。
また、他のユーザーがチャットに参加しているかどうか、メッセージを読んだかどうかも確認できる機能を追加します。
必要なツール
- Node.js
- npm
- Express
- Apollo Server (GraphQL)
- htmx
各技術の説明
Node.js: JavaScriptランタイムで、サーバーサイドのスクリプトを実行するために使用されます。
Express: Node.js上で動作するウェブアプリケーションフレームワークで、APIの作成やルーティングに便利です。
GraphQL: データの取得と操作を行うためのクエリ言語で、クライアントが必要なデータだけを取得できます。
htmx: HTML属性を使って、サーバーからのデータを直接DOMに反映させることができるライブラリです。
プロジェクトのセットアップ
Node.jsとExpressのインストール
mkdir chat-app
cd chat-app
npm init -y
npm install express apollo-server-express graphql htmx
サーバーの設定
server.js
を作成し、ExpressとApollo Serverを設定します。
const express = require('express');
const { ApolloServer, gql, PubSub } = require('apollo-server-express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const pubsub = new PubSub();
const MESSAGE_ADDED = 'MESSAGE_ADDED';
const typeDefs = gql`
type Message {
id: ID!
content: String!
user: String!
readBy: [String!]
}
type Query {
messages: [Message]
}
type Mutation {
addMessage(content: String!, user: String!): Message
markAsRead(messageId: ID!, user: String!): Message
}
type Subscription {
messageAdded: Message
}
`;
let messages = [];
const resolvers = {
Query: {
messages: () => messages,
},
Mutation: {
addMessage: (_, { content, user }) => {
const message = { id: messages.length + 1, content, user, readBy: [] };
messages.push(message);
pubsub.publish(MESSAGE_ADDED, { messageAdded: message });
return message;
},
markAsRead: (_, { messageId, user }) => {
const message = messages.find(msg => msg.id == messageId);
if (message && !message.readBy.includes(user)) {
message.readBy.push(user);
}
return message;
}
},
Subscription: {
messageAdded: {
subscribe: () => pubsub.asyncIterator([MESSAGE_ADDED])
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`Server ready at http://localhost:4000${server.graphqlPath}`)
);
フロントエンドの設定
index.html
を作成し、htmxを使用してチャットインターフェースを構築します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>チャットアプリ</title>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script src="https://unpkg.com/htmx.org/dist/ext/ws.js"></script>
</head>
<body>
<div id="chat">
<div hx-get="/messages" hx-trigger="load" hx-swap="innerHTML"></div>
<form hx-post="/addMessage" hx-trigger="submit" hx-swap="beforeend">
<input type="text" name="content" required>
<input type="hidden" name="user" value="ユーザー名">
<button type="submit">送信</button>
</form>
</div>
<div id="read-status">
<div hx-get="/readStatus" hx-trigger="load" hx-swap="innerHTML"></div>
</div>
</body>
</html>
メッセージの取得と追加
サーバー側でメッセージの取得と追加をハンドリングするエンドポイントを設定します。
app.get('/messages', (req, res) => {
res.send(messages.map(msg => `<div>${msg.user}: ${msg.content}</div>`).join(''));
});
app.post('/addMessage', (req, res) => {
const { content, user } = req.body;
const message = { id: messages.length + 1, content, user, readBy: [] };
messages.push(message);
res.send(`<div>${user}: ${content}</div>`);
});
メッセージの既読状態の管理
メッセージが読まれたかどうかを管理するためのエンドポイントを追加します。
app.post('/markAsRead', (req, res) => {
const { messageId, user } = req.body;
const message = messages.find(msg => msg.id == messageId);
if (message && !message.readBy.includes(user)) {
message.readBy.push(user);
}
res.send(message);
});
app.get('/readStatus', (req, res) => {
res.send(messages.map(msg => `<div>${msg.user}: ${msg.content} - Read by: ${msg.readBy.join(', ')}</div>`).join(''));
});
まとめ
このチュートリアルでは、Node.jsとExpressをバックエンドに、GraphQLをデータ取得に、htmxをフロントエンドに使用したチャットアプリを作成しました。
また、他のユーザーがチャットに参加しているかどうか、メッセージを読んだかどうかも確認できる機能を追加しました。
詳細なコードと設定により、リアルタイムでメッセージを送受信できるシンプルなチャットアプリを構築しました。