Astro Cookies API

Astro Cookies API

Cookies on HTTP Requests

🚀 Astro Cookies API

The Astro cookies API provides a way to pass data between user browser and the server. You can use it on your SSR sites, as an example, to keep track of whether a user is authenticated. Essentially, the API is just a wrapper for JavaScript and browser APIs which let you set cookies on HTTP requests by adding a cookies header. That said, the standard APIs have a little boilerplate code and the Astro Cookie API will save you some typing!

To see how the API works we will look at an example with the Mux video player. We will see how you might save the video playback position using cookies. This could be used to let a logged-in user resume a video from the right place even if they switch device. The main goal here is to show how the API works, and you can adapt the code we see to work with HTTP cookies in many use cases.

For some cookie use cases you can manage cookies just on the client side with JavaScript APIs. We focus on using cookies in HTTP requests between client and server.

🧱 What are we Building?

We will set up a video with Mux video player in an Astro SSR app. In the video component, we will use browser APIs to let us know when the user pauses playback. We then send the playback position to our API endpoint, where we can save it to a database ready for the user’s next login. In the API endpoint we will also set the cookie with the playback position using the Astro Cookie API, of course! Finally, in our Astro client code, we will have a check for the playback position cookie and pass any value to the video player component.

Astro Cookies API: Screen capture shows a video in a browser window. There are play, advance and other controls.

If that sounds like what you were looking for, then why don’t we get started? We won’t build the app step-by-step, but there is a link further down the page to the full project code on GitHub. You will need a Mux account (and to upload some video) to try out the full code, and you can do this for free.

🎥 Mux Video Player Component

We are working in Svelte here. This makes it easier to load the script tag we require for the Mux player and also to access the embedded video element APIs.

<script lang="ts">
    export let videoId = import.meta.env.PUBLIC_MUX_PLAYBACK_ID as string;
    export let title = 'Raindrops in Super Slow Motion';
    export let startTime = '0';

    const siteUrl = import.meta.env.PUBLIC_SITE_URL;

    let playerElement: HTMLMediaElement;

    async function handlePause() {
        const currentTime = Math.floor(playerElement.currentTime);
        fetch(`${siteUrl}/api/player?startTime=${currentTime}`, {
            method: 'POST',
        });
    }
</script>

<svelte:head><script src="https://cdn.jsdelivr.net/npm/@mux/mux-player"></script></svelte:head>
<figure>
    <mux-player
        bind:this={playerElement}
        playback-id={videoId}
        stream-type="on-demand"
        {title}
        disable-cookies
        start-time={startTime}
        on:timeupdate={console.log(Math.floor(playerElement.currentTime))}
        on:pause={handlePause}
    />
    <figcaption>
        {title}. Credit: <a href="https://www.videvo.net/profile/Beachfront">Beachfront</a>
    </figcaption>
</figure>

<style>
    mux-player {
        display: flex;
        max-width: var(--max-width-full);
        aspect-ratio: 16 / 9;
    }
</style>

We are using a couple of MediaElement API events here. onTimeUpdate (line 27) tracks playback, letting us know the video is actually playing. We just console log the currentTime here and include it for reference. We do use the onPause event (line 28) however. As you might expect, this causes our handlePause function to be invoked when the user taps the pause button. The current time has millisecond precision, which is probably a little more than we require. We use Math.floor to truncate the value.

The other thing to note is using browser APIs, we can set the playback start time and do so in line 26, This will be passed in when we create an instance of this component later.

In the handlePause function, we use fetch to send a POST HTTP request to our own endpoint with the currentTime as a URL-encoded string. Let’s look at that endpoint code next.

▶️ Astro Cookies API: Player Endpoint

This code listens for for HTTP requests on the http://example.com/api/player.

import type { APIRoute } from 'astro';

const siteUrl = import.meta.env.PUBLIC_SITE_URL;

