3

Compatibility with Existing Standards

Some frameworks attempt to simplify your work as a developer by providing you with tools and functionality to wrap around common tasks, such as making network requests or managing data submitted by forms. While the intentions are noble, this strategy can have unintended consequences. For instance, when learning a new framework, developers have to master all of its intricacies to be effective. Reading about yet another way to make network requests can slow developers down, as time spent reading documentation is time spent not building. It can also prevent code portability. When the code written for application A is specific to framework X, then the code will need to be modified before being reused in application B, which was built with framework Y.

SvelteKit has a solution to this, and that solution is to do nothing. Well, not nothing, but rather than providing you with wrappers and functions that will require you to look up the documentation each time you go to use one, SvelteKit encourages the use of existing Web Application Programming Interfaces (APIs) and standards. By not reinventing the wheel, more developers can get started with SvelteKit quickly, since they won’t have to learn abstractions for standards that they’re already familiar with. Not only does it keep time spent reading documentation to a minimum, but it also requires less code to power a framework.

This chapter will cover some basic use cases of Web APIs and how they interact with SvelteKit. Specifically, we’ll look at these examples of current web standards:

  • Fetch
  • FormData
  • URL

An in-depth dive into each of these standards is beyond the scope of this chapter, but if you’re looking for more information about them, resources will be provided at the end. After these examples, you should feel comfortable using your existing knowledge of various web standards with SvelteKit.

Technical requirements

The complete code for this chapter is available on GitHub at: https://github.com/PacktPublishing/SvelteKit-Up-and-Running/tree/main/chapters/chapter03.

Fetch

To begin, let’s take a look at one of the most commonly used Web APIs, fetch. Assuming your development environment is on the latest LTS version of Node.js (v18+), you’ll have access to fetch on the server without having to install an extra package such as node-fetch. That package was the most widely used package to deliver functionality, allowing developers to make network requests before fetch was incorporated into Node.js's core features. Since fetch is now also supported in every major browser, we can safely use it in both client- and server-side environments to make external and internal requests. Because SvelteKit can run in both a browser and server environment, it is safe to say that we have, in fact, “made fetch happen.”

To illustrate how fetch can work in both the browser and server, let’s take a look at an example. We’ll create a new route by adding the src/routes/fetch/+page.svelte file to serve as our demo page. We’ll also create src/routes/fetch/+page.js to get data from an actual fetch request. In this example, we’ll utilize NASA’s free Astronomy Picture of the Day API found at https://api.nasa.gov/. It’s recommended to obtain an API key for regular use; however, the demonstration API key provided in the following examples will suffice for our purposes. This example makes a network request to the NASA API and shows the data received on our page.

Please note that the styles are by no means required and are only provided to make our example more digestible when viewed in the browser. We could add lots of styles and make this look fantastic, but that would take up more of our limited space on these pages. Plus, you’re here to learn about SvelteKit, not CSS:

src/routes/fetch/+page.svelte

<script>
  export let data;
</script>
<h1>Astronomy Picture of the Day</h1>
<h2>{data.pic.title}</h2>
<div class='wrap'>
  <a href={data.pic.hdurl}><img src={data.pic.url}></a>
</div>
<p>{data.pic.explanation}</p>
<style>
  h1, h2 {
    text-align: center;
  }
  .wrap {
    max-width: 75%;
    margin: 0 auto;
    text-align: center;
  }
  img {
    max-width: 100%;
  }
</style>

In this file, we’re simply setting up the markup for our data to populate. To get access to the data provided by the sibling +page.js file, we must include export let data;. If you're familiar with React or Vue, think of this as SvelteKit's method for passing props between components. If you're familiar with React of Vue, think of this as SvelteKit's method for passing props between components. After doing that, we can use the data provided in the object to populate the image title, a link to the high-resolution file, a link to the externally hosted image, and show an image description. And, of course, we’ve added some minimal styles. The real magic, and our fetch request, happens in the next snippet for +page.js, where we make the call to the external API:

