Deploying typescript serverless functions

I’m having trouble deploying my serverless node function. It works locally with vercel dev but not in prod. The function is very simple and handles a booking API endpoint which fires off an email:

// api/book.ts
import type { VercelRequest, VercelResponse } from '@vercel/node'
import { fromEnv } from '@aws-sdk/credential-providers'
import { SendEmailCommand, SESClient } from '@aws-sdk/client-ses'

export default async function handler(request: VercelRequest, response: VercelResponse) {
  const { body } = request
  const input = { ... } // a text template with some values from body
  const command = new SendEmailCommand(input)
  const sesClient = new SESClient({ region: 'us-east-1', credentials: fromEnv() })
  const api_response = await sesClient.send(command)

  response.send(api_response);
};

Locally it works :ok_hand: but when deployed, I get errors in the logs:

ReferenceError: exports is not defined in ES module scope
This file is being treated as an ES module because it has a '.js' file extension and '/var/task/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
    at file:///var/task/api/book.js:2:23
    at ModuleJob.run (node:internal/modules/esm/module_job:268:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:543:26)
    at async d (/opt/rust/nodejs.js:9:19220)
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.

I can’t reproduce the issue locally, and ChatGPT/v0 can’t help either. My hunch is that my typescript function runs locally just fine when vite transpiles it to JS, but there’s some other build process that handles serverless functions in prod. I’ve tried modifying tsconfig.node.json with ChatGPT’s input to no avail. Would appreciate any pointers. I’m using node 22 as the runtime, with react as the frontend. My package.json does have "type": "module". Thanks in advance for looking :pray:

Someone else ran into this with a Hono project recently, but the solution should work for just about anything using Node. The short answer is, “Make sure the project has a tsconfig.json set for ESM.”

The full discussion includes examples that can help:

Thanks that did the trick. For anyone reading in the future, the solution was simply adding

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

to tsconfig.json.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.