Skip to content
mxHeadless
REST API gateway for headless frontends on MODX 3. Resources, objects, OpenAPI, API keys, and OAuth
  1. Extras
  2. mxHeadless
  3. Examples
  4. SvelteKit

SvelteKit

Server fetch in +page.server.ts and an API proxy.

Env

.env:

env
PUBLIC_MXHEADLESS_BASE_URL=https://example.com/api/v1
MXHEADLESS_API_KEY=mxh_...

Server helper

src/lib/server/mxheadless.ts (import only from +page.server.ts / +server.ts):

ts
import { env } from '$env/dynamic/private'
import { env as publicEnv } from '$env/dynamic/public'

type Envelope<T> = {
  data: T
  meta?: Record<string, unknown>
  links?: Record<string, string>
}

const baseURL = publicEnv.PUBLIC_MXHEADLESS_BASE_URL ?? env.MXHEADLESS_BASE_URL
const apiKey = env.MXHEADLESS_API_KEY

export async function mxGet<T>(
  fetchFn: typeof fetch,
  path: string,
  query?: Record<string, string | number | boolean>,
): Promise<Envelope<T>> {
  const url = new URL(path.replace(/^\//, ''), baseURL.endsWith('/') ? baseURL : baseURL + '/')
  if (query) {
    for (const [k, v] of Object.entries(query)) {
      url.searchParams.set(k, String(v))
    }
  }

  const res = await fetchFn(url, {
    headers: {
      Accept: 'application/json',
      ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
    },
  })

  if (!res.ok) {
    throw new Error(`mxHeadless ${res.status}`)
  }

  return res.json()
}

Pass event.fetch so SvelteKit tracks the request during SSR.

Page by URI

src/routes/[...slug]/+page.server.ts:

ts
import { error } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
import { mxGet } from '$lib/server/mxheadless'

export const load: PageServerLoad = async ({ params, fetch }) => {
  const uri = `${params.slug}.html`

  try {
    const page = await mxGet<Record<string, unknown>>(fetch, `/pages/${encodeURIComponent(uri)}`, {
      fields: 'id,pagetitle,content,uri',
    })
    return { page: page.data }
  } catch {
    error(404, 'Page not found')
  }
}

+page.svelte:

svelte
<script lang="ts">
  let { data } = $props()
</script>

<article>
  <h1>{data.page.pagetitle}</h1>
  <!-- Sanitize HTML before {@html} -->
  <div>{data.page.content}</div>
</article>

API proxy

src/routes/api/news/+server.ts:

ts
import { json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'
import { mxGet } from '$lib/server/mxheadless'

export const GET: RequestHandler = async ({ fetch, url }) => {
  const parent = url.searchParams.get('parent') ?? '2'
  const body = await mxGet(fetch, '/resources', {
    'filter[published]': 1,
    'filter[parent]': parent,
    limit: 20,
    fields: 'id,pagetitle,uri',
  })
  return json(body)
}

See also