19

In the getServerSideProps function of my index page, I'd like to use a function foo, imported from another local file, which is dependent on a certain Node library. Said library can't be run in the browser, as it depends on "server-only" modules such as fs or request.

I've been using the following pattern, but would like to optimize it. Defining foo as mutable in order to have it be in scope is clunky and seems avoidable.

let foo;
if (typeof window === "undefined") {
  foo =  require("../clients/foo");
}

export default function Index({data}) {
  ...
}

export async function getServerSideProps() {
  return {
    props: {data: await foo()},
  }
}

What would be the best practice here? Is it somehow possible to leverage ES6's dynamic import function? What about dynamically importing within getServerSideProps?

I'm using Next.js version 9.3.6.

Thanks.

UPDATE:

It seems as if Next.js's own dynamic import solution is the answer to this. I'm still testing it and will update this post accordingly, when done. The docs seem quite confusing to me as they mentionn disabling imports for SSR, but not vice versa.

https://nextjs.org/docs/advanced-features/dynamic-import

Sam Bokai
  • 538
  • 1
  • 5
  • 13

3 Answers3

9

When using getServerSideProps/getStaticProps, Next.js will automatically delete any code inside those functions, and imports used exclusively by them from the client bundle. There's no risk of running server code on the browser.

However, there are a couple of considerations to take in order to ensure the code elimination works as intended.

  • Don't use imports meant for the server-side inside client-side code (like React components).
  • Ensure you don't have unused imports in those files. Next.js won't be able to tell if an import is only meant for the server, and will include it in both the server and client bundles.

You can use the Next.js Code Elimination tool to verify what gets bundled for the client-side. You'll notice that getServerSideProps/getStaticProps gets removed as do the imports used by it.

juliomalves
  • 42,130
  • 20
  • 150
  • 146
5

Outside of getServerSideProps/getStaticProps, I found 2 fairly similar solutions.

Rely on dead code elimination

In next.config.js:

config.plugins.push(
    new webpack.DefinePlugin({
      'process.env.RUNTIME_ENV': JSON.stringify(isServer ? 'server' : 'browser'),
    }),
  );
export const addBreadcrumb = (...params: AddBreadcrumbParams) => {
  if (process.env.RUNTIME_ENV === 'server') {
    return import('./sentryServer').then(({ addBreadcrumb }) => addBreadcrumb(...params));
  }
  return SentryBrowser.addBreadcrumb(...params);
};

Note that some for reason I don't understand, dead code elimination does not work well if you use async await, or if you use a variable to store the result of process.env.RUNTIME_ENV === 'server'. I created a discussion in nextjs github.

Tell webpack to ignore it

In next.config.js

if (!isServer) {
    config.plugins.push(
      new webpack.IgnorePlugin({
        resourceRegExp: /sentryServer$/,
      }),
    );
  }

In that case you need to make sure you will never import this file in the client otherwise you would get an error at runtime.

D0m3
  • 1,471
  • 2
  • 13
  • 19
0

You can import the third party library or a serverside file inside getServerSideProps or getInitialProps since these functions run on server.

In my case I am using winston logger which runs on server only so importing the config file only on server like this

export async function getServerSideProps (){
   const logger = await import('../logger');
   logger.info(`Info Log ->> ${JSON.stringify(err)}`);  
}

You can also import library/file which has default export like this

export async function getServerSideProps(context) {
  const moment = (await import('moment')).default(); //default method is to access default export
  return {
    date: moment.format('dddd D MMMM YYYY'),
  }
}
pranav shinde
  • 1,260
  • 13
  • 11