DEV Community

Alex Spinov
Alex Spinov

Posted on

Astro Has a Free API You Should Know About

Astro is not just a static site generator — it has powerful server-side capabilities including API endpoints, middleware, and server-side rendering. Here's what most developers overlook.

API Endpoints

Astro lets you create API routes that return JSON, XML, or any response:

// src/pages/api/posts.json.js
export async function GET({ request }) {
  const url = new URL(request.url)
  const tag = url.searchParams.get("tag")

  const posts = await db.post.findMany({
    where: tag ? { tags: { has: tag } } : undefined,
    orderBy: { publishedAt: "desc" }
  })

  return new Response(JSON.stringify(posts), {
    headers: { "Content-Type": "application/json" }
  })
}

export async function POST({ request }) {
  const body = await request.json()
  const post = await db.post.create({ data: body })
  return new Response(JSON.stringify(post), { status: 201 })
}
Enter fullscreen mode Exit fullscreen mode

Server-Side Rendering (SSR)

With an adapter, Astro pages become server-rendered:

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

export default defineConfig({
  output: "server",
  adapter: node({ mode: "standalone" })
})
Enter fullscreen mode Exit fullscreen mode
---
// src/pages/dashboard.astro
const user = await getUser(Astro.cookies.get("session")?.value)
if (!user) return Astro.redirect("/login")

const stats = await getUserStats(user.id)
---

<html>
<body>
  <h1>Welcome, {user.name}</h1>
  <div class="stats">
    <p>Posts: {stats.postCount}</p>
    <p>Views: {stats.totalViews}</p>
  </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Middleware

Astro middleware runs before every request:

// src/middleware.js
import { defineMiddleware, sequence } from "astro:middleware"

const auth = defineMiddleware(async (context, next) => {
  const token = context.cookies.get("session")?.value
  if (token) {
    context.locals.user = await verifyToken(token)
  }
  return next()
})

const logging = defineMiddleware(async (context, next) => {
  const start = Date.now()
  const response = await next()
  console.log(`${context.url.pathname} - ${Date.now() - start}ms`)
  return response
})

export const onRequest = sequence(auth, logging)
Enter fullscreen mode Exit fullscreen mode

Content Collections API

Astro's content collections provide a type-safe way to query local content:

// src/content/config.ts
import { defineCollection, z } from "astro:content"

const blog = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    date: z.date(),
    tags: z.array(z.string()),
    draft: z.boolean().default(false)
  })
})

export const collections = { blog }
Enter fullscreen mode Exit fullscreen mode
---
// src/pages/blog/index.astro
import { getCollection } from "astro:content"

const posts = await getCollection("blog", ({ data }) => !data.draft)
const sorted = posts.sort((a, b) => b.data.date - a.data.date)
---

<ul>
{sorted.map(post => (
  <li>
    <a href={`/blog/${post.slug}`}>{post.data.title}</a>
    <time>{post.data.date.toLocaleDateString()}</time>
  </li>
))}
</ul>
Enter fullscreen mode Exit fullscreen mode

Server Islands (Astro 4+)

Mix static and dynamic content on the same page:

---
// Static shell renders at build time
const staticContent = await getStaticData()
---

<html>
<body>
  <h1>{staticContent.title}</h1>

  <!-- This component renders on the server at request time -->
  <UserGreeting server:defer>
    <p slot="fallback">Loading...</p>
  </UserGreeting>

  <StaticFooter />
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • API endpoints in src/pages/api/ for REST APIs
  • SSR mode with adapters for Node, Deno, Cloudflare, Vercel
  • Middleware for auth, logging, rate limiting
  • Content Collections for type-safe local content queries
  • Server Islands for mixing static and dynamic content
  • Island Architecture — ship zero JS by default

Explore Astro docs for the complete reference.


Building web scrapers or data pipelines? Check out my Apify actors for ready-made solutions, or email spinov001@gmail.com for custom development.

Top comments (0)