FisherHub Blog
← 返回列表 | 开发实践

Astro SSR 实战:用 @astrojs/node 构建动态站点

从静态站点到全栈应用——SSR 模式下的 API 路由、Hono 集成和 Node 部署

为什么需要 SSR

Astro 默认是 SSG(静态生成),但以下场景需要 SSR:

  • 搜索功能:需要服务端处理查询
  • 用户认证:需要 Session/Cookie
  • 动态内容:实时更新的数据
  • API 接口:像 Express/Hono 一样写 API

切换到 SSR

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

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});

API 路由

src/pages/api/ 下创建 .ts 文件:

// src/pages/api/posts/search.ts
import type { APIRoute } from 'astro';
import { getCollection } from 'astro:content';

export const GET: APIRoute = async ({ request }) => {
  const url = new URL(request.url);
  const q = url.searchParams.get('q')?.toLowerCase() || '';
  
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  const results = posts.filter(p =>
    p.data.title.toLowerCase().includes(q) ||
    p.data.description.toLowerCase().includes(q)
  );

  return new Response(JSON.stringify({
    posts: results.slice(0, 10).map(p => ({
      title: p.data.title,
      description: p.data.description,
      slug: p.id.replace(/\.mdx$/, ''),
    })),
  }), {
    headers: { 'Content-Type': 'application/json' },
  });
};

Hono 集成替代方案

如果你需要更强大的路由(中间件、RPC、WebSocket),可以用 Hono:

// middleware.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';

const app = new Hono();
app.use('/api/*', cors());

app.get('/api/posts', async (c) => {
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  return c.json(posts.slice(0, 20));
});

export const onRequest = app.fetch;

部署

# 构建
npm run build

# Node standalone 模式
node dist/server/entry.mjs

# Docker
docker build -t fisherhub-blog .

SSR 让 Astro 从”静态站点生成器”变成了”全栈框架”。结合 Hono,你可以用同一套代码同时处理前端页面和后端 API。