メインコンテンツへスキップ
#Web制作 約7分で読めます

Astro 5.1 Server Islands で WebSocket 通知システムを実装する完全ガイド【2026年版】

Astro 5.1 の Server Islands と WebSocket を組み合わせてリアルタイム通知システムを構築する実装手順を解説。部分的 SSR で配信遅延を最小化する方法を詳しく紹介します。

はじめに

リアルタイム通知システムは、ダッシュボード・管理画面・SaaS アプリケーションにおいて必須の機能です。従来は React や Vue などの SPA フレームワークで WebSocket を実装することが一般的でしたが、Astro 5.1 の Server Islands を活用することで、通知コンポーネントだけを動的に配信し、他のページは完全静的化できるようになりました。

本記事では、Astro 5.1 の Server Islands と WebSocket を組み合わせて、軽量かつ高速なリアルタイム通知システムを構築する手順を解説します。

Astro Server Islands と WebSocket を組み合わせる利点

通知だけを動的レンダリングしてパフォーマンスを保つ

Server Islands を使うことで、通知コンポーネントだけを SSR で動的配信し、ページ本体は静的に保つことができます。これにより以下のメリットがあります。

  • 初回表示が高速: ページ本体は CDN から配信されるため LCP が改善する
  • SEO に影響しない: 静的コンテンツはクローラーが即座にインデックス可能
  • サーバー負荷を最小化: 通知コンポーネントだけがサーバーサイドで処理される

WebSocket でリアルタイム配信を実現

通知は WebSocket を使ってプッシュ配信 することで、ポーリングよりも効率的にリアルタイム性を確保できます。

  • 新規通知が発生した瞬間に配信
  • サーバーとクライアント間の通信が双方向
  • 接続が確立されている間、常に最新状態を保つ
sequenceDiagram
    participant User
    participant AstroPage[Astro ページ(静的)]
    participant ServerIsland[Server Islands<br/>(通知コンポーネント)]
    participant WebSocketServer[WebSocket サーバー]
    participant Database[データベース]

    User->>AstroPage: ページアクセス
    AstroPage-->>User: 静的コンテンツを配信(CDN経由)
    AstroPage->>ServerIsland: 通知コンポーネントをリクエスト
    ServerIsland->>Database: 初期通知データ取得
    Database-->>ServerIsland: 通知リスト
    ServerIsland-->>User: 通知コンポーネントを配信
    User->>WebSocketServer: WebSocket接続確立
    WebSocketServer-->>User: 接続完了
    Database->>WebSocketServer: 新規通知発生
    WebSocketServer->>User: 通知をプッシュ配信
    User->>User: UIを即座に更新

上記の図は、Astro Server Islands と WebSocket を組み合わせたリアルタイム通知システムの処理フローを示しています。ページ本体は静的配信され、通知コンポーネントだけが動的にレンダリングされます。

実装手順

1. Astro プロジェクトに Server Islands を有効化

まず、Astro 5.1 以降で Server Islands を有効化します。

npm install astro@latest

astro.config.mjs で SSR モードを有効化し、Server Islands をサポートするアダプターを指定します。

// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'hybrid', // hybrid または server モード
  adapter: node({ mode: 'standalone' }),
  experimental: {
    serverIslands: true, // Server Islands を有効化
  },
});

2. WebSocket サーバーを Node.js で構築

通知配信用の WebSocket サーバーを別プロセスで起動します。ここでは ws ライブラリを使用します。

npm install ws
// websocket-server.js
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('新しい接続が確立されました');

  // 接続時に初期通知を送信
  ws.send(JSON.stringify({
    type: 'init',
    message: 'WebSocket接続が確立されました',
  }));

  // クライアントからのメッセージを受信
  ws.on('message', (data) => {
    console.log('受信:', data.toString());
  });

  // 接続が切断されたとき
  ws.on('close', () => {
    console.log('接続が切断されました');
  });
});

// 5秒ごとに全クライアントに通知を配信(例)
setInterval(() => {
  wss.clients.forEach((client) => {
    if (client.readyState === 1) { // OPEN状態
      client.send(JSON.stringify({
        type: 'notification',
        title: '新しい通知',
        message: `現在の時刻: ${new Date().toLocaleTimeString('ja-JP')}`,
        timestamp: Date.now(),
      }));
    }
  });
}, 5000);

console.log('WebSocketサーバーがポート8080で起動しました');

サーバーを起動します。

node websocket-server.js

3. Server Islands で通知コンポーネントを実装

通知コンポーネントを Server Islands として定義します。

---
// src/components/NotificationIsland.astro
// Server Islands として動的配信されるコンポーネント
export const prerender = false; // 静的ビルドを無効化

// サーバーサイドで初期通知データを取得
const initialNotifications = [
  { id: 1, title: 'システム通知', message: 'ようこそ!', timestamp: Date.now() },
];
---

<div id="notification-container" class="notification-panel">
  <h3>通知</h3>
  <ul id="notification-list">
    {initialNotifications.map((notif) => (
      <li key={notif.id}>
        <strong>{notif.title}</strong>: {notif.message}
      </li>
    ))}
  </ul>
</div>