src/routes/fetch/+page.js

const key = 'DEMO_KEY'; // your API key here
export function load() {
  const pic = fetch(`https://api.nasa.gov/planetary/apod?api_key=${key}`)
  .then(response => {
    console.log('got response');
    return response.json();
  });
  return {pic};
}

In +page.js, we’ve created a constant where you can place your own API key. Then, we exported the load function, which is necessary to tell SvelteKit to run before rendering the sibling +page.svelte file. It can be async or not; SvelteKit doesn’t care and will handle each case accordingly. We then create the constant pic, to which we can assign the promise returned from the fetch call. In the fetch call, we provided the URL with an API key appended to it, as the first and only argument. If your API needed to have options specified in the header, a method set, or perhaps an authentication cookie, you could do so by providing that as an object in the second argument. Remember that SvelteKit aims to be compatible with the existing web standards, so the implementations will have at least the standardized functionality. See the resources at the end of this chapter for more information on how to utilize fetch in these ways.

Continuing with the promise received by the fetch call, we run console.log() to demonstrate that code is being run in both the browser and server environments. If your development environment isn’t yet running, you can start it with the npm run dev command. Remember that it will then be available in your browser at the provided URL. You can confirm this by checking your browser console output in your developer tools, as well as the output from your Vite development server in your terminal. In both cases, you should see the “got response” output displayed. Because our request didn’t require any authentication, it was safe for us to run on the client side.

Finally, we convert the Response object body to JSON, and the entire promise is wrapped in an object so that it may be returned. As we’ve seen with universal load functions, they run on both the server and browser. In the case of universal load functions, they must return an object.

Loading Data

We’ve established that code run from +page.js is actually run in both the browser and server. If we wanted the code to only be run on the client, we would place that in +page.svelte. Similarly, if we wanted this code to be run only on the server, then we could change the filename to +page.server.js. This second use case would be more suitable for making an authenticated database call or accessing environment variables, such as API keys (as we did in this previous example). SvelteKit will recognize that this file is intended to run only in the server environment and will take the appropriate steps to separate that logic. Server load functions also work slightly differently from universal load functions, but we’ll go into that in more depth in a later chapter.

After doing all of this, we can now navigate to /fetch in our browser and see the image, its title, and a description, and even click on the image to view the full high-resolution file. It’s very helpful to make calls to various internal or external APIs, but our application will likely need to receive data from users at some point. In the next section, we’ll cover how we can access and manipulate FormData objects provided in Requests.

FormData

When building web applications, it’s common to accept data from users through the use of forms. Accessing this data can be done via the FormData API. To see this in action, let’s look at a basic form that allows users to post a comment to our application. In this example, we’ll create two files, +page.svelte and +page.server.js, under the new comment route. As before, the +page.svelte file contains HTML to scaffold our form and minimal styles. In order to get access to the data sent from our server, we must include the line reading export let form; in our client-side <script> code. This lets us view the status of the object returned from +page.server.js and report the status back to the user by using the templating system provided by Svelte:

src/routes/comment/+page.svelte

<script>
  export let form;
