Search code examples
fetchendpointsvelte-3sveltekit

Consuming external API using SvelteKit works but only after reloading route


Using SvelteKit 1.0.0-next.95 to get a JSON array back from an external API endpoint and display in a template like this:

<script context="module">
  export async function load({ fetch }) {
    const url = 'https://www.schoolhouseyoga.com/api/announcement'
    const res = await fetch(url, {
      method: 'GET',
      mode: 'cors',
      headers: {
        'content-type': 'application/json'
      }
    })

    if (res.ok) {
      return {
        props: {
          sections: await res.json()
        }
      }
    }

    return {
      status: res.status,
      error: new Error(`Could not load ${url}`)
     }
  }
</script>

<script>
  export let sections = []
</script>

<template>
  <section>
    {#if sections.length > 0}
      <div class="callout">
        <h1>Announcements</h1>
        {#each sections as section}
          <div>
            {#if section.announcements.length > 0}
              <h2>{section.section}</h2>
            {/if}
            {#each section.announcements as announcement}
              <p><b>{announcement.title} </b>- {@html announcement.description}</p>
            {/each}
          </div>
        {/each}
      </div>
    {/if}
  </section>
</template>

If you try https://www.schoolhouseyoga.com/api/announcement (CORS) in a browser or using curl, you'll get a JSON array with two elements.

When I run this in dev mode, npm run dev -- --open and navigate to this route on Safari 14.1 (macOS), I get a 500 error and the message, "Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin." If I try to navigate to that route on Google Chrome, I get a 500 error and "TypeError: Failed to fetch".

But with either browser, if I refresh the page, the data loads successfully. Navigating to a different route then back again, the error reappears.

I am guessing this has to do with SSR but not sure what to do about it.

Any thoughts?


Solution

  • The problem was related to server-side rendering and a CORS issue with the endpoint. When the server-side code performed the fetch, it worked fine. Subsequent fetches were being performed by the client (which ran into the CORS issue).

    While the endpoint appeared to have CORS enabled...

    import { Router } from 'express';
    import cors from 'cors';
    import * as controller from './announcement.controller';
    
    const router = Router();
    
    router.get('/', cors(), controller.index);
    

    But the endpoint was also using helmet and needed

    app.use(helmet.permittedCrossDomainPolicies());
    

    prior to loading the routes.

    Hope this helps others.