Lesson 31-Deno Advanced Application Development

Server-Side Rendering (SSR)

Basic SSR Implementation

Deno.serve with Template Engine Integration

// ssr_basic.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { renderToString } from "https://deno.land/x/dejs@0.10.0/mod.ts";

// Simulated data fetch
async function getPostData(id: string) {
  return { id, title: `Post ${id}`, content: "This is post content" };
}

// Page template
const template = `
<!DOCTYPE html>
<html>
<head>
  <title>{{ post.title }}</title>
</head>
<body>
  <h1>{{ post.title }}</h1>
  <p>{{ post.content }}</p>
</body>
</html>
`;

// SSR handler
async function handleRequest(req: Request) {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  // 1. Fetch data
  const post = await getPostData(postId);

  // 2. Render HTML
  const html = await renderToString(template, { post });

  // 3. Return response
  return new Response(html, {
    headers: { "Content-Type": "text/html" }
  });
}

// Start server
serve(handleRequest, { port: 8000 });
console.log("SSR server running on http://localhost:8000");

Modern Framework Integration Example (ABC)

// ssr_abc.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";

const app = new Application();
const router = new Router();

// Simulated data store
const posts = new Map([
  ["1", { id: "1", title: "First Post", content: "Hello World" }],
  ["2", { id: "2", title: "Second Post", content: "Deno SSR" }]
]);

// SSR route
router.get("/post/:id", async (ctx) => {
  const post = posts.get(ctx.params.id);
  if (!post) {
    ctx.response.status = 404;
    return;
  }

  // Use ABC's built-in template engine
  await ctx.render("post.ejs", { post });
});

app.use(router.routes());
app.use(router.allowedMethods());

console.log("ABC SSR server running on http://localhost:8000");
await app.listen({ port: 8000 });

Data Prefetching and State Synchronization

Client-Server State Synchronization

// ssr_state_sync.ts
// Server-side code
async function handleRequest(req: Request) {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  // 1. Fetch data
  const post = await getPostData(postId);

  // 2. Render HTML and inject initial state
  const initialState = JSON.stringify({ post });
  const html = `
  <!DOCTYPE html>
  <html>
  <head>
    <title>${post.title}</title>
    <script>window.__INITIAL_STATE__ = ${initialState};</script>
    <script src="/client.js"></script>
  </head>
  <body>
    <div id="app">
      <h1>${post.title}</h1>
      <p>${post.content}</p>
    </div>
  </body>
  </html>
  `;

  return new Response(html);
}

// Client-side code (client.js)
const app = {
  init() {
    const state = window.__INITIAL_STATE__;
    if (state) {
      // Use server-injected state
      document.getElementById("app").innerHTML = `
        <h1>${state.post.title}</h1>
        <p>${state.post.content}</p>
      `;
    } else {
      // Fetch data client-side
      fetch(`/api/post?id=${window.location.search.split("=")[1]}`)
        .then(res => res.json())
        .then(data => {
          document.getElementById("app").innerHTML = `
            <h1>${data.post.title}</h1>
            <p>${data.post.content}</p>
          `;
        });
    }
  }
};

app.init();

SSR Performance Optimization

Caching Strategy Implementation

// ssr_caching.ts
// In-memory cache implementation
class SSRCache {
  private cache = new Map();
  private ttl = 60 * 1000; // 1-minute cache

  async get(key: string, fallback: () => Promise<string>) {
    const entry = this.cache.get(key);
    if (entry && Date.now() - entry.timestamp < this.ttl) {
      return entry.html;
    }

    const html = await fallback();
    this.cache.set(key, { html, timestamp: Date.now() });
    return html;
  }

  invalidate(key: string) {
    this.cache.delete(key);
  }
}

// SSR handler with caching
const cache = new SSRCache();

async function handleRequest(req: Request) {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  return new Response(
    await cache.get(`post:${postId}`, async () => {
      const post = await getPostData(postId);
      return renderToString(template, { post });
    }),
    { headers: { "Content-Type": "text/html" } }
  );
}

Streaming Rendering Optimization

// ssr_streaming.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

