Endpoints and Server Data
By Syncfusion®, Inc. · Last updated
What endpoints are, .js and .ts endpoint files, HTTP method functions, reading JSON and form data, returning JSON and status codes, static and on-demand endpoints, the APIRoute type, RSS feeds, databases in server code, and advanced routing with src/fetch.ts.
Endpoints
- A page answers with HTML. Sometimes you need to answer with data instead: JSON, an RSS feed, or plain text.
- An endpoint is a
.jsor.tsfile insrc/pagesthat returns aResponseinstead of HTML. - Scripts, apps and other websites can read it, like any API.
- In this lesson, Asha builds a small API for her blog.
Simple lesson: a page returns HTML, an endpoint returns data.
A Real-World Example
- Asha's friend wants to show Asha's latest posts on his own site.
- He does not want to copy HTML. He wants a list of posts as JSON.
- Asha adds
src/pages/api/posts.json.ts. It returns the list at/api/posts.json.
Press ▶ Run. Then pick /api/posts.json in the address bar.
What happened:
- The file name
posts.json.tsbecame the address/api/posts.json. The.tsis dropped. - The
GETfunction ran during the build and returned aResponse. - The Terminal shows "static endpoint". The JSON file was built like a page.
What You Will Learn
In this lesson, you will learn:
- How to write an endpoint in JavaScript or TypeScript.
- What the HTTP method functions are.
- How to read JSON and form data.
- How to return JSON with the right status code.
- Static endpoints vs on-demand endpoints.
- How to build an RSS feed.
- Where database code belongs.
Endpoint Basics
What Endpoints Are
- Endpoint = a route file that exports functions named after HTTP methods.
- HTTP method = the kind of request:
GET(read),POST(send),PUTorPATCH(change),DELETE(remove). - Each function receives a context and returns a
Response.
[JS] Creating an Endpoint as a .js File
// src/pages/api/hello.json.js
export function GET() {
return Response.json({ message: "Hello" });
}
[TS] Creating an Endpoint as a .ts File
// src/pages/api/hello.json.ts
import type { APIRoute } from "astro";
export const GET: APIRoute = () => {
return Response.json({ message: "Hello" });
};
APIRoutetells your editor what the context holds and what to return.
HTTP Method Functions
- Export one function per method:
GET,POST,PUT,PATCH,DELETE. ALLanswers any method that has no function of its own.- A request with a method you did not export gets a
405 Method Not Allowed.
Method names are written in capitals: GET, not get. Astro looks for those exact names.
The Context
The function's argument has the same tools as Astro in a page:
params= values from[name]in the file name.request= the request: method, headers, body.url= the full address, withsearchParams.cookies,localsandredirect()= the same as in pages.
Simple lesson: export a function per HTTP method. It gets a context and returns a Response.
Handling Data
Reading JSON and Form Data
await request.json()= the body, when the sender sent JSON.await request.formData()= the fields, when a form sent the request.- Both only work in on-demand endpoints. A static endpoint is built before any request has a body.
Press ▶ Run. Fill the form and send it. The endpoint reads the form fields and answers with JSON.
- The browser sent the fields as form data.
formData()read them by theirname.
Returning JSON and Status Codes
Response.json(data, { status: 201 })returns JSON with a status, in one line.Common status codes:
200OK,201Created,400Bad Request (the input is wrong),404Not Found,405Method Not Allowed,500Server Error.
- Always check the input, and answer
400when it is wrong.
Press ▶ Run. Click both buttons. The endpoint counts likes, and refuses a bad request with 400.
In this lab, likes lives in a variable, so it resets on each Run. A real server can restart at any time, and serverless hosts start a fresh copy for many requests. Keep real data in a database.
Simple lesson: read the body with request.json() or formData(), check it, and answer with a fitting status.
Endpoints and Rendering Modes
Static Endpoints
- By default, an endpoint is static, like a page.
- Its
GETruns once at build time. The result is saved as a file, such asdist/api/posts.json. - Great for data that changes only when you rebuild: feeds, search indexes, sitemaps.
- A static endpoint with
[param]needsgetStaticPaths(), like a page.
On-Demand Endpoints
export const prerender = false;makes it run on every request.- Needed for
POST, for reading cookies or headers, and for fresh data. - Needs an adapter, like on-demand pages.
[TS] The APIRoute Type
APIRoutetypes the context:params,request,url,cookies,locals.- Use
APIRoute<{ id: string }>style types only when you need them. The plain type is enough for most endpoints.
Press ▶ Run. One endpoint file makes one JSON file per post, at build time.
Simple lesson: endpoints are static by default. Make them on-demand when they need the request.
RSS Feeds
What an RSS Feed Is
- RSS feed = an XML file that lists your newest posts.
- Feed readers, like Feedly, check it and tell readers when you post something new.
Creating an RSS Feed with @astrojs/rss
npm install @astrojs/rss
// src/pages/rss.xml.ts
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context) {
const posts = await getCollection("blog");
return rss({
title: "Asha's Dev Journal",
description: "Notes from my coding journey",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
link: "/blog/" + post.id + "/",
})),
});
}
context.sitecomes fromsiteinastro.config.mjs. Feeds need full links, so set it.
Generating the Feed from a Collection
- The feed is a static endpoint, so it is rebuilt with the site.
- Add a link in your layout's
<head>so browsers can find it:<link rel="alternate" type="application/rss+xml" href="/rss.xml" />.
The lab cannot install @astrojs/rss, so this lab writes the same XML by hand. Press ▶ Run and open /rss.xml.
Simple lesson: an RSS feed is a static endpoint. Use @astrojs/rss on your computer, and set site.
Databases and Advanced Routing
Where Database Code Belongs
- Database code needs secrets and a connection, so it must run on the server.
- Put it in endpoints, actions, middleware or the script of on-demand pages.
- Keep the connection in one file, such as
src/lib/db.ts, and import it where needed.
// src/lib/db.ts (Node.js 22.5+)
import { DatabaseSync } from "node:sqlite";
export const db = new DatabaseSync("journal.db");
db.exec("CREATE TABLE IF NOT EXISTS likes (slug TEXT PRIMARY KEY, count INTEGER)");
// src/pages/api/likes/[slug].ts
import { db } from "../../../lib/db";
export const prerender = false;
export function GET({ params }) {
const row = db.prepare("SELECT count FROM likes WHERE slug = ?").get(params.slug);
return Response.json({ likes: row?.count ?? 0 });
}
- Always pass values with
?placeholders. Never join user input into the SQL text. That stops SQL injection attacks. - For bigger projects, Drizzle ORM gives you typed queries with the same safety.
The Reserved src/fetch.ts File (Advanced)
- Astro 7 lets you replace the whole request pipeline with one
src/fetch.tsfile. This is called advanced routing. - It is for special cases, such as custom routers or framework bridges. Most sites never need it.
fetchFilein the config changes the file name.
Start with normal pages and endpoints. Reach for advanced routing only when you have a clear need the file-based routes cannot meet.
Simple lesson: database code runs on the server, in endpoints and actions. Use placeholders for user input.
TL;DR
- Endpoint = a
.jsor.tsfile insrc/pagesthat returns aResponse. - Address = file path without
.ts:api/posts.json.ts→/api/posts.json. - Methods = export
GET,POST,PUT,PATCH,DELETE, orALL. - Context =
params,request,url,cookies,locals,redirect(). - Body =
await request.json()orawait request.formData()(on-demand only). - Answer =
Response.json(data, { status }).400for bad input. - Static endpoint = built once (default). On-demand =
prerender = false. - RSS = a static endpoint with
@astrojs/rss. Setsite. - Databases = server code only. Use
?placeholders.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Reading request.json() in a static endpoint | Static endpoints get requests | Use prerender = false for bodies | The body is empty at build time |
Lower-case get | Any name works | Export GET in capitals | 405 Method Not Allowed |
| Returning an object | Astro turns it into JSON | Return Response.json(data) | Error: not a Response |
| No status for bad input | 200 is fine for errors | Return 400 with a message | Callers think it worked |
| User input in SQL text | Strings are safe | Use ? placeholders or an ORM | SQL injection |
Feed with no site | Relative links work in feeds | Set site in the config | Broken links in feed readers |
Check Yourself
Exercises
What to Remember
- An endpoint returns a
Response. Its address is its file path without.ts. - Export
GET,POSTand the other methods. UseResponse.json()and fitting status codes. - Endpoints are static by default. Bodies, cookies and fresh data need
prerender = false. - RSS feeds are static endpoints. Database code runs on the server, with placeholders.
Next: Middleware and Cookies, where code runs before every request.