How To Use Realtime Subscriptions with SvelteKit and Pocketbase

No Comments
Published: 18.02.2024

Do you want to learn how to use realtime subscriptions with Pocketbase and SvelteKit? In this post, we will do exactly that by extending our previous application – the expense tracker – with realtime data. Inside the application, we will change our previous data fetching to use a subscription for realtime updates.

Don’t want to read? Watch the video instead!

Create Realtime Subscriptions for Pocketbase using SvelteKit in List View

For a realtime subscription we will need to use a hybrid Pocketbase setup. We will use our current SSR setup to fetch the initial data and then use a client-side Pocketbase instance to create a realtime subscription, which then updates the initial data on change.
To set up the client instance, we will first need to update our .env file with another environment variable PUBLIC_PB_URL=$PB_URL. Additionally, we will update our src/+hooks.server.ts file to make the cookie also available in the frontend. We need to do that so that we can sync the SSR auth session with the one on the client side. For this to work, we will update the exportToCookie call:

//...

// send back the default 'pb_auth' cookie to the client with the latest store state
response.headers.append('set-cookie', event.locals.pb.authStore.exportToCookie({ sameSite: 'Lax', httpOnly: false }));

//...

With that, we have the foundation down. Next, we will create the client instance and the realtime subscription. For that, we will open src/routes/(app)/dashboard/+page.svelte and create both inside a onMount call. The subscription will need to have a topic, which, in our case, is specified with a *; thus, it listens to all events. Inside the subscription, we have a callback in which we can work with the data. We will update our available data based on the event. In case of create we will append the new expense, in case of update we well update an expense and on delete we will remove it. Finally, we will also expand the category of expense records so that we can display the category icon.
When the page is closed or destroyed, we will also close the client connection.

<script lang="ts">
...
    let pb: PocketBase;
    onMount(async () => {
        // use client side pocketbase for subscriptions
        pb = new PocketBase(env.PUBLIC_PB_URL);
        pb.authStore?.loadFromCookie(document.cookie || '')

        pb.collection('expenses').subscribe<ExpensesResponse<{ category: CategoriesResponse; }>>('*', function (e) {
            switch (e.action) {
                case 'create':
                    let icon = e.record.expand?.category?.icon
                    if (icon) {
                        const url = pb.files.getUrl(e.record.expand?.category!, icon, {'thumb': '100x100'});
                        e.record.expand!.category!.icon = url;
                    }
                    data.expenses = [...data.expenses, e.record];
                    break;
                case 'update':
                    data.expenses = data.expenses.map((expense) => {
                        if (expense.id === e.record.id) {
                            let icon = e.record.expand?.category?.icon
                            if (icon) {
                                const url = pb.files.getUrl(e.record.expand?.category!, icon, {'thumb': '100x100'});
                                e.record.expand!.category!.icon = url;
                            }

                            return e.record;
                        }
                        return expense;
                    });
                    break;
                case 'delete':
                    data.expenses = data.expenses.filter((expense) => expense.id !== e.record.id);
                    break;
            }
        }, { expand: "category" });
    });

    onDestroy(()=>{
        // destroy client when component is destroyed
        pb?.authStore?.clear()
    })
</script>

To see if the realtime subscription in SvelteKit works go to your Pocketbase dashboard and edit, create or delete expenses. It then should automatically update in the application dashboard as well.

Create a Pie Chart with Pocketbase Realtime Subscriptions in the Dashboard

Next, we will also add a Pie Chat to our dashboard. We will use the realtime updated data, bring them in the correct format and then display them inside the pie chart.

realtime subscriptions in pocketbase: pie chart

For the chart, we will use chartjs and svelte-chartjs:

Need help or want to share feedback? Join my discord community!

pnpm add svelte-chartjs chart.js

Next, we will create a new component inside of src/lib/components/PieChart.svelte. In the component, we configure the chart and create a prop to submit the data:

<script lang="ts">
    import { Pie } from 'svelte-chartjs';

    import {
        Chart as ChartJS,
        Title,
        Tooltip,
        Legend,
        ArcElement,
        CategoryScale,
        type ChartData
    } from 'chart.js';

    ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale);

    export let data: ChartData<'pie', number[], unknown>;
</script>

<Pie {data} options={{borderColor: "#363f5d", color: "white"}} />

We then use it inside of src/routes/(app)/dashboard/+page.svelte by first formatting the data correctly and then using the PieChart inside of the HTML section:

KOFI Logo

If this guide is helpful to you and you like what I do, please support me with a coffee!

<script lang="ts">
    // ...

    $: formattedExpenses = ((): ChartData<'pie', number[], unknown> => {
        // labels
        let labels = [...(new Set(data.expenses.map((expense) => expense.expand ? expense.expand.category.name : "Other")))];
        labels = labels.map((label) => label.charAt(0).toUpperCase() + label.slice(1));

        // data to label
        let dataToLabel = new Array(labels.length).fill(0);
        data.expenses.forEach((expense) => {
            let label = expense.expand ? expense.expand.category.name : "Other";
            let index = labels.indexOf(label.charAt(0).toUpperCase() + label.slice(1));
            dataToLabel[index] += expense.expense;
        });

        // color to label
        let backgroundColor = new Array(labels.length).fill('');
        dataToLabel.forEach((_, index) => {
            backgroundColor[index] = `rgb(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)})`;
        });

        return {
            labels,
            datasets: [
                {
                    label: 'Expenses in €',
                    data: dataToLabel,
                    backgroundColor
                },
            ]
        }
    })()
</script>

<div class="grid grid-cols-5 h-full max-h-full">
    <div class="col-span-2">
        <div class="w-full h-full p-16">
            <PieChart data={formattedExpenses} />
        </div>
    </div>
<!-- ... -->

And with that, we have both the list view and a pie chart with realtime updates. This may not be the best use-case to use the realtime subscriptions as the app is currently only based on one user. A better example would be in a group-sharing scenario where different users submit expenses, and we want to display them in realtime. But if you followed along, I think you got the idea, and you can build the group-sharing functionality on your own if you want to!

You can also access the current state of the project in this repository and the branch called “course-8”.

Conclusion

In this post, we learned how to set up realtime subscriptions inside SvelteKit to get all changes on a Pocketbase collection. I hope you like the course so far. In the next part, we will create the user profile settings and allow you to edit the user settings. See you then.

In case you have any questions, feel free to ask them, and if you want to stay up to date with all my posts, consider subscribing to my monthly newsletter!

[convertkit form=2303042]

Discussion (0)