CallumBoase / plasmic-supabase

GNU General Public License v3.0
7 stars 5 forks source link

Login protected pages load slowly due to fetching designs from plasmic each time (with no cache) #13

Closed CallumBoase closed 3 months ago

CallumBoase commented 3 months ago

CONTEXT: In version 0.1.0 intructions were added to create a [...catchall].tsx page in the root directory which makes use of getServerSideProps() instead of the standard getStaticProps() to:

PROBLEM

CallumBoase commented 3 months ago

Draft new [...catchall].tsx page that is currently being trialled

This changes behaviour:

// ./pages/[...catchall].tsx

/* 
  Catchall page that runs for every page EXCEPT /, /login, and /public/*
  These pages are login protected by default
  The logic for checking authorization & where to redirect if a user is not authorized is controlled
  by @/authorization-settings.ts.
  The authorization-settings.ts file should export:
  - authorizationCheckFunction: a function that returns true if the user is authorized to view the page
  - loginPagePath: where to redirect to if authorization fails  eg '/login'

  The routes that render through this page are rendered on-demand (getServerSideProps instead of getStaticProps) 
  because they are login protected. This ensures that the user's session is checked on every request
  and avoids login-protected pages being cached and related issues.

  This pages is a modified various of the standard Plasmic NextJS loader API catchall page.

  Pages created in Plasmic studio will render using this catchall if it's:
    Page Settings -> URL path does NOT start with '/public/' and is not "/" or "/login"
*/

import type { GetServerSideProps } from "next";
import { createClient } from 'plasmic-supabase/dist/utils/supabase/server-props'

import * as React from "react";
import {
  PlasmicComponent,
  PlasmicRootProvider,
} from "@plasmicapp/loader-nextjs";

import Error from "next/error";
import { useRouter } from "next/router";
import { PLASMIC } from "@/plasmic-init";
import useSWR from "swr";

import { authorizationCheckFunction, loginPagePath } from "@/authorization-settings";

export default function PlasmicLoaderPage(props: {
  plasmicPath: string;
}) {

  const router = useRouter();

  //Fetch the component (page) data from Plasmic and cache it with SWR
  //Note that when navigating between [[...catchall]].tsx and this page
  //A warning  from Plasmic will appear in console https://github.com/plasmicapp/plasmic/blob/7117b4c2de9e89f4435db9efa0cba6a00012c297/packages/loader-react/src/loader-shared.ts#L498
  //Because maybeFetchComponentData will fetch designs with query string parameter browserOnly=true here
  //But browserOnly=false from [[...catchall]].tsx
  //Because fetching of Plasmic componet data is happening client side here, but server side in [[...catchall]].tsx
  //This does not appear to matter since the referenced file above seems to gracefully handle this case
  //However if the error could be removed by some refactor, it would be ideal

  const plasmicComponentFetcher = React.useCallback(async () => {
    return await PLASMIC.maybeFetchComponentData(props.plasmicPath);
  }, [props.plasmicPath]);

  const { data: plasmicData, error, isValidating } = useSWR(
    `plasmicData_${props.plasmicPath}`,
    plasmicComponentFetcher
  );

  //Render the error page if there is an error
  if(error) {
    return <Error statusCode={500} />;
  }

  //Render a loading message if the data is still loading
  if(isValidating && !plasmicData) {
    return <div>Loading...</div>;
  }

  //Render a 404 page if the page is not found in Plasmic
  if ((!isValidating && (!plasmicData || plasmicData.entryCompMetas.length === 0))) {
    return <Error statusCode={404} />;
  }

  //Extract the page meta data from the Plasmic data
  const pageMeta = plasmicData!.entryCompMetas[0]

  //Render the Plasmic component (page)
  return (
    <PlasmicRootProvider
      loader={PLASMIC}
      prefetchedData={plasmicData!}
      prefetchedQueryData={{}}
      pageParams={pageMeta.params}
      pageQuery={router.query}
    >
      <PlasmicComponent component={pageMeta.displayName} />
    </PlasmicRootProvider>
  );
}

//This runs on the server while rendering
//Unlike the pages in the root directory, we run this every time the page is requested with no cache
//This is appropriate because these pages are login protected and only work with a valid session
//We also need to recheck each time the page is requested to ensure the user is still authenticated
export const getServerSideProps: GetServerSideProps = async (context) => {

  //Get the catchall parameter from the page context
  const { catchall } = context.params ?? {};

  //Get the path of the current page
  let plasmicPath = typeof catchall === 'string' ? catchall : Array.isArray(catchall) ? `/${catchall.join('/')}` : '/';

  //Determine if the user is authorized to view this page
  const supabase = createClient(context);

  const { data: { user } } = await supabase.auth.getUser();

  const isAuthorized = authorizationCheckFunction(plasmicPath, user);

  if(isAuthorized !== true) return {
    redirect: {
      destination: loginPagePath,
      permanent: false,
    }
  }

  //We don't try and fetch the plasmic component data or data on the page here, because getServerSideProps does not cache
  //Therefore we would run this every time without cache, causing slow page navigation
  //Instead, we do that client-side and cache results with SWR (see above)

  return { props: { plasmicPath } };
}
CallumBoase commented 3 months ago

After further experemintation, the sluggish page load speed persists even with the above changes.

This will be addressed by changing to use of middleware to login protect pages see https://github.com/CallumBoase/plasmic-supabase/pull/22