Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to best import "server-only" code in Next.js?

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

like image 366
Sam Bokai Avatar asked May 05 '20 10:05

Sam Bokai


Video Answer


2 Answers

When using getServerSideProps/getStaticProps Next.js will automatically delete all server-only code from the client-side bundle. There's no risk of running server code on the browser.

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.

like image 161
juliomalves Avatar answered Oct 15 '22 03:10

juliomalves


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.

like image 36
D0m3 Avatar answered Oct 15 '22 05:10

D0m3