prezly / theme-kit-js

Data layer and utility library for developing Prezly themes with JavaScript
MIT License
11 stars 2 forks source link

Providing Higher Order Functions for common getServerSideProps scenarios #7

Open e1himself opened 2 years ago

e1himself commented 2 years ago

I had this idea with this package providing higher-order functions to provide extended context to the wrapped getServerSideProps function to simplify common scenarios.

An example using such approach with HTTP env headers and Prezly API client:

function withHttpEnv<Context extends GetServerSidePropsContext, Props>(
    getServerSideProps: (
        context: Context & { env: Env },
    ) => Props | Promise<Props>,
) {
    return function (context: Context) {
        const env = getEnvVariables(context.req);

        return getServerSideProps({ ...context, env });
    };
}
function withPrezlyApi<Context extends GetServerSidePropsContext, Props>(
    getServerSideProps: (
        context: Context & { api: PrezlyApi },
    ) => Props | Promise<Props>,
) {
    return withHttpEnv(function (context: Context) {
        const { PREZLY_ACCESS_TOKEN, PREZLY_NEWSROOM_UUID } = context.env;
        if (!PREZLY_ACCESS_TOKEN) {
           throw new Error('"PREZLY_ACCESS_TOKEN" is not set in env variables.');
        }
        if (!PREZLY_NEWSROOM_UUID) {
           throw new Error('"PREZLY_NEWSROOM_UUID" is not set in env variables.');
        }

        const api = new PrelzlyApi(PREZLY_ACCESS_TOKEN, PREZLY_NEWSROOM_UUID);

        return getServerSideProps({ ...context, api });
    });
}

and then the usage will be something like this:

export const getServerSideProps: GetServerSideProps<BasePageProps> = withPrezlyApi(async (context) => {
    const api = context.api; 
    const env = context.env;
    // use API and env
});

what do guys you think about this approach?

riffbyte commented 2 years ago

So basically it's just about converting the utility functions (getPrezlyApi and getEnvVariables) into HOC functions? From what I see it would make the getNewsroomServerSideProps function a bit simpler, since it wouldn't need to initialize and return the api object anymore, using it from the context instead.

Are there any other improvements to the DX that we can make with these? 🤔

UPD: I see that it would help to make scenarios like this one a bit cleaner, so that sound like a great idea actually. Although I would still allow using the current getPrezlyApi and getEnvVariables methods as-is, since they are not actually dependent on Context (e.g. they are used in /api endpoints, which don't have the same context that pages do, so they wouldn't benefit from such HOC functions)

e1himself commented 2 years ago

Are there any other improvements to the DX that we can make with these?

Anything. For example, typically fetched page props can be done with a HOF and passed to the wrapped function as a second argument (or alternatively within the context object):

export const getServerSideProps: GetServerSideProps<BasePageProps> = withStoryPageProps(async (context, props) => {
    const { story, translations, languages, newsroom, theme, preset } = props;
    const discourseUrl = ...;

   return {
      props: { 
          ...props,
          discourseUrl,
      };
   };
});
riffbyte commented 2 years ago

This was kind of addressed in https://github.com/prezly/theme-kit-nextjs/pull/31 (although not in a HOF way). We'll need to see if the approach made in that PR would be good enough. If not, we can convert it to the proposed HOF approach, which should be a bit cleaner code-wise.