async function handleRequest(req: Request) {
  const { writable, readable } = new TransformStream();
  const writer = writable.getWriter();

  // Immediately send HTML header
  await writer.write(new TextEncoder().encode(`
  <!DOCTYPE html>
  <html>
  <head>
    <title>Streaming SSR</title>
  </head>
  <body>
  `));

  // Simulate async data fetch
  const postPromise = getPostData("1");

  // Send immediately displayable content
  await writer.write(new TextEncoder().encode(`
    <div>Loading...</div>
  `));

  // Send remaining content after data fetch
  const post = await postPromise;
  await writer.write(new TextEncoder().encode(`
    <h1>${post.title}</h1>
    <p>${post.content}</p>
  `));

  // Close HTML document
  await writer.write(new TextEncoder().encode(`
  </body>
  </html>
  `));

  await writer.close();

  return new Response(readable, {
    headers: { "Content-Type": "text/html" }
  });
}

serve(handleRequest, { port: 8000 });

SSR Security and Error Handling

Security Protection Measures

// ssr_security.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

// XSS protection function
function escapeHtml(unsafe: string): string {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "<")
    .replace(/>/g, ">")
    .replace(/"/g, """)
    .replace(/'/g, "'");
}

// Error handling middleware
async function errorHandler(fn: (req: Request) => Promise<Response>) {
  return async (req: Request) => {
    try {
      return await fn(req);
    } catch (err) {
      console.error("SSR Error:", err);
      return new Response(
        "500 Internal Server Error",
        { status: 500 }
      );
    }
  };
}

// Secure SSR handler
const safeHandler = errorHandler(async (req: Request) => {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  // Input validation
  if (!/^\d+$/.test(postId)) {
    return new Response("Invalid post ID", { status: 400 });
  }

  const post = await getPostData(postId);

  // Secure rendering
  const html = `
  <!DOCTYPE html>
  <html>
  <head>
    <title>${escapeHtml(post.title)}</title>
  </head>
  <body>
    <h1>${escapeHtml(post.title)}</h1>
    <p>${escapeHtml(post.content)}</p>
  </body>
  </html>
  `;

  return new Response(html);
});

serve(safeHandler, { port: 8000 });

SSR Application Scenarios

SEO Optimization Implementation

// ssr_seo.ts
async function handleRequest(req: Request) {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  // Fetch complete page data
  const post = await getPostData(postId);
  const relatedPosts = await getRelatedPosts(postId);

  // Render complete HTML (including all SEO elements)
  const html = `
  <!DOCTYPE html>
  <html>
  <head>
    <title>${post.title}</title>
    <meta name="description" content="${post.content.slice(0, 160)}">
    <meta name="keywords" content="blog, post, ${post.title}">
    <meta name="robots" content="index, follow">
  </head>
  <body>
    <article>
      <h1>${post.title}</h1>
      <p>${post.content}</p>
    </article>
    <section>
      <h2>Related Posts</h2>
      <ul>
        ${relatedPosts.map(p => `
          <li><a href="/post?id=${p.id}">${p.title}</a></li>
        `).join("")}
      </ul>
    </section>
  </body>
  </html>
  `;

  return new Response(html, {
    headers: {
      "Content-Type": "text/html",
      "X-Robots-Tag": "index, follow" // SEO directive
    }
  });
}

First-Screen Performance Optimization

// ssr_performance.ts
async function handleRequest(req: Request) {
  const url = new URL(req.url);
  const postId = url.searchParams.get("id") || "1";

  // 1. Start response immediately
  const { writable, readable } = new TransformStream();
  const writer = writable.getWriter();

  // 2. Send critical CSS and HTML skeleton first
  await writer.write(new TextEncoder().encode(`
  <!DOCTYPE html>
  <html>
  <head>
    <title>Loading...</title>
    <style>
      body { font-family: Arial; }
      .loading { text-align: center; }
    </style>
  </head>
  <body>
    <div id="app" class="loading">Loading...</div>
  `));

  // 3. Fetch data and render remaining content in parallel
  const postPromise = getPostData(postId);

  // 4. Send immediately displayable content
  await writer.write(new TextEncoder().encode(`
    <p>Loading content...</p>
  `));

  // 5. Update DOM after data fetch
  const post = await postPromise;
  await writer.write(new TextEncoder().encode(`
    <script>
      document.getElementById("app").innerHTML = \`
        <h1>${post.title}</h1>
        <p>${post.content}</p>
      \`;
    </script>
  `));

  // 6. Close document
  await writer.write(new TextEncoder().encode(`
  </body>
  </html>
  `));

  await writer.close();

  return new Response(readable, {
    headers: { "Content-Type": "text/html" }
  });
}

Static Site Generation (SSG)

Basic SSG Implementation

Deno.build with File Generation

// ssg_basic.ts
import { ensureDir, writeTextFile } from "https://deno.land/std@0.224.0/fs/mod.ts";

