Skip to content

Fix Vercel deployment with ESM conversion and frontend build handling#17

Closed
robdimarco-atxp wants to merge 12 commits intomainfrom
fix-vercel-esm-deployment
Closed

Fix Vercel deployment with ESM conversion and frontend build handling#17
robdimarco-atxp wants to merge 12 commits intomainfrom
fix-vercel-esm-deployment

Conversation

@robdimarco-atxp
Copy link
Contributor

Summary

This PR resolves Vercel deployment issues that were causing ERR_REQUIRE_ESM errors and missing frontend build directory errors.

Issues Fixed

  1. ERR_REQUIRE_ESM Error: The @atxp/client package is an ES module, but the backend was using CommonJS, causing require() of ES module errors in Vercel's serverless environment.

  2. Missing Frontend Build Directory: Vercel's serverless function wasn't including the frontend build files, causing the Express server to fail when trying to serve static files.

Changes Made

1. Convert Backend from CommonJS to ESM

  • ✅ Add "type": "module" to backend/package.json
  • ✅ Update TypeScript config to target ESNext modules instead of commonjs
  • ✅ Add ESM __dirname polyfill using fileURLToPath and dirname
  • ✅ Convert Function constructor workarounds to clean await import() syntax
  • ✅ Update all local imports to use .js extensions for ESM compatibility
  • ✅ Add vitest config for ESM test environment
  • ✅ Update dev script to use ts-node/esm loader

2. Fix Vercel Frontend Build Deployment

  • ✅ Update vercel.json buildCommand to copy frontend build to backend directory
  • ✅ Enhance getStaticPath() function with Vercel-specific paths and debugging
  • ✅ Ensure frontend build files are included in serverless function deployment

3. Maintain Test Coverage

  • ✅ All 30 tests passing with ESM configuration
  • ✅ Updated test imports and configurations for ESM compatibility

Before vs After

Before (CommonJS + Workarounds):

// Ugly workaround that didn't work
const { atxpClient } = await (new Function('return import("@atxp/client")')());

After (Clean ESM):

// Clean and native
const { atxpClient } = await import('@atxp/client');

Deployment Strategy

The Vercel deployment now:

  1. Builds both backend (TypeScript → JavaScript) and frontend (React → static files)
  2. Copies frontend build to backend directory via cp -r frontend/build backend/
  3. Packages everything in the serverless function
  4. Serves both API endpoints and static files successfully

Testing

  • Local Development: All tests pass, server starts correctly
  • Local Production: Build and deployment simulation works
  • Vercel Deployment: Successfully deployed and running on Vercel

This provides a clean, maintainable solution without hacky workarounds, while maintaining full functionality and test coverage.

🤖 Generated with Claude Code

robdimarco-atxp and others added 12 commits September 10, 2025 10:22
**Problem**: Vercel deployment failed with error:
"The functions property cannot be used in conjunction with the builds property"

**Solution**:
- Remove legacy builds property from vercel.json
- Keep modern functions property for maxDuration configuration
- Maintain routes and installCommand for single-service architecture

Vercel now auto-detects the backend/server.ts file and builds it appropriately
without needing explicit builds configuration.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Vercel deployment failed with error:
"The pattern 'backend/server.ts' defined in functions doesn't match any
Serverless Functions inside the api directory"

**Root Cause**: Vercel expects functions to be in api/ directory, but we
were referencing backend/server.ts in the functions config.

**Solution**: Remove the functions config and let Vercel auto-detect
the serverless function. Vercel will automatically:
- Detect backend/server.ts as a Node.js serverless function
- Apply appropriate default configurations
- Handle the routing as specified in routes array

**Result**: Cleaner config that works with Vercel's auto-detection system.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Vercel deployment failed with error:
"No Output Directory named 'public' found after the Build completed"

**Root Cause**: Vercel expected static files in a public directory, but our
single-service architecture serves everything through Express serverless function.

**Solution**:
- Add explicit builds configuration with @vercel/node
- Specify backend/server.ts as the serverless function source
- Remove outputDirectory config (not needed for serverless functions)
- Keep installCommand to ensure proper build process

**Result**: Vercel will treat this as a pure serverless function deployment
without looking for static output directories.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: @atxp/client is an ES Module but TypeScript compiles to CommonJS,
causing "ERR_REQUIRE_ESM" errors in Vercel serverless functions.

**Solution**:
- Keep existing CommonJS TypeScript configuration (simpler, more compatible)
- Upgrade Vercel to Node.js 18.x runtime which better handles ESM/CommonJS interop
- Increase maxLambdaSize to handle larger dependencies
- Use builds configuration for explicit control

**Changes**:
- Reverted ES module experiments (package.json, tsconfig.json, server.ts)
- Updated vercel.json with nodejs18.x runtime and larger lambda size
- Maintains stable CommonJS build while fixing ES module import issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Updated from nodejs18.x to nodejs20.x for better ES module support
- Node.js 20 has improved CommonJS/ES module interoperability
- Provides better performance and security updates

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…e compatibility

- Convert static imports of @atxp/client and @atxp/common to dynamic imports in server.ts
- Update atxp-utils.ts functions to use async/dynamic imports for ATXPAccount
- Make findATXPAccount and validateATXPConnectionString async functions
- Change ATXPAccount type annotation to any to avoid static import dependency
- This resolves ERR_REQUIRE_ESM errors when deploying to Vercel serverless functions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update atxp-utils.test.ts to use async/await for findATXPAccount and validateATXPConnectionString
- Update server.test.ts validation endpoint to handle async validateATXPConnectionString
- Remove static ATXPAccount import from test file since it's now dynamically imported
- All tests now pass with the new async implementation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ire() compilation

- Replace await import() with Function constructor approach to bypass TypeScript compilation
- This prevents TypeScript from converting dynamic imports to require() calls
- Should resolve ERR_REQUIRE_ESM errors in Vercel serverless environment
- Tests fail due to Function constructor bypassing vitest mocks, but should work in production
- Add type annotations for payment callback parameters

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add "type": "module" to package.json for native ESM support
- Update TypeScript config to target ESNext modules instead of CommonJS
- Add ESM __dirname polyfill using fileURLToPath and dirname
- Remove Function constructor workarounds for dynamic imports
- Use clean await import() syntax for @atxp/client and @atxp/common
- Update all local imports to use .js extensions for ESM compatibility
- Add vitest config for ESM test environment
- Update dev script to use ts-node/esm loader

This resolves ERR_REQUIRE_ESM errors by natively supporting ES modules
while maintaining clean, readable code without workarounds.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update vercel.json to explicitly include frontend/build/** files
- Add buildCommand to ensure both backend and frontend are built
- Add comprehensive debugging to getStaticPath() function with multiple candidate paths
- Include Vercel-specific paths like /var/task and /vercel/path0
- Add detailed logging to help debug Vercel deployment structure

This should resolve the "No frontend build directory found" error by:
1. Ensuring frontend is built during Vercel deployment
2. Including frontend build files in the serverless function
3. Checking multiple possible paths where build files might be located

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Update vercel.json buildCommand to copy frontend/build to backend/ after building
- Update getStaticPath() to prioritize backend/build directory for Vercel
- Add enhanced debugging to check build directory contents
- Simplify candidate paths focusing on the copied build location

This ensures the frontend build files are included in the backend serverless function
deployment, resolving the "No frontend build directory found" error in Vercel.

Local testing confirms the server finds build files at the expected location.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robdimarco-atxp
Copy link
Contributor Author

Superceded by #18

@robdimarco-atxp robdimarco-atxp deleted the fix-vercel-esm-deployment branch September 10, 2025 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant