I'm developing a backend application using Node.js, TypeScript and Hono. I'm trying to use path aliases defined in my tsconfig.json
to import modules, but I'm facing the following error now:
Error Message:
file:///D:/workspace/../backend/src/controllers/MRFController.ts:1
...
SyntaxError: The requested module '@shared/validations/claimValidationSchema' does not provide an export named 'claimsArrayValidationSchema'
at ModuleJob._instantiate (node:internal/modules/esm/module_job:131:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:213:5)
at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
at async loadESM (node:internal/process/esm_loader:34:7)
at async handleMainPromise (node:internal/modules/run_main:66:12)
I'm sharing my project structure and some codebase you might need to review.
project-root/
├── backend/
│ ├── package.json
│ ├── tsconfig.json
│ ├── src/
│ │ ├── controllers/
│ │ │ └── MRFController.ts
│ │ ├── index.ts
│ │ └── ... (other backend source files)
│ └── ... (other backend files)
├── frontend/
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── src/
│ │ └── ... (frontend source files)
│ └── ... (other frontend files)
├── shared/
│ ├── package.json (optional)
│ ├── tsconfig.json (optional)
│ ├── src/
│ │ └── validations/
│ │ └── claimValidationSchema.ts
│ │ └── types/
│ │ └── ... (shared TypeScript types)
│ └── ... (other shared files)
└── ... (other project files)
MRFController.ts
:
import { promises as fs } from "fs";
import path from "path";
import { fileURLToPath } from "url";
import type { Context } from "hono";
import { convertClaimsToMRF } from "@/services/mrfService";
import { ENTITY_NAME, MESSAGES } from "@/utils/constants";
import { getCurrentTimestamp, parseTimestampString } from "@/utils/helpers";
import { MRFReportStatus } from "@/utils/enums";
import type { MRFResponse } from "@shared/types/apiTypes";
import type { MRFRow } from "@shared/types/mrfTypes";
import { claimsArrayValidationSchema } from "@shared/validations/claimValidationSchema";
// Convert `import.meta.url` to a file path
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const jsonFilesDir = path.join(__dirname, "..", "..", "..", "data", "files");
export default class MRFController {
static async generateMRF(context: Context) {
try {
const { claims } = await context.req.json();
try {
claimsArrayValidationSchema.parse(claims);
} catch (error) {
console.log(error);
return context.json<MRFResponse>(
{
success: false,
message: MESSAGES.VALIDATION_ERROR,
error: JSON.stringify(error),
},
400
);
}
// ... rest of the code
} catch (error) {
// ... error handling
}
}
// ... other methods
}
claimValidationSchema.ts
:
import { z } from "zod";
const claimValidationSchema = z.object({
claimId: z.string(),
subscriberId: z.string(),
memberSequence: z.number(),
claimStatus: z.string(),
billed: z.number(),
allowed: z.number(),
paid: z.number(),
paymentStatusDate: z.string(),
serviceDate: z.string(),
receivedDate: z.string(),
entryDate: z.string(),
processedDate: z.string(),
paidDate: z.string(),
paymentStatus: z.string(),
groupName: z.string(),
groupId: z.string(),
divisionName: z.string(),
divisionId: z.string(),
plan: z.string(),
planId: z.string(),
placeOfService: z.string(),
claimType: z.string(),
procedureCode: z.string(),
memberGender: z.string(),
providerId: z.string(),
providerName: z.string(),
});
export const claimsArrayValidationSchema = z.array(claimValidationSchema);
Backend tsconfig.json
:
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"allowJs": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* Paths */
"baseUrl": ".",
"paths": {
"~/*": ["src/*"],
"@shared/*": ["../shared/src/*"]
}
},
"include": ["src", "vite.config.ts"]
}
Frontend tsconfig.json
(working fine):
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"allowJs": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* Paths */
"baseUrl": ".",
"paths": {
"~/*": ["src/*"],
"@shared/*": ["../shared/src/*"],
"@rules/*": ["../rules/src/*"]
}
},
"include": ["src", "vite.config.ts"]
}
Frontend vite.config.ts
:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tsconfigPaths()],
build: {
outDir: "build",
},
});
Description:
claimsArrayValidationSchema
from @shared/validations/claimValidationSchema
.@shared/*
is defined in my tsconfig.json
and points to "../shared/src/*"
.SyntaxError
mentioned above.How can I resolve the SyntaxError
in my Node.js + TypeScript backend when importing modules using path aliases defined in tsconfig.json
? Is there a way to configure my backend so that it understands the path aliases without adding extra build steps or using tools like ts-node
with tsconfig-paths
?
I finally resolved it. It is because Node.js doesn't understand the TypeScript path aliases defined in tsconfig.json
by default.
Create a tsconfig.json
in the shared directory and define the aliases there.
shared/tsconfig.json
:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "Node",
"experimentalDecorators": true,
"declaration": true,
"baseUrl": ".",
"paths": {
"~/*": ["src/*"]
},
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "**/*.ts"],
"exclude": ["node_modules"]
}
Make sure each project (frontend
, backend
, and shared
) has "type": "module"
in its package.json
.
Example package.json
:
{
"type": "module",
...
}
Verify tsconfig paths in the backend/tsconfig.json
: Make sure the backend project’s tsconfig.json
includes the correct paths to the shared modules, like so:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["src/*"],
"@shared/*": ["../shared/src/*"]
}
},
"include": ["src"]
}