// Simulated content data
const posts = [
  { id: "1", title: "First Post", content: "Hello World" },
  { id: "2", title: "Second Post", content: "Deno SSG" }
];

// Template function
function renderPostTemplate(post: typeof posts[0]) {
  return `
  <!DOCTYPE html>
  <html>
  <head>
    <title>${post.title}</title>
  </head>
  <body>
    <h1>${post.title}</h1>
    <p>${post.content}</p>
  </body>
  </html>
  `;
}

// Generate static files
async function generateStaticSite() {
  // Ensure output directory exists
  await ensureDir("./dist/posts");

  // Generate post pages
  for (const post of posts) {
    const content = renderPostTemplate(post);
    await writeTextFile(`./dist/posts/${post.id}.html`, content);
  }

  // Generate index page
  const indexContent = `
  <!DOCTYPE html>
  <html>
  <head>
    <title>Blog Posts</title>
  </head>
  <body>
    <h1>Blog Posts</h1>
    <ul>
      ${posts.map(p => `
        <li><a href="/posts/${p.id}.html">${p.title}</a></li>
      `).join("")}
    </ul>
  </body>
  </html>
  `;
  await writeTextFile("./dist/index.html", indexContent);

  console.log("Static site generated in ./dist");
}

generateStaticSite();

Content Management and Route Generation

Dynamic Route Generator

// ssg_routing.ts
interface ContentItem {
  id: string;
  title: string;
  content: string;
  tags?: string[];
}

// Simulated content data
const contents: ContentItem[] = [
  { id: "1", title: "Deno Introduction", content: "Deno is a modern runtime" },
  { id: "2", title: "SSG Guide", content: "Static Site Generation guide" },
  { id: "3", title: "Web Development", content: "Modern web dev techniques", tags: ["web", "dev"] }
];

// Generate route configuration
function generateRoutes(contents: ContentItem[]) {
  // 1. Generate post routes
  const postRoutes = contents.map(item => ({
    path: `/posts/${item.id}`,
    template: "post",
    data: item
  }));

  // 2. Generate tag routes
  const tags = new Set<string>();
  contents.forEach(item => {
    if (item.tags) {
      item.tags.forEach(tag => tags.add(tag));
    }
  });

  const tagRoutes = Array.from(tags).map(tag => ({
    path: `/tags/${tag}`,
    template: "tag",
    data: { tag, items: contents.filter(item => item.tags?.includes(tag)) }
  }));

  // 3. Generate index route
  const indexRoute = {
    path: "/",
    template: "index",
    data: { items: contents }
  };

  return [indexRoute, ...postRoutes, ...tagRoutes];
}

// Generate static site
async function generateSite() {
  const routes = generateRoutes(contents);
  await ensureDir("./dist");

  for (const route of routes) {
    let content = "";
    switch (route.template) {
      case "post":
        content = `
        <!DOCTYPE html>
        <html>
        <head>
          <title>${route.data.title}</title>
        </head>
        <body>
          <h1>${route.data.title}</h1>
          <p>${route.data.content}</p>
        </body>
        </html>
        `;
        break;
      case "tag":
        content = `
        <!DOCTYPE html>
        <html>
        <head>
          <title>Posts tagged with ${route.data.tag}</title>
        </head>
        <body>
          <h1>Posts tagged with ${route.data.tag}</h1>
          <ul>
            ${route.data.items.map(item => `
              <li><a href="/posts/${item.id}.html">${item.title}</a></li>
            `).join("")}
          </ul>
        </body>
        </html>
        `;
        break;
      case "index":
        content = `
        <!DOCTYPE html>
        <html>
        <head>
          <title>Blog Posts</title>
        </head>
        <body>
          <h1>Blog Posts</h1>
          <ul>
            ${route.data.items.map(item => `
              <li><a href="/posts/${item.id}.html">${item.title}</a></li>
            `).join("")}
          </ul>
        </body>
        </html>
        `;
        break;
    }

    const outputPath = route.path === "/" 
      ? "./dist/index.html" 
      : `./dist${route.path}.html`;

    await ensureDir(new URL(outputPath, import.meta.url).pathname.split("/").slice(0, -1).join("/"));
    await writeTextFile(outputPath, content);
  }

  console.log("Static site with dynamic routes generated");
}

generateSite();

SSG Performance Optimization

Incremental Build Implementation

// ssg_incremental.ts
import { walk } from "https://deno.land/std@0.224.0/fs/walk.ts";

// Track file modification times
const fileTimestamps = new Map<string, number>();

