I have an Astro project where I would like to include a serverless function to send a few emails. I have done something similar in an old Gatsby project and expected this to be rather straight-forward.
However, when testing things out locally with the latest version of netlify CLI my function is compiled to commonJS - and I simply cannot figure out how to avoid this?
I use a few packages that do not support commonJS - which makes perfectly sense in 2024…
I see that an issue was created on github with the same problem: Netlify functions not working with ESM…
In my netlify.toml file I have:
[functions]
node_bundler = "esbuild"
directory = "functions"
included_files = ["functions/*.handlebars"]
external_node_modules = [
"nodemailer",
"nodemailer-express-handlebars",
"zod"
]
and in my package.json I have “type”: “module”.
I have also tried moving my function into an *.mjs file instead of *.js - but this file is also compiled to commonJS???
Is it something trivial I am missing here?
Do you have a reproduction to share?
1 Like
Thanks for the quick reply.
I will create this!
@hrishikesh Please inspect this project for a reproduction of the issue:
Just start up the project, fill out the contact form on the index page and submit it - and you will get the error directly in the browser.
You seem to be using Netlify Functions v1 which, I believe are transpiled into CJS. You should try to migrate to Functions v2 where ESM is preserved as far as I recall.
@hrishikesh - thank for you comment - I appreciate the help
Just to make sure we are on the same page: Functions v2 - as in edge functions?
So, I need to use the API?:
import type { Config, Context } from "@netlify/edge-functions";
export default async (request: Request, context: Context) => {
// some code ending with a response
Not exactly. Functions v2 means still using Functions, but the syntax is similar to that of Edge Functions. They’re (almost) drop-in replacements in terms of syntax. So you would be using the same thing (but replace the import with @netlify/functions
for the minor things that might be different).
I just tried re-writing the function (seems very straight-forward). However, when netlify CLI compiles it locally it places the function in .netlify/functions-serve/send-email
in this folder in includes a package.json with the following content:
{“type”:“commonjs”}
And as a result my function is compiled to commonJS and my packages fail when trying to import using require.
I have updated the minimal reproduction at:
https://github.com/LarsEjaas/netlify-function-commonjs-issue-minimal-reproduction/tree/main to reflect the changes.
You’re still exporting handler, so that makes you use Functions v1. You should export a default function to use v2.
Thanks a lot for trying to help @hrishikesh
I really just needed to verify that I did everything correctly. I got it working now.
- use *.mts file format (probably not really important - used *.ts before).
- use export default when exporting the function (as you mentioned).
Last part (took me a while to debug):
Do not import types from a namespace, this will make everything explode when code is compiled.
Somehow this line made everything break apart:
import { type Options as MailOptions } from 'nodemailer/lib/smtp-transport';
This is an interface on a namespace. Works fine normally in an ES environment - but makes everything explode when compiled.
I changed to this syntax:
//@ts-ignore
import type { Options as MailOptions } from '@types/nodemailer/lib/smtp-transport';
And now my function actually works!
Thanks for taking the time to help - I really appreciate it!