export const post: APIRoute = async function post({ cookies, redirect, request }) {
    try {
        const { url: requestUrl } = request;
        const { searchParams } = new URL(requestUrl);
        const startTime = searchParams.get('startTime');
        console.log({ startTime });

        cookies.set('start-time', '31', { path: '/' });

        return new Response('', { status: 200 });
    } catch (error: unknown) {
        console.error(`Error in player api route: ${error as string}`);
        return redirect(`${siteUrl}/`);
    }
};

You might notice the code is a little slimmed down compared to what you would expect to find in a production app. We just console log the startTime once we have parsed it from the request here. You would want to store it to the database against the logged-in user in a real-world app.

Astro Cookies API: Screen capture shows Terminal a console log for start Time of 2

The code in line 12 is where we set the cookie. In a complete app, this would probably go in the login logic, so if the user switched device. We would read the startTime from the database and set it on the cookie.

Astro Cookies API

In endpoints, you can access the API using the cookie object on the API route (as in line 5). The API has a few methods which all do exactly what you would expect them to: get, set and has are probably the ones you will be reaching for most often. In line 12 above, we call set. The first argument is the cookie name start-time in this case. The second argument is the value for that cookie. It is a simple string here. It can be an object instead, though you would want to try to limit yourself to types which can automatically serialized with JSON.stringify.

The final argument to the set call is the options. This is where you can set expires as a date, httpOnly (for more secure cookies which JavaScript code running in the browser cannot access). sameSite and secure are other values you would consider setting here. Here we just set path, which is the web page paths the cookie will apply to.

Astro Cookies API: Screen capture shows browser dev tool with the storage tab open. We can see a start-time cookie set to 31

We just return an empty response, and this is enough to set the cookie on the user device. The final part is to read this data client-side, which we see next.

🧑🏽 Astro Cookies API: Reading Cookies Client-side

To access the Astro Cookie API from the client, we can use the Astro global. One thing to note is that the cookie API is only accessible in .astro and API endpoint files. Because of that, we read in the cookie value in the index.astro client file and pass it as a prop to the Video component.

---
import '~styles/fonts.css';
import '~styles/global.css';
import Video from '~components/Video.svelte';

const startTime = Astro.cookies.get('start-time').value ?? '0';

const title = 'Astro Cookies API';
const description = 'Astro Cookies API: mux video position save example';
---

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="icon" href="/favicon.ico" sizes="any" />
    <link rel="icon" href="/icon.svg" type="image/svg+xml" />
    <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
    <link rel="manifest" href="/manifest.webmanifest" />
    <title>{title}</title>
    {description ? <meta name="description" content={description} /> : null}
</head>
<main>
    <h1>Astro Cookies API 🍪</h1>
    <Video client:load {startTime} />
</main>

We call Astro.cookies.get('cookie-name').value here to retrieve the string value we stored in the endpoint. If we had stored an object, instead of a string, then we would have used:

Astro.cookies.get('cookie-name').json()

That gets us back an object. If we pause the video, then refresh the browser, the video skips to 31 . seconds (the value we set on the cookie). Remember, in a real-world app we would have pulled an actual startTime value from our database and used that value instead of our arbitrary 31 value.

Astro Cookies API: Screen capture shows video player in browser. The video is ready to play with the current time at 31 seconds.

🙌🏽 Astro Cookies API: Wrapping Up

In this post, we saw how the Astro Cookies API works and also some browser Media Element APIs. In particular, we saw:

  • how to set cookies in a server endpoint in Astro,
  • how to read cookies in client code,
  • a way to capture video current time and a pause action in Svelte.

You can see the full code for this project in the Rodney Lab GitHub repo. I do hope you have found this post useful! I am keen to hear what you are doing with Astro and ideas for future projects. Also let me know about any possible improvements to the content above.

🙏🏽 Astro Cookies API: Feedback

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @rodney@toot.community on Mastodon and also the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as SEO. Also subscribe to the newsletter to keep up-to-date with our latest projects.

Did you find this article valuable?

Support Ask Rodney by becoming a sponsor. Any amount is appreciated!