// Initialize file timestamps
async function initFileTimestamps(dir: string) {
  for await (const entry of walk(dir)) {
    if (entry.isFile) {
      fileTimestamps.set(entry.path, entry.mtime?.getTime() || 0);
    }
  }
}

// Check if file needs rebuilding
function needsRebuild(filePath: string): boolean {
  const currentMtime = Deno.statSync(filePath).mtime?.getTime() || 0;
  const lastMtime = fileTimestamps.get(filePath) || 0;
  return currentMtime > lastMtime;
}

// Update file timestamp
function updateTimestamp(filePath: string) {
  fileTimestamps.set(filePath, Deno.statSync(filePath).mtime?.getTime() || 0);
}

// Incremental site build
async function incrementalBuild(contentDir: string, outputDir: string) {
  await initFileTimestamps(contentDir);

  // 1. Check for content file changes
  const contentFiles = Array.from(fileTimestamps.keys())
    .filter(path => path.startsWith(contentDir));

  const changedFiles = contentFiles.filter(needsRebuild);

  if (changedFiles.length === 0) {
    console.log("No changes detected, skipping build");
    return;
  }

  console.log(`Rebuilding ${changedFiles.length} changed files`);

  // 2. Rebuild affected pages
  // (Simplified; actual logic should determine affected pages based on content changes)
  await generateSite(); // Implement finer-grained update logic in practice

  // 3. Update all file timestamps
  for (const path of fileTimestamps.keys()) {
    updateTimestamp(path);
  }
}

// Usage example
incrementalBuild("./content", "./dist");

SSG and CMS Integration

Headless CMS Integration Example

// ssg_cms.ts
interface CmsPost {
  id: string;
  title: string;
  content: string;
  publishedAt: string;
}

// Fetch content from CMS
async function fetchCmsContent(): Promise<CmsPost[]> {
  // Should use actual CMS API
  const response = await fetch("https://cms.example.com/api/posts");
  if (!response.ok) {
    throw new Error("Failed to fetch CMS content");
  }
  return response.json();
}

// Generate static site
async function generateSiteFromCms() {
  try {
    const posts = await fetchCmsContent();

    // Generate post pages
    for (const post of posts) {
      const content = `
      <!DOCTYPE html>
      <html>
      <head>
        <title>${post.title}</title>
      </head>
      <body>
        <h1>${post.title}</h1>
        <p>${post.content}</p>
        <p>Published: ${new Date(post.publishedAt).toLocaleDateString()}</p>
      </body>
      </html>
      `;

      await writeTextFile(`./dist/posts/${post.id}.html`, content);
    }

    // Generate index page
    const indexContent = `
    <!DOCTYPE html>
    <html>
    <head>
      <title>Blog Posts</title>
    </head>
    <body>
      <h1>Blog Posts</h1>
      <ul>
        ${posts.map(p => `
          <li><a href="/posts/${p.id}.html">${p.title}</a></li>
        `).join("")}
      </ul>
    </body>
    </html>
    `;

    await writeTextFile("./dist/index.html", indexContent);

    console.log("Static site generated from CMS content");
  } catch (err) {
    console.error("Failed to generate site:", err);
  }
}

generateSiteFromCms();

SSG Application Scenarios

Documentation Site Generation

// ssg_docs.ts
interface DocPage {
  id: string;
  title: string;
  content: string;
  navOrder: number;
}

// Documentation data
const docs: DocPage[] = [
  { id: "getting-started", title: "Getting Started", content: "# Getting Started\n\nInstall Deno...", navOrder: 1 },
  { id: "ssg-guide", title: "SSG Guide", content: "# SSG Guide\n\nStatic Site Generation with Deno...", navOrder: 2 },
  { id: "api-reference", title: "API Reference", content: "# API Reference\n\nDetailed API documentation...", navOrder: 3 }
];

