07 - API Routes
What are API Routes?
API routes are server-side endpoints defined in +server.ts files. They handle HTTP requests and return JSON or other responses — like building a REST API inside your SvelteKit app.
Basic API Route
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
const posts = [
{ id: 1, title: 'First Post' },
{ id: 2, title: 'Second Post' }
];
return json(posts); // json() sets Content-Type and serializes automatically
};Access at: GET http://localhost:5173/api/posts
HTTP Methods
Export named functions matching HTTP methods — GET, POST, PUT, PATCH, DELETE:
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url }) => {
const id = url.searchParams.get('id'); // query params: /api/posts?id=1
return json({ id });
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json(); // parse JSON body
return json({ created: data }, { status: 201 });
};
export const PUT: RequestHandler = async ({ request }) => {
const data = await request.json();
return json({ updated: data });
};
export const DELETE: RequestHandler = async ({ url }) => {
const id = url.searchParams.get('id');
return json({ deleted: id });
};Dynamic API Routes
Use [param] folders just like page routes:
// src/routes/api/posts/[id]/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ params }) => {
const post = await db.posts.find(params.id); // params.id from URL
if (!post) {
error(404, 'Post not found'); // throws an error response
}
return json(post);
};Access at: GET http://localhost:5173/api/posts/42
Request Headers
Read incoming headers from the request:
import { error, json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ request }) => {
const auth = request.headers.get('authorization');
if (!auth) {
error(401, 'Unauthorized');
}
return json({ authenticated: true });
};Response Headers
Set custom headers on the response:
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
return json(
{ data: 'value' },
{
headers: {
'Cache-Control': 'max-age=3600'
}
}
);
};Non-JSON Responses
Return plain text, HTML, or any other format using the standard Response object:
import type { RequestHandler } from './$types';
// Plain text
export const GET: RequestHandler = async () => {
return new Response('Plain text');
};// HTML
export const GET: RequestHandler = async () => {
return new Response('<h1>HTML</h1>', {
headers: { 'Content-Type': 'text/html' }
});
};Error Handling
Use try/catch and error() for proper HTTP error responses:
import { error, json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ params }) => {
try {
const data = await fetchData(params.id);
return json(data);
} catch (e) {
error(500, 'Internal server error');
}
};CORS Headers
For cross-origin access, set CORS headers manually. Handle the OPTIONS preflight request too:
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
return json(
{ data: 'value' },
{
headers: {
'Access-Control-Allow-Origin': '*'
}
}
);
};
// Preflight request — browsers send OPTIONS before cross-origin POST/PUT/DELETE
export const OPTIONS: RequestHandler = async () => {
return new Response(null, {
headers: {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
};Key Points
+server.tsfiles define API endpoints — no+page.svelteneeded- Export
GET,POST,PUT,DELETEetc. as named functions - Use
json()helper for JSON responses,new Response()for anything else error()throws HTTP error responses- Same dynamic routing (
[param]) as pages - These run server-side only — safe for DB access and secrets