pocketbase / js-sdk

PocketBase JavaScript SDK
https://www.npmjs.com/package/pocketbase
MIT License
2.17k stars 127 forks source link

Feature Request: Custom fetch function on Pocketbase initialization #316

Closed DDuran19 closed 3 weeks ago

DDuran19 commented 3 weeks ago

Hi team! from what I understand, under the hood, this sdk uses the native fetch api which is great, but there are frameworks like svelte who provides a modified fetch function that has additional features like passing cookies, authorization, etc (see on link).

The issue I am having now is because svelte operates both in client and server side, I am having mismatch of the authStore.

even when the client-side is authenticated (client-side svelte to pocketbase server), the server-side requests (server-side svelte to pocketbase server) isn't it (I'm not sure why, I am guessing the cookies are not being passed to the server when the server is making the request to pocketbase server...

The goal is when the user is on homepage, if he is authenticated, he should be routed to/apps

if not, he should be routed to /

// src\routes\(default)\+page.ts

import { pb } from "$lib/database/pocketbase";
import { redirect } from "@sveltejs/kit";
import type { PageLoad } from "./$types";

// this is the +page.ts of the homepage "/"
export const load: PageLoad = async () => {
    // if user is authenticated
    const isLoggedIn = pb.authStore.isValid;

    if (isLoggedIn) {
        // I am gonna redirect him to /apps
        redirect(303, "/apps");
    }
    return {};
};
// src\routes\(authenticated)\apps\+layout.ts

import { pb } from "$lib/database/pocketbase";
import { redirect } from "@sveltejs/kit";
import type { LayoutLoad } from "./$types";

export const csr = true;
export const ssr = false; // but if I remove this, it breaks and loops forever
// by loops I mean redirect loop

// this is the +layout.ts for my "/apps"
export const load: LayoutLoad = async () => {
    // if the user is not logged in, redirect to the home page
    const isLoggedIn = pb.authStore.isValid;

    if (!isLoggedIn) {
        // not logged in
        pb.authStore.clear(); // clear any auth data if any
        redirect(303, "/"); // redirect to "/"
    }
    return {};
};

// it continues for a redirect loop because there's a mismatch
// with the server and client...
// disabling the ssr will make it so that ALL requests will come from client-side

screen-capture (1).webm

Possible Solution: make it so that we can initialize Pocketbase with a custom fetch function so we can pass in authorizations on requests made on the server...

DDuran19 commented 3 weeks ago

I understand that we can do something like this:

export const load: LayoutLoad = async ({ fetch }) => {
    const [features, announcements] = await Promise.all([
        pb.collection("features").getFullList({ fetch }), // adding fetch here
        pb.collection("announcements").getFullList({ fetch }),
    ]);
    return {
        features: structuredClone(features),
        announcements: structuredClone(announcements),
    };
};

but when working on authstores..


export const load: LayoutLoad = async () => {
    const isLoggedIn = pb.authStore.isValid;  //Can't pass in the fetch function

    if (!isLoggedIn) {
        pb.authStore.clear(); 
        redirect(303, "/");
    }
    return {};
};

there's a method I saw which is what I am working on to see if it fits my use-case. but I just submitted the feature request so that it's like it's gonna use the provided fetch function when available or something like that.. without much configurations for each operation

(method) BaseAuthStore.loadFromCookie(cookie: string, key?: string): void Parses the provided cookie string and updates the store state with the cookie's token and model data.

NB! This function doesn't validate the token or its data. Usually this isn't a concern if you are interacting only with the PocketBase API because it has the proper server-side security checks in place, but if you are using the store isValid state for permission controls in a node server (eg. SSR), then it is recommended to call authRefresh() after loading the cookie to ensure an up-to-date token and model state. For example:

pb.authStore.loadFromCookie("cookie string...");

try {
    // get an up-to-date auth store state by veryfing and refreshing the loaded auth model (if any)
    pb.authStore.isValid && await pb.collection('users').authRefresh();
} catch (_) {
    // clear the auth store on failed refresh
    pb.authStore.clear();
}
ganigeorgiev commented 3 weeks ago

The JS SDK uses whatever global fetch implementation is available.

You can specify a fetch using the beforeSendHook or by passing it in the SendOptions for each request, aka:

await pb.collection("example").getList(1, 30, {
  fetch: yourCustomFetch,
})

For everything else - I cannot help, but I don't recommend using PocketBase with JS SSR, especially if you are new to web development. Make sure to read https://github.com/pocketbase/pocketbase/discussions/5313.

DDuran19 commented 3 weeks ago

Thanks man, I see and your point on https://github.com/pocketbase/pocketbase/discussions/5313 makes a lot of sense. I'm taking your advise :) I'm relatively new to the industry (about a year) I was able to create a full-stack app using sveltekit in cloudflare environment... but the dev time is so long, specially setting up D1, the queries.. the migrations, etc. so time consuming, but I saw this Pocketbase and was instantly amazed at how great it is! Thanks a lot for this magnificent application. I wish I could create something like this in the future. I am laying out my application as a SPA.. Thanks for entertaining my questions.