Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(build): modernize how we bundle next-auth #1682

Merged
merged 9 commits into from
Apr 15, 2021

Conversation

balazsorban44
Copy link
Member

@balazsorban44 balazsorban44 commented Apr 10, 2021

What:

The goal of this change is massive bundle size reduction. My naive method of verifying this showed everything between 50-80% of size reduction in single use cases!

Why:

Currently, we bundle packages that are unused, or only used by a subset of users. Two of these are typeorm and nodemailer. According to BundlePhobia TypeORM takes up 71% of the package size, and it will be downloaded for those users who doesn't even use a database!

How:

typeorm and nodemailer are now only optionally imported packages. In this PR it is done by require_optional, a package that is getting out-of-date (the last update was over 3 years ago), and there are now better alternatives, like

We are going to try dynamic imports and peerDependenciesMeta that is supported by both npm and Yarn, so we would also want to explore that.

I also removed querystring, which is included in Node.js by default, and crypto-js, which didn't have a single reference in the codebase (we rather use crypto, which is included in Node.js).

I also introduce exports in the package.json file, which is a modern alternative to include sub-modules, that is supported by the newest versions of Node.js. I did not remove the current workaround (like https://github.com/nextauthjs/next-auth/blob/main/client.js), as not all bundlers support these (Webpack 5 for example does, but Webpack 4 will simply ignore it)

This is especially useful when only using a few Providers. The current import from next-auth/providers will bundle ALL providers, every time. This will still work, but from now on, it will be possible to do something like:

import IdentityServer4 from "next-auth/providers/identity-server4" which will only include the IDS4 provider, and leave out the rest. (This doesn't seem to be a breaking change actually, so we might be able to backport this.)

Here is a before/after of a production app only using jwt and a single Provider:
image

Notice both providers (aside from the single identity-server4 one) and adapters (we don't use databases, not needed anyway) are gone from the bundle!

Checklist:

  • Documentation
  • Tests
  • Ready to be merged

BREAKING CHANGE:

Users who want to use the Default/TypeORM adapter now have to install typeorm in their projects as a dependency. The same applies if they want to use the Email provider.

If the user doesn't use databases,
it shouldn't be necessary to iclude it in the bundle.
This can more than half the package size!
Remove unused dependencies, move optional ones to be optional

BREAKING CHANGE:
`typeorm` and `nodemailer` are no longer added by default.
If you need them, install them in your own project.
@vercel
Copy link

vercel bot commented Apr 10, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/nextauthjs/next-auth/92QvJ7Gy4JCB6TjUMsjdJe9NfDa7
✅ Preview: https://next-auth-git-feat-reduce-bundle-size-nextauthjs.vercel.app

@github-actions github-actions bot added core Refers to `@auth/core` providers labels Apr 10, 2021
@ndom91
Copy link
Member

ndom91 commented Apr 10, 2021

Regarding require_optional being a bit old, seems like dynamic imports are doable natively in node now.

https://javascript.info/modules-dynamic-imports

@balazsorban44
Copy link
Member Author

balazsorban44 commented Apr 10, 2021

@ndom91 I know, but I think @iaincollins said that they weren't working in some environments. Would be good to have som specifica though, maybe it's old and they ARE supported now. Would be awesome to just use dynamic imports TBH, we could drop another dependency!

Copy link
Collaborator

@ubbe-xyz ubbe-xyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow amazing 💯 , super nice we moved typeorm to peerDependencies, I didn't know require_optional even existed 💪🏽

@vercel vercel bot temporarily deployed to Preview April 11, 2021 18:18 Inactive
@ndom91
Copy link
Member

ndom91 commented Apr 11, 2021

So I did some more research and apparently the peerOptionalDependencies field is non-standard and pnpm and yarn don't seem to like it. I was able to dig up a mongodb jira issue and I copied the way they dealt with it in this last commit, they were using require_optional as well btw

Docs: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#peerdependenciesmeta
Mongodb jira issue: https://jira.mongodb.org/browse/NODE-2867

@vercel vercel bot temporarily deployed to Preview April 11, 2021 18:26 Inactive
@vercel vercel bot temporarily deployed to Preview April 11, 2021 18:34 Inactive
@ndom91
Copy link
Member

ndom91 commented Apr 12, 2021

God damn it, did I accidentally commit the npm7 package-lock? Sorry guys. Can someone who has npm6 still installed commit the npm6 one back up before this gets merged?

@balazsorban44
Copy link
Member Author

@ndom91 Cannot see that you did. Although I see we have a package-lock.json conflict now. 😬 We can look at it maybe tomorrow or after that.

@vercel vercel bot temporarily deployed to Preview April 12, 2021 17:21 Inactive
@mod-flux
Copy link

This is genuinely awesome and will have a noticeable positive impact on cold boot Lambdas!

@balazsorban44 based on your approach, for users only using a custom OAuth2.0 provider (not provided by Next Auth), they wouldn't need to import any providers at all, is that correct?

On a related GitHub issue, it was also suggested that reviewing the outdated and deprecated version of jose in use may also see some size reductions as they've removed all external dependencies between version 1 that Next Auth currently utilises and 3 which is their current LTS version.

@balazsorban44
Copy link
Member Author

balazsorban44 commented Apr 13, 2021

If you have a custom provider, you already don't need to import anything from next-auth/providers. providers takes a list of objects, the Provider.OauthLibraryHere({}) pattern is just syntactic sugar to hide some boilerplate.

upgrading jose would have some size reduction benefits as well I think, yes. v3 looks much more modular and so we would only have to import what is actually used

src/providers/email.js Outdated Show resolved Hide resolved
@balazsorban44
Copy link
Member Author

balazsorban44 commented Apr 14, 2021

Doing some research here:
We presumably used require_optional as a dependency, because mongodb did so as well. I recently discovered that they changed to another alternative called optional-require, because there has been an issue with it on Windows machines. (Bugfixes for require_optional are highly unlikely as it has been an unmaintained package for many years now. Actually there is a fix, that hasn't been accepted in over 2 years: christkv/require_optional#8)

See: https://jira.mongodb.org/browse/NODE-3050

I am still not convinced that we need either of these, so I am going to push dynamic imports here, and try to do some testing. (@iaincollins you mentioned in the source code that Using a dynamic import causes problems for some compilers/bundlers. Additional information here about what tooling breaks with dynamic import would be useful here, to pinpoint if the problem has been fixed in those tools.)

It is a Stage 4 proposal https://github.com/tc39/proposal-dynamic-import, so it is highly likely to soon be a standard.

Next.js (currently our only target as far as I know) do support them https://nextjs.org/docs/advanced-features/dynamic-import.

Webpack 4 (and 5) both support them: https://webpack.js.org/guides/code-splitting/#dynamic-imports (which is the tool used to bundle next apps.)

Unless I miss something, I don't see why we cannot AT LEAST TRY using dynamic imports. We are on a next branch here, so bugs are expected anyway.

@vercel vercel bot temporarily deployed to Preview April 14, 2021 21:27 Inactive
@github-actions github-actions bot added the adapters Changes related to the core code concerning database adapters label Apr 14, 2021
Conflicts:
	package-lock.json
@balazsorban44
Copy link
Member Author

Just discovered that Next.js also utilizes peerDependenciesMeta for optional dependencies:

https://github.com/vercel/next.js/blob/1797fc50ab6a6059a3d118236dcd9cbf22ef91b8/packages/next/package.json#L114-L131

@balazsorban44 balazsorban44 merged commit cb4342f into next Apr 15, 2021
@balazsorban44 balazsorban44 deleted the feat/reduce-bundle-size branch April 15, 2021 21:40
@github-actions
Copy link

🎉 This PR is included in version 4.0.0-next.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@balazsorban44
Copy link
Member Author

DISCLAIMER: So this approach did not work, not even with a small adjustment in #1736. I will keep exploring ways that will make typeorm and nodemailer truly optional, so we can drastically reduce bundle size for those not using any of them.

The most simple idea would actually be, just to remove the database option, really.

We are already in the process of moving out ALL the adapter from the core to the https://github.com/nextauthjs/adapters repo, and once done, you would be able to import only the adapter you need.

Providing a database option MIGHT be convenient in a miniature way for those who want to avoid one extra line:

+ import TypeOrm from "@next-auth/typeorm-adapter"

//...
- database: "mysql://nextauth:password@127.0.0.1:3306/database_name"
+ adapter: TypeOrm.Adapter("mysql://nextauth:password@127.0.0.1:3306/database_name") 

Getting rid of it and explicitly ask the user to import their adapter would not require any hacky import solutions in the core.

Regarding nodemailer:

Currently the recommended way of importing a provider is as follows:

import Providers from "next-auth/providers"

//...

providers: [
  Providers.Auth0({...}),
  Providers.IdentityServer4({...}),
  ...
]

This means that you will only need a single import at the top, but this will actually bundle ALL the providers in the production build. Explained in #1682 (comment).
The most simple solution is to just do the same as with adapters and directly import the provider you will actually need:

- import Providers from "next-auth/providers"
+ import Auth0Provider from "next-auth/providers/auth0"
+ import IDS4Provider from "next-auth/providers/identity-server4"

//...

providers: [
-  Providers.Auth0({...}),
+  Auth0Provider({...}),
-  Providers.IdentityServer4({...}),
+  IDS4Provider({...}),
  ...
]

So if you don't use the built-in Email provider, you would never need to import nodemailer either!

I'll do another PR on this.

mnphpexpert added a commit to mnphpexpert/next-auth that referenced this pull request Sep 2, 2024
* feat(build): optionally include TypeORM

If the user doesn't use databases,
it shouldn't be necessary to iclude it in the bundle.
This can more than half the package size!

* feat(build): clean up in dependencies

Remove unused dependencies, move optional ones to be optional

* feat(build): add exports field

* fix: use peerDependenciesMeta instead of non-standard peerOptionalDependecns field

* fix: ts-standard string quotes

* fix: ts-standard string quotes

* refactor: use asnyc/await for sendVerificationRequest

* chore(deps): upgrade mongodb, remove require_optional

Co-authored-by: ndom91 <yo@ndo.dev>

BREAKING CHANGE:
`typeorm`, and `nodemailer` are no longer dependencies added by default.
If you need any of them, you will have to install them yourself in your project directory.
TypeOrm is the default adapter, so if you only provide an `adapter` configuration or a `database`, you will need `typeorm`. You could also check out `@next-auth/typeorm-adapter`. In case you are using the Email provider, you will have to install `nodemailer` (or you can use the choice of your library in the `sendVerificationRequest` callback to send out the e-mail.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
adapters Changes related to the core code concerning database adapters core Refers to `@auth/core` providers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants