Endpoints

In this module, we will learn how to create server-side routes using SvelteKit endpoints.
Table of Contents
EndpointsView the code for this module.
Course Version History
  • Nov. 21, 2022 - Updated to SvelteKit v1.0.0-next.549. Updated from using the old file based router to the new folder based router, changed HTTP verbs to uppercase, and updated to return the new Response object.

At this point in the course we've gone over routing to pages, but we can also route to endpoints. Endpoints are server-side routes, so They provide "backend" functionality within the SvelteKit application providing a great place to, for example, make an external API request.

Endpoints are modules written in .js or .ts files that export functions corresponding to HTTP methods. These endpoint files become API routes in our application.

Similarly to pages, you can define routes with a +server.js file, which gives you full control over the response. These are referred to as API routes, or endpoints. +server.js files export functions corresponding to HTTP verbs like GET, POST, PATCH, PUT and DELETE that take a RequestEvent argument and return a Response object. These endpoints live in the /src/routes/api directory. Let's create and endpoint that we can fetch product data from. In our api directory, let's add a server route (/src/api/getProduct/+server.js).

As mentioned earlier, endpoints export request handler functions corresponding to HTTP methods. Request handlers make it possible to read and write data that is only available on the server. For example, within this file we can export a GET, POST, PATCH, DELETE ... or any valid HTTP method. Endpoints also have access to fetch in case you need to request data from external APIs. For example, within our endpoint file, we can make a GET request on this route by exporting an async function called get like this.

export async function GET() {}

Now, we can reach out to a database and retrieve some data. Since we do not have a database set up, let's instead return an object with some hard coded data representing the response. In our example, let's declare our value product, and return the product in our body like this.

import { error } from '@sveltejs/kit';
 
export async function GET() {
  const product = {
		name: 'sticker'
		src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Cup-front-black.png?v=1623159405'
		price: '$10'
	};
  if (!product) {
    throw error(400, 'No product exists.');
  }
  return new Response(JSON.stringify(product));
}

The first argument to Response can be a ReadableStream which makes it possible to stream large amounts of data.

If an error is thrown (throw error(...) or an unexpected error), the response will be a JSON representation of the error or a fallback error page. This is different than the +error.svelte page discussed in the previous module. In this instance, the error page can be customised in src/error.html.

For security reasons, we typically need to be more mindful of what information we expose in client side code. However, since this is all server side, we can reach out directly to a database and use sensitive information here usually provided by environment variables, as an example.

By exporting POST/PUT/PATCH/DELETE handlers, +server.js files can be used to create a complete API, which we have done in the above example. We can receive data from this api using fetch from any page, component or layout like this:

let product = {};
 
async function getProduct() {
  const response = await fetch('/api/getProduct');
  product = await response.json();
}

Currently, our endpoint is only returning our sticker data. However, we want it to use a dynamic parameter like our product page does so we can dynamically fetch data associated with whatever product page we are currently on. We can use dynamic params with our endpoints like we do with pages. Let's re-name our endpoint /[product]/+server.js. Now when we fetch data from this endpoint, instead of fetching data from /api/getProduct we will fetch it from /api/[product] where [product] can be any product name. We will pass in the name of the product page we are currently on using $page.params like this:

import { page } from '$app/stores';
 
let product = {};
 
async function getProduct(name) {
  const response = await fetch(`/api/${name}`);
  product = await response.json();
}
 
$: {
  getProduct($page.params.name);
}

Now, we can access the product name passed into our endpoint using the params parameter like this:

import { error } from '@sveltejs/kit';
 
export async function GET({params}) {
  const productName = params.product;
  const product = {
		name: 'sticker'
		src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Cup-front-black.png?v=1623159405'
		price: '$10'
	};
  if (!product) {
    throw error(400, 'No product exists.');
  }
  return new Response(JSON.stringify(product));
}

Now we could use this to reach out to a database and fetch data associated with this specific product. We don't have a database set up, but for the sake of example that might look something like this.

import { error } from '@sveltejs/kit';
import db from database;
 
export async function GET({params}) {
  const productName = params.product;
  const product = db.collection.find(productName)
  if (!product) {
    throw error(400, 'No product exists.');
  }
  return new Response(JSON.stringify(product));
}

We are going add a hard coded array of products, and use productName to search this array and return the correct product to sort of mock a database.

import { error } from '@sveltejs/kit';
 
const products = [
  {
    name: 'cup',
    price: '$10',
    quantity: 1,
    src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Cup-front-black.png?v=1623159405',
  },
  {
    name: 'shirt',
    price: '$10',
    quantity: 1,
    src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/short-sleeve-t-shirt-0.png?v=1622902418',
  },
  {
    name: 'jacket',
    src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/bomber-jacket-0.png?v=1622902777',
    price: '$80.00',
    quantity: 1,
  },
  {
    name: 'sticker',
    src: 'https://cdn.shopify.com/s/files/1/0434/0285/4564/products/Sticker-mock.png?v=1623256356',
    price: '$8.00',
    quantity: 1,
  },
];
 
export async function GET({ params }) {
  const productName = params.product;
  let product = products.find((product) => product.name === productName);
 
  if (!product) {
    throw error(400, 'No product exists.');
  }
  return new Response(JSON.stringify(product));
}

As you can see, page endpoints allow you to run code server side in Svelte by exporting an async function. It is common for a page or layout to need to load data before it can be rendered. In the next module, we will learn a more convenient way to load data into pages and layouts.