// Generate documentation site
async function generateDocsSite() {
  // 1. Generate documentation pages
  for (const doc of docs.sort((a, b) => a.navOrder - b.navOrder)) {
    const content = `
    <!DOCTYPE html>
    <html>
    <head>
      <title>${doc.title}</title>
    </head>
    <body>
      <nav>
        <ul>
          ${docs.sort((a, b) => a.navOrder - b.navOrder)
            .map(d => `
              <li><a href="/docs/${d.id}.html">${d.title}</a></li>
            `).join("")}
        </ul>
      </nav>
      <main>
        ${doc.content}
      </main>
    </body>
    </html>
    `;

    await writeTextFile(`./dist/docs/${doc.id}.html`, content);
  }

  // 2. Generate index page
  const indexContent = `
  <!DOCTYPE html>
  <html>
  <head>
    <title>Documentation</title>
  </head>
  <body>
    <h1>Documentation</h1>
    <ul>
      ${docs.sort((a, b) => a.navOrder - b.navOrder)
        .map(d => `
          <li><a href="/docs/${d.id}.html">${d.title}</a></li>
        `).join("")}
    </ul>
  </body>
  </html>
  `;

  await writeTextFile("./dist/index.html", indexContent);

  // 3. Copy static assets
  await ensureDir("./dist/styles");
  // Should copy CSS/JS assets in practice

  console.log("Documentation site generated");
}

generateDocsSite();

Real-Time Application Development

WebSocket Real-Time Communication

Basic WebSocket Implementation

// websocket_basic.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

const server = serve({ port: 8000 });
console.log("WebSocket server running on ws://localhost:8000");

// Store all connected clients
const clients = new Set<WebSocket>();

for await (const req of server) {
  // Upgrade to WebSocket connection
  const { socket, response } = Deno.upgradeWebSocket(req);

  // Add to client set
  clients.add(socket);
  console.log(`New client connected. Total: ${clients.size}`);

  // Message handling
  socket.onmessage = (e) => {
    console.log("Received message:", e.data);

    // Broadcast message to all clients
    for (const client of clients) {
      if (client !== socket && client.readyState === WebSocket.OPEN) {
        client.send(e.data);
      }
    }
  };

  // Connection close handling
  socket.onclose = () => {
    clients.delete(socket);
    console.log(`Client disconnected. Total: ${clients.size}`);
  };

  // Error handling
  socket.onerror = (e) => {
    console.error("WebSocket error:", e);
    clients.delete(socket);
  };
}

Room/Channel Implementation

// websocket_rooms.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

// Room manager
class RoomManager {
  private rooms: Map<string, Set<WebSocket>> = new Map();

  // Join room
  joinRoom(roomId: string, socket: WebSocket) {
    if (!this.rooms.has(roomId)) {
      this.rooms.set(roomId, new Set());
    }
    this.rooms.get(roomId)?.add(socket);
    console.log(`Client joined room ${roomId}. Total: ${this.rooms.get(roomId)?.size || 0}`);
  }

  // Leave room
  leaveRoom(roomId: string, socket: WebSocket) {
    this.rooms.get(roomId)?.delete(socket);
    console.log(`Client left room ${roomId}. Total: ${this.rooms.get(roomId)?.size || 0}`);
  }

  // Broadcast message to room
  broadcast(roomId: string, message: string) {
    this.rooms.get(roomId)?.forEach(socket => {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(message);
      }
    });
  }
}

const roomManager = new RoomManager();

const server = serve({ port: 8000 });
console.log("WebSocket room server running on ws://localhost:8000");

for await (const req of server) {
  const { socket, response } = Deno.upgradeWebSocket(req);

  // Client sends join room message
  socket.onmessage = (e) => {
    try {
      const data = JSON.parse(e.data);
      if (data.type === "join") {
        roomManager.joinRoom(data.roomId, socket);
      } else if (data.type === "message") {
        roomManager.broadcast(data.roomId, e.data);
      }
    } catch (err) {
      console.error("Message parse error:", err);
    }
  };

  socket.onclose = () => {
    // Should track rooms for each socket in practice
    // Simplified handling here
    console.log("Client disconnected");
  };
}

GraphQL and Deno Integration

Apollo Server Implementation

// graphql_apollo.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";
import { ApolloServer } from "https://deno.land/x/apollo@0.8.0/mod.ts";
import { gql } from "https://deno.land/x/graphql_tag@0.1.2/mod.ts";

// Define GraphQL Schema
const typeDefs = gql`
  type Post {
    id: ID!
    title: String!
    content: String!
  }

  type Query {
    posts: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createPost(title: String!, content: String!): Post!
  }
`;

// Simulated data store
const posts = new Map<string, { id: string; title: string; content: string }>();

// Resolver implementation
const resolvers = {
  Query: {
    posts: () => Array.from(posts.values()),
    post: (_: any, args: { id: string }) => posts.get(args.id),
  },
  Mutation: {
    createPost: (_: any, args: { title: string; content: string }) => {
      const id = Date.now().toString();
      const post = { id, title: args.title, content: args.content };
      posts.set(id, post);
      return post;
    },
  },
};

// Create Apollo Server
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// Integrate with ABC framework
const app = new Application();
const router = new Router();

