SvelteKit + Cloudflare — Create an API Endpoint
Ensure some of your web app’s code stays firmly on the server and never sees the client.
Deploying from SvelteKit to Cloudflare is an incredible way to create a frontend. But what about when you want more than client-side code? What if you have sensitive data you want kept from the client? Or large amounts of information you don’t want to embed? That’s where Cloudflare’s backend workers come in.
In this tutorial, I’ll explain how they work and show you how to leverage them.
Prerequisites + Background
The jumping in point for this article is the jumping off point of my last SvelteKit + Cloudflare article:
The Best Frontend — SvelteKit + Tailwind + Cloudflare
If you’re starting fresh, read that. Or if you can’t be bothered, here’s what you’ll need:
- A Cloudflare account.
- A GitHub account.
- A SvelteKit website, written in TypeScript, held in a repository on your GitHub account that’s connected to the Cloudflare CI/CD pipeline.
- A Cloudflare Pages deployment based on your GitHub repo, and deployed live.
If you’re interested, you can see my Cloudflare website here:
What Are Cloudflare Workers?
Cloudflare Workers are serverless functions that run on Cloudflare’s edge network. They allow you to execute JavaScript (or TypeScript) closer to your users, without managing traditional servers. Workers can deliver web pages to the client, or handle sensitive operations, such as accessing a database or making API calls. SvelteKit puts server side code in backend Workers so the client cannot see the source.
Workers are perfect for:
- Handling sensitive logic that shouldn’t be exposed to the client.
- Processing large datasets without bloating the client-side bundle.
- Interacting with external APIs or databases securely.
- Customising responses based on user data or geolocation.
Background and prerequisites aside, let’s start building!
Step 1 — Create an API Route
You can create a backend function in SvelteKit by adding a route and placing a +server.ts file inside it. The code contained in this file will be run on the server-side only, keeping any important secrets safe from the client.
SvelteKit recommends using the route /api to store your server-side functions. To set this up, run the following from the root directory:
mkdir src/routes/api
Next choose the name of your API endpoint by adding another folder:
mkdir src/routes/api/<your-endpoint-name>
Add the +server.ts file with:
touch src/routes/api/<your-endpoint-name>/+server.ts
There you have it, your new api route fully set up and ready to be filled with code.
Step 2 — Write Your API Function
An API route can handle all of the standard HTTP methods:
GETPOSTPUT- … (etc.)
To use one, you export it from the +server.ts file as a constant. Here are examples for GET, and POST that produce an array of numbers.
GET
For GET commands, you typically extract data from the search params before acting on it. Like so:
import { json } from '@sveltejs/kit';
You can see we parse the params out of the URL search parameters, then use them in our calculations before returning a numberArray as JSON. You can call this endpoint in Typescript like this:
async function getNumberArray() {
const response = await fetch('/api/<your-endpoint-name>?min=1&max=100', {
method: 'GET',
});
let data = await response.json();
console.log(data.numberArray);
}
POST
For POST commands, your information is sent via the body, so you receive it like this:
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
// Request type for clarity
interface Request {
min: number
max: number
}
export const POST: RequestHandler = async ({ request }) => {
const { min, max } = await request.json() as Request;
const numberArray: number[] = [];
for (let i = min; i < max; i++) {
numberArray.push(i);
}
return json({numberArray}, {status: 200})
};
You can call the endpoint like this:
async function add() {
const response = await fetch('/api/<your-endpoint-name>', {
method: 'POST',
body: JSON.stringify({ min: 1, max: 10}),
headers: {
'content-type': 'application/json'
}
});
let data = response.json();
console.log(data.numberArray);
}
Step 3 — Using Data
You can store data you want to use in the server-side function inside of src/lib like client-side data. As long as you don’t import it anywhere in a function that touches the frontend, your data will only ever be present on the server side.
This is great if you have reasonably static data and would rather bake it into your endpoint to reduce latency. This is what I have initially done for my full-stack gaming website.
Step 4 — Deploy and Test!
You can test in two ways. Firstly, you can test locally with:
npm run dev
Which starts your site on https://localhost:5173, or you can push your new API endpoint to GitHub and deploy it straight to Cloudflare. Then test it by calling the route from curl with:
curl "https://your-website.pages.dev/api/<your-endpoint-name>?min=1&max=100"
for a GET request. Or:
curl -X POST -H "Content-Type: application/json" -d '{"min":1,"max":100}' http://your-website.pages.dev/api/<your-endpoint-name>
for a POST request.
Extra — Why Does the Code Stay on the Backend?
It’s all to do with the Cloudflare adapter we’ve set up in svelte.config.js. The SvelteKit adapter tells it how to compile your Svelte project, following standard rules. The two important ones for us are:
- Anything in a
+page.sveltefile is client-side. It should be compiled into a package and sent to the client when a user visits its route as defined by the directory tree. - Anything in a
+server.tsfile is server-side. It should be compiled into JS and deployed privately on a Cloudflare worker accessed by an endpoint whose route is defined by the directory tree.
That’s it. We have the file in server.ts, ergo the deployment step shoves it in a backend worker and never sends it to the client.
Final Words
The end! I hope you enjoyed this and it helps you quickly set up and understand backend API endpoints in SvelteKit + Cloudflare. If you want to know more, subscribe! I regularly publish battery-included tutorials on practical Fullstack Engineering.
Catch you in the next one!