5 min read
I've recently tried to implement social media cards with @vercel/og. The implementation uses an edge function and during development everything went well, until I deployed it to Vercel. On Vercel the build ended with the following error:
Provided Edge Function is too large
Too large?
I've used only the @vercel/og
and the contentlayer/generated
package.
And how large is actually too large?
A quick look at the vercel documentation revealed:
The maximum size for an Edge Function is 1 MB, including all the code that is bundled in the function.
1mb
is not very much, but it should be enough for the two imports.
Analysis
So I installed @next/bundle-analyzer to analyze the generated bundle and found out what breaks the limit.
After installing the analyzer we can configure it in the next config:
If we start our build with the environment variable ANALYZE=true
, the output of the bundle analyzer will be opened in a browser window.
I was pretty much surprised, because the analyzer showed me that the module contentlayer/generated
is 960k
in size!
After a quick look at the generated file at .contentlayer/generated/Post/_index.json
,
it became clear that the body of the post needed all the memory.
Especially the code blocks with Code Hike need a lot of memory.
Solutions
Now that we know what caused the problem, we need a solution.
Load posts dynamically
My first idea was to use fetch
instead of import
to get the posts:
As the comments show, there were a few stumbling blocks, but on the whole the implementation was straightforward. So I deployed my results to Vercel, but unfortunately it showed the same error:
Provided Edge Function is too large
This is strange, because our bundle should be much smaller now. So I' installed the vercel cli to see the generated deployment locally.
After the installation we can generate the deployment bundle by calling vercel build
.
The generated output shows that the bundle size is now 390K
,
but there is a assets
directory which contains the _index.json
of contentlayer.
- .vercel/output/functions/api/og/posts/[slug].func/
- assets
- _index.df90b64bb8d3d072.json(962.24 kB)
- index.js(390.22 kB)
- index.js.map(551.10 kB)
It looks like the assets
directory is also counted and that still exceeds the limit.
So we need an other solution.
Parameters as part of the url
My second thought was to add the required parameters for the edge function to the url. So we have a url like the following:
/api/og/posts?summary=Next%20app%20directory%20and%20100%25%20height&description=...
The maximum url length is 14KiB
which should be enough for our requirements.
But with such a url it is easy to manipulate the parameters and
to generate a social media card with our layout but with different content.
To avoid such a manipulation we have to sign or encrypt the parameters.
There is an example on Vercel for encrypting parameters.
This should work, but it is quite an effort and the urls are still looking awful.
Can we do better?
Generate json without body
A third idea came to my mind. We can generate a json, which contains all fields of the posts, but without the body.
The script above loads the posts from the contentlayer directory,
removes the body and writes the new file to ./.scripts/Post/withoutbody.json
.
The newly generated file is only 3.9K
large.
Now we have to integrate the script into the build process of our project.
First we need to call the contentlayer build, so that we are able to import it in our script afterwards. Then we can run our script and finally we can run the next build.
After that build integration we can import the generated json:
To ensure that we are not accidentally adding the generated file to our repository,
we need to add the .scripts
directory to .gitignore
.
/node_modules.contentlayer.scripts
Now with this solution established, the build also succeeds at Vercel.
Build Completed in /vercel/output [44s] Deployed outputs in 8s Build completed. Populating build cache... Uploading build cache [84.14 MB]... Build cache uploaded: 1.756s Done with "."
But this solution is only a workaround and there should be a better way backed in contentlayer itself. For this reason I also opened a ticket contentlayerdev/contentlayer#339.
Posted in: vercel, edge, mdx, contentlayer