server.applyMiddleware({ app });

router.get("/", (ctx) => {
  ctx.response.body = "GraphQL Server running";
});

app.use(router.routes());
app.use(router.allowedMethods());

console.log("GraphQL server running on http://localhost:8000/graphql");
await app.listen({ port: 8000 });

Subscription Functionality Implementation

// graphql_subscriptions.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";
import { ApolloServer } from "https://deno.land/x/apollo@0.8.0/mod.ts";
import { gql } from "https://deno.land/x/graphql_tag@0.1.2/mod.ts";
import { PubSub } from "https://deno.land/x/apollo@0.8.0/deps.ts";

// PubSub implementation
const pubsub = new PubSub();

// Define Schema with subscriptions
const typeDefs = gql`
  type Post {
    id: ID!
    title: String!
    content: String!
  }

  type Query {
    posts: [Post!]!
  }

  type Mutation {
    createPost(title: String!, content: String!): Post!
  }

  type Subscription {
    postCreated: Post!
  }
`;

// Resolver implementation
const resolvers = {
  Query: {
    posts: () => Array.from(posts.values()),
  },
  Mutation: {
    createPost: (_: any, args: { title: string; content: string }) => {
      const id = Date.now().toString();
      const post = { id, title: args.title, content: args.content };
      posts.set(id, post);

      // Publish event
      pubsub.publish("POST_CREATED", { postCreated: post });

      return post;
    },
  },
  Subscription: {
    postCreated: {
      subscribe: () => pubsub.asyncIterator(["POST_CREATED"]),
    },
  },
};

// Create Apollo Server (with subscriptions)
const server = new ApolloServer({
  typeDefs,
  resolvers,
  subscriptions: {
    path: "/subscriptions",
  },
});

// Integrate with ABC framework
const app = new Application();
const router = new Router();

server.applyMiddleware({ app });

router.get("/", (ctx) => {
  ctx.response.body = "GraphQL Server with Subscriptions running";
});

app.use(router.routes());
app.use(router.allowedMethods());

console.log("GraphQL server with subscriptions running");
console.log("HTTP endpoint: http://localhost:8000/graphql");
console.log("WS endpoint: ws://localhost:8000/subscriptions");

await app.listen({ port: 8000 });

Real-Time Collaborative Editing

Simplified CRDT Algorithm Implementation

// crdt_basic.ts
// Simplified CRDT character list implementation
class CRDTDocument {
  private characters: Array<{ id: string; value: string; siteId: string; counter: number }> = [];
  private siteId: string;
  private counter: number = 0;

  constructor(siteId: string) {
    this.siteId = siteId;
  }

  // Local insert
  localInsert(position: number, value: string): void {
    this.counter++;
    const charId = `${this.siteId}-${this.counter}`;
    this.characters.splice(position, 0, {
      id: charId,
      value,
      siteId: this.siteId,
      counter: this.counter,
    });
  }

  // Remote insert
  remoteInsert(char: { id: string; value: string; siteId: string; counter: number }): void {
    // Find insertion position
    let insertAt = this.characters.findIndex(
      (c) => c.counter > char.counter || (c.counter === char.counter && c.siteId > char.siteId)
    );
    if (insertAt === -1) {
      insertAt = this.characters.length;
    }
    this.characters.splice(insertAt, 0, char);
  }

  // Local delete
  localDelete(position: number): void {
    this.characters.splice(position, 1);
  }

  // Remote delete (by ID)
  remoteDelete(charId: string): void {
    const index = this.characters.findIndex((c) => c.id === charId);
    if (index !== -1) {
      this.characters.splice(index, 1);
    }
  }

  // Get document content
  getContent(): string {
    return this.characters.map((c) => c.value).join("");
  }
}

// Usage example
const doc1 = new CRDTDocument("site1");
const doc2 = new CRDTDocument("site2");

// Site 1 local inserts
doc1.localInsert(0, "H");
doc1.localInsert(1, "e");
doc1.localInsert(2, "l");
doc1.localInsert(3, "l");
doc1.localInsert(4, "o");

// Site 2 local inserts (concurrent editing)
doc2.localInsert(0, "H");
doc2.localInsert(1, "i"); // Different content
doc2.localInsert(2, "l");
doc2.localInsert(3, "l");
doc2.localInsert(4, "o");