<script>
  // クライアントサイドで WebSocket 接続
  const ws = new WebSocket('ws://localhost:8080');

  ws.onopen = () => {
    console.log('WebSocket接続が確立されました');
  };

  ws.onmessage = (event) => {
    const data = JSON.parse(event.data);

    if (data.type === 'notification') {
      // 新しい通知を DOM に追加
      const list = document.getElementById('notification-list');
      const li = document.createElement('li');
      li.innerHTML = `<strong>${data.title}</strong>: ${data.message}`;
      list.prepend(li); // 最新の通知を上に表示
    }
  };

  ws.onerror = (error) => {
    console.error('WebSocketエラー:', error);
  };

  ws.onclose = () => {
    console.log('WebSocket接続が切断されました');
  };
</script>

<style>
  .notification-panel {
    position: fixed;
    top: 20px;
    right: 20px;
    width: 300px;
    max-height: 400px;
    overflow-y: auto;
    background: white;
    border: 1px solid #ccc;
    border-radius: 8px;
    padding: 16px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  }

  #notification-list {
    list-style: none;
    padding: 0;
    margin: 0;
  }

  #notification-list li {
    padding: 8px 0;
    border-bottom: 1px solid #eee;
  }
</style>

4. ページに Server Islands を配置

通知コンポーネントを server:defer ディレクティブで配置します。

---
// src/pages/index.astro
import NotificationIsland from '../components/NotificationIsland.astro';
---

<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>リアルタイム通知デモ</title>
  </head>
  <body>
    <h1>Astro Server Islands + WebSocket</h1>
    <p>このページは静的に配信されます。通知コンポーネントだけが動的です。</p>

    <!-- Server Islands として配信 -->
    <NotificationIsland server:defer />
  </body>
</html>

server:defer を指定することで、ページ本体は即座に配信され、通知コンポーネントは非同期で後から読み込まれます

5. 開発サーバーで動作確認

npm run dev

ブラウザで http://localhost:4321 にアクセスすると、ページは即座に表示され、数秒後に通知コンポーネントが表示されます。5秒ごとに新しい通知がリアルタイムで追加されます。

本番環境へのデプロイ

Cloudflare Workers へのデプロイ

Cloudflare Workers では WebSocket サーバーを Durable Objects で実装します。

npm install @astrojs/cloudflare
// astro.config.mjs
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare(),
  experimental: {
    serverIslands: true,
  },
});

Durable Objects を使った WebSocket サーバーの実装例は Cloudflare の公式ドキュメントを参照してください。

Vercel へのデプロイ

Vercel では WebSocket サーバーを別サービス(例: Railway, Render)で運用し、Astro アプリから接続します。

npm install @astrojs/vercel
// astro.config.mjs
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';

export default defineConfig({
  output: 'hybrid',
  adapter: vercel(),
  experimental: {
    serverIslands: true,
  },
});

環境変数 WEBSOCKET_URL を設定し、クライアント側で読み込むようにします。

実装時の注意点

WebSocket 接続の再接続処理を実装する

ネットワーク切断時に自動再接続する仕組みを追加します。

let ws;
let reconnectInterval = 1000;

function connectWebSocket() {
  ws = new WebSocket('ws://localhost:8080');

  ws.onopen = () => {
    console.log('接続成功');
    reconnectInterval = 1000; // 再接続間隔をリセット
  };

  ws.onclose = () => {
    console.log('接続が切断されました。再接続を試みます...');
    setTimeout(connectWebSocket, reconnectInterval);
    reconnectInterval = Math.min(reconnectInterval * 2, 30000); // 最大30秒
  };

  ws.onmessage = (event) => {
    // 通知処理
  };
}

connectWebSocket();

Server Islands のキャッシュ戦略

通知コンポーネントは常に最新データを取得する必要があるため、キャッシュを無効化します。

---
export const prerender = false;

// キャッシュ無効化ヘッダーを設定
Astro.response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate');
---

通知の既読管理

通知を既読にする機能を実装する場合、クライアント側で既読状態を localStorage に保存し、サーバー側の API で永続化します。

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.type === 'notification') {
    const notificationId = data.id;
    const isRead = localStorage.getItem(`notif_${notificationId}`);

    if (!isRead) {
      // 新しい通知として表示
      addNotificationToDOM(data);
    }
  }
};

function markAsRead(notificationId) {
  localStorage.setItem(`notif_${notificationId}`, 'true');
  // サーバーにも既読状態を送信
  fetch(`/api/notifications/${notificationId}/read`, { method: 'POST' });
}

まとめ

Astro 5.1 の Server Islands と WebSocket を組み合わせることで、以下のメリットを持つリアルタイム通知システムを構築できます。

  • ページ本体は静的配信で高速
  • 通知コンポーネントだけを動的配信
  • WebSocket でリアルタイム性を確保
  • SEO・パフォーマンスへの影響を最小化

この実装パターンは、ダッシュボード・管理画面・SaaS アプリケーションなど、リアルタイム性が求められる画面で特に有効です。従来の SPA よりも軽量で、SSG の利点を損なわずにリアルタイム機能を追加できます。

参考リンク

#Astro #Server Islands #WebSocket #リアルタイム通知 #SSR
シェア