</script>
<div class='wrap'>
  {#if form && form.status === true}
    <p>{form.msg}</p>
  {/if}
  <form method='POST'>
    <label>
      Comment
      <input name="comment" type="text">
    </label>
    <button>Submit</button>
  </form>
</div>
<style>
  .wrap {
    width: 50%;
    margin: 0 auto;
    text-align: center;
  }
  p {
    color: green;
  }
  label {
    display: block;
    margin: 1rem;
  }
</style>

src/routes/comment/+page.server.js

export const actions = {
  default: async (event) => {
    const form = await event.request.formData();
    const comment = form.get('comment');
    if(comment) {
      // save comment to database
      return {
        status: true,
        msg: `Your comment has been received!`
      }
    }
  }
}

As noted earlier, +page.server.js works in the same way as a +page.js file, except that it will only run on the server. Just as we can export load(), like in our fetch example, server-side files can also export actions. These exported actions can only be accessed via POST requests. We’ll look at how we can further utilize actions in a later chapter, but for now, note that we’re creating a single action, default. This means that when a form is submitted, the POST request is handled by the /comment endpoint.

From the default action, this example accesses the Request object located in event, and then accesses FormData within that Request. Once the FormData object has been assigned to the form variable, we use get() to retrieve the value from the form input by its name. Normally, we would then move on to something akin to a database call that would save the comment alongside a unique user identifier. For the sake of brevity, we will just return an object with the status and msg properties.

When the page reloads, those properties are then checked by the Svelte template syntax and show the user a message that their comment has been successfully submitted. Even though this example only accessed FormData with get(), all methods documented in official standards are also available to us. If we wanted to cycle through all values and keys submitted, we could do so using a for...of loop and access the values via form.values() and form.keys().

Now that we know how to obtain data from submitted forms, we should also look at another way of receiving data from users that may be overlooked – the URL.

URL

The URL API can be helpful in instances where you need to parse your application’s URL. SvelteKit makes accessing the URL API quite simple, as it is also available in the event object, just as the request object was. Let’s continue with our previous example using comments. If we wanted to build a commenting service for use across a network of other websites, we may want to report to a user which site they just commented on. Let’s expand on that previous example and do just that. We don’t need to make any changes to src/routes/comment/+page.svelte. Instead, we’ll adjust our server action accordingly.

This example is identical to the last except for a couple of changes. The URL API is accessed via event.url and assigned to the url constant. It is then output to the server with console.log(url) to show you the various read-only properties that you can access. For this demonstration, we get the hostname via url.hostname and use it in a template literal, which is assigned to the msg property of the returned object:

src/routes/comment/+page.server.js

export const actions = {
  default: async (event) => {
    const form = await event.request.formData();
    const url = event.url;
    console.log(url);
    const comment = form.get('comment');
    if(comment) {
      // save comment to DB here
      return {
        status: true,
        msg: `Your comment at ${url.hostname} has been 
          received!`
      }
    }
  }
}

If you switch to your browser window and post a comment, you’ll now see that it reports the hostname property. In a production environment, this would ideally be your domain name, but in our development environment, we see the message Your comment at 127.0.0.1 has been received!. If you’ve accessed your development site from http://localhost, you’ll see localhost instead of the IP address. Switch back to your terminal where the development server is running, and you’ll see the various read-only properties that you have access to within the URL object:

Sample event.url object

URL {
  href: 'http://127.0.0.1:5173/comment',
  origin: 'http://127.0.0.1:5173',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1:5173',
  hostname: '127.0.0.1',
  port: '5173',
  pathname: '/comment',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}

This URL object showcases how simple it is to grab information about it. All of the read-only properties shown are easily accessed using dot notation. Gone are the days of parsing an entire URL with regex in an attempt to extract the portion needed. Now, if you wanted to get the values set in query strings, you could easily iterate over event.url.searchParams with a for...of loop. Go ahead and add some options to the URL in your browser. An example might look like http://127.0.0.1:5173/comment?id=5&name=YOUR_NAME_HERE. Now, the console.log(url) function call will output the names and values set after the first ? and each subsequent &.

Summary

In this chapter, we covered an example use case of fetch and a two-part demonstration showing how URL and FormData can be used. While the examples presented here do not represent the full scope of the various Web APIs that you’ll have access to, they should illustrate how simple it is to use them with SvelteKit. If you’re hoping to build applications with SvelteKit, it’s important you become familiar with these modern Web APIs, as they’re used extensively throughout development with SvelteKit. SvelteKit encourages you to lean on that existing knowledge. By doing so, SvelteKit can ship less code to you so that you can ship less code to your users.

In the next chapter, we’ll move away from the background information that is necessary for using SvelteKit and start building something that resembles an application. We’ll cover various routing techniques and how you can build out a consistent user interface across the application.

Resources

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.183.150