// Sync operations (simulate network transfer)
// Site 1 receives Site 2's operations
doc1.remoteInsert({ id: "site2-1", value: "H", siteId: "site2", counter: 1 });
doc1.remoteInsert({ id: "site2-2", value: "i", siteId: "site2", counter: 2 });
doc1.remoteInsert({ id: "site2-3", value: "l", siteId: "site2", counter: 3 });
doc1.remoteInsert({ id: "site2-4", value: "l", siteId: "site2", counter: 4 });
doc1.remoteInsert({ id: "site2-5", value: "o", siteId: "site2", counter: 5 });

// Final content (merged result)
console.log("Merged content:", doc1.getContent()); // Should include all characters

Complete Collaborative Editor Implementation

// collab_editor.ts
// Simplified real-time collaborative editor
class CollaborativeEditor {
  private document = new CRDTDocument(Deno.env.get("SITE_ID") || "local");
  private clients = new Set<WebSocket>();
  private wsServer: Deno.HttpServer;

  constructor(port: number) {
    this.wsServer = serve({ port });
    this.setupWebSocketServer();
  }

  private setupWebSocketServer() {
    for await (const req of this.wsServer) {
      const { socket, response } = Deno.upgradeWebSocket(req);
      this.clients.add(socket);

      // Send current document state to new client
      socket.onopen = () => {
        socket.send(JSON.stringify({
          type: "init",
          content: this.document.getContent(),
        }));
      };

      // Handle client operations
      socket.onmessage = (e) => {
        try {
          const message = JSON.parse(e.data);
          if (message.type === "insert") {
            this.document.localInsert(message.position, message.value);
            this.broadcast({
              type: "insert",
              position: message.position,
              value: message.value,
              charId: message.charId,
            });
          } else if (message.type === "delete") {
            this.document.localDelete(message.position);
            this.broadcast({
              type: "delete",
              position: message.position,
              charId: message.charId,
            });
          }
        } catch (err) {
          console.error("Message error:", err);
        }
      };

      // Forward remote operations
      const forwardMessage = (message: any) => {
        if (socket.readyState === WebSocket.OPEN) {
          socket.send(JSON.stringify(message));
        }
      };

      // Receive and forward remote operations to other clients
      socket.onmessage = (e) => {
        const message = JSON.parse(e.data);
        if (message.type === "insert") {
          this.document.remoteInsert({
            id: message.charId,
            value: message.value,
            siteId: message.siteId,
            counter: message.counter,
          });
          this.clients.forEach((client) => {
            if (client !== socket) forwardMessage(message);
          });
        } else if (message.type === "delete") {
          this.document.remoteDelete(message.charId);
          this.clients.forEach((client) => {
            if (client !== socket) forwardMessage(message);
          });
        }
      };

      // Connection close handling
      socket.onclose = () => {
        this.clients.delete(socket);
      };
    }
  }

  // Broadcast message to all clients
  private broadcast(message: any) {
    this.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(message));
      }
    });
  }
}

// Usage example
const editor = new CollaborativeEditor(8080);
console.log("Collaborative editor server running on ws://localhost:8080");

// Simulate client operation (should be initiated by frontend in practice)
setTimeout(() => {
  // Simulate insert operation
  const charId = `server-${Date.now()}`;
  editor["document"].localInsert(0, "H");
  editor.broadcast({
    type: "insert",
    position: 0,
    value: "H",
    charId,
    siteId: "server",
    counter: 1,
  });
}, 1000);

Real-Time Communication Performance Optimization

WebSocket Message Compression

// websocket_compression.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { compress, decompress } from "https://deno.land/std@0.224.0/encoding/gzip.ts";

const server = serve({ port: 8000 });
console.log("Compressed WebSocket server running on ws://localhost:8000");

// Store client connections and compression state
const clients = new Map<WebSocket, { compressor: TextEncoder; decompressor: TextDecoder }>();

for await (const req of server) {
  const { socket, response } = Deno.upgradeWebSocket(req);

  // Initialize compressor
  const compressor = new TextEncoder();
  const decompressor = new TextDecoder();
  clients.set(socket, { compressor, decompressor });

  // Message receive handling (with decompression)
  socket.onmessage = async (e) => {
    try {
      // Should use more efficient compression (e.g., pako) in practice
      const decompressed = decompressor.decode(await decompress(e.data));
      console.log("Received:", decompressed);

      // Process message...
      const response = `Echo: ${decompressed}`;

      // Send compressed response (simplified example)
      const compressed = await compress(compressor.encode(response));
      socket.send(compressed);
    } catch (err) {
      console.error("Message error:", err);
    }
  };

  // Connection close handling
  socket.onclose = () => {
    clients.delete(socket);
  };
}

Batch Message Processing

// websocket_batching.ts
class MessageBatcher {
  private batch: any[] = [];
  private batchSize: number;
  private flushInterval: number;
  private timer: number | null = null;

  constructor(batchSize: number = 10, flushInterval: number = 100) {
    this.batchSize = batchSize;
    this.flushInterval = flushInterval;
    this.startFlushTimer();
  }

  addMessage(message: any) {
    this.batch.push(message);
    if (this.batch.length >= this.batchSize) {
      this.flush();
    }
  }

  private startFlushTimer() {
    this.timer = setInterval(() => {
      if (this.batch.length > 0) {
        this.flush();
      }
    }, this.flushInterval) as unknown as number;
  }

  private flush() {
    if (this.batch.length === 0) return;

    // Send batch messages
    const batchToSend = [...this.batch];
    this.batch = [];

    // Should send via WebSocket in practice
    console.log("Flushing batch:", batchToSend);
    // socket.send(JSON.stringify(batchToSend));
  }

  destroy() {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }
}

// Usage example
const batcher = new MessageBatcher(5, 50); // Flush every 5 messages or 50ms

// Simulate message generation
setInterval(() => {
  batcher.addMessage({ type: "update", data: Date.now() });
}, 10);

Real-Time Application Security and Permission Control

WebSocket Authentication and Authorization

// websocket_auth.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

// Simulated user database
const users = new Map<string, { token: string; permissions: string[] }>([
  ["user1", { token: "token1", permissions: ["read", "write"] }],
  ["user2", { token: "token2", permissions: ["read"] }],
]);

const server = serve({ port: 8000 });
console.log("Authenticated WebSocket server running on ws://localhost:8000");

for await (const req of server) {
  // 1. Get token from query parameters
  const url = new URL(req.url);
  const token = url.searchParams.get("token");

  // 2. Validate token
  let user;
  for (const [_, u] of users) {
    if (u.token === token) {
      user = u;
      break;
    }
  }

  if (!token || !user) {
    req.respond({ status: 401, body: "Unauthorized" });
    continue;
  }

  // 3. Upgrade to WebSocket connection
  const { socket, response } = Deno.upgradeWebSocket(req);

  // 4. Permission check example
  socket.onmessage = (e) => {
    try {
      const message = JSON.parse(e.data);

      // Check write permission
      if (message.type === "write" && !user.permissions.includes("write")) {
        socket.send(JSON.stringify({
          type: "error",
          message: "Permission denied"
        }));
        return;
      }

      // Process message...
      console.log("Authorized message:", message);
      socket.send(JSON.stringify({ type: "ack" }));
    } catch (err) {
      console.error("Message error:", err);
    }
  };

  socket.onclose = () => {
    console.log(`User ${token} disconnected`);
  };
}

Rate Limiting Implementation

// websocket_rate_limit.ts
class RateLimiter {
  private requests: Map<string, { count: number; timestamp: number }> = new Map();
  private windowSize: number; // Milliseconds
  private maxRequests: number;

  constructor(windowSize: number = 1000, maxRequests: number = 10) {
    this.windowSize = windowSize;
    this.maxRequests = maxRequests;
  }

  checkLimit(key: string): boolean {
    const now = Date.now();
    const record = this.requests.get(key);

    if (!record) {
      this.requests.set(key, { count: 1, timestamp: now });
      return true;
    }

    if (now - record.timestamp > this.windowSize) {
      // New time window
      this.requests.set(key, { count: 1, timestamp: now });
      return true;
    }

    if (record.count >= this.maxRequests) {
      return false; // Exceeds limit
    }

    // Increment count
    this.requests.set(key, { count: record.count + 1, timestamp: record.timestamp });
    return true;
  }
}

// Usage example
const limiter = new RateLimiter(1000, 5); // Max 5 messages per second

const server = serve({ port: 8000 });
console.log("Rate limited WebSocket server running on ws://localhost:8000");

for await (const req of server) {
  const { socket, response } = Deno.upgradeWebSocket(req);

  socket.onmessage = (e) => {
    const clientIp = req.conn.remoteAddr.hostname; // Simplified; use more reliable client ID in practice

    if (!limiter.checkLimit(clientIp)) {
      socket.send(JSON.stringify({
        type: "error",
        message: "Too many requests"
      }));
      return;
    }

    // Process message...
    console.log("Processing message:", e.data);
    socket.send(JSON.stringify({ type: "ack" }));
  };
}
Share your love