Books/Node.js Essentials/Modules and Imports

    Modules and Imports

    Modules and Imports

    When you look at AI-generated code, you'll see import and require statements at the top of every file. These are how Node.js files share code with each other. Understanding this system is key to reading and working with any project.

    What is a Module?

    A module is simply a file that exports some code (functions, variables, classes) so other files can use it. Every .js or .ts file in Node.js is a module.

    Think of modules like LEGO bricks — each one does something specific, and you snap them together to build your application.

    Two Module Systems

    Node.js has two ways to handle imports. This confuses many beginners, so let's clear it up:

    FeatureCommonJS (CJS)ES Modules (ESM)
    Syntaxrequire() / module.exportsimport / export
    File extension.js (default).mjs or .js with "type": "module"
    AgeOriginal Node.js systemNewer, modern standard
    Used inOlder projects, config filesModern projects, TypeScript
    LoadingSynchronousAsynchronous

    CommonJS (The Old Way)

    // ── math.js ─── Exporting
    function add(a, b) {
      return a + b;
    }
    
    function multiply(a, b) {
      return a * b;
    }
    
    module.exports = { add, multiply };
    
    // ── app.js ─── Importing
    const { add, multiply } = require("./math");
    
    console.log(add(2, 3));       // 5
    console.log(multiply(4, 5));  // 20

    ES Modules (The Modern Way)

    // ── math.js ─── Exporting
    export function add(a, b) {
      return a + b;
    }
    
    export function multiply(a, b) {
      return a * b;
    }
    
    // ── app.js ─── Importing
    import { add, multiply } from "./math.js";
    
    console.log(add(2, 3));       // 5
    console.log(multiply(4, 5));  // 20

    Which Should You Use?

    Use ES Modules (import/export) for new projects. It's the modern standard, and all TypeScript projects use this syntax. When you see require() in older code, just know it does the same thing as import.

    To enable ES Modules in a Node.js project, add this to your package.json:

    {
      "type": "module"
    }

    What to ask your AI: "Should this project use CommonJS or ES Modules? Convert the require statements to import syntax."

    Named vs Default Exports

    There are two styles of exporting from a module:

    Named Exports

    You can export multiple things from one file. When importing, you use curly braces {}:

    // ── utils.ts ─── Multiple named exports
    export function formatDate(date: Date): string {
      return date.toLocaleDateString();
    }
    
    export function formatCurrency(amount: number): string {
      return `$${amount.toFixed(2)}`;
    }
    
    export const MAX_RETRIES = 3;
    
    // ── app.ts ─── Import specific items by name
    import { formatDate, formatCurrency, MAX_RETRIES } from "./utils";

    Default Exports

    Each file can have one default export. When importing, you don't use curly braces and can name it anything:

    // ── Database.ts ─── Default export
    class Database {
      async connect() { /* ... */ }
      async query(sql: string) { /* ... */ }
    }
    
    export default Database;
    
    // ── app.ts ─── Import the default (name it whatever you want)
    import Database from "./Database";
    import DB from "./Database";        // Same thing, different name

    Combining Both

    A file can have both named exports and a default export:

    // ── api.ts
    export const BASE_URL = "https://api.example.com";
    export function buildUrl(path: string) { return BASE_URL + path; }
    
    // Default export
    const apiClient = { get, post, put, delete: del };
    export default apiClient;
    
    // ── app.ts
    import apiClient, { BASE_URL, buildUrl } from "./api";

    Importing from npm Packages vs Local Files

    This is an important distinction:

    // ── npm packages — just the package name, no path
    import express from "express";
    import { OpenAI } from "openai";
    import dotenv from "dotenv";
    
    // ── Local files — start with ./ or ../
    import { add } from "./math";
    import Database from "../services/Database";
    import { User } from "../../types/User";

    The rule is simple:

    • No dot prefix = npm package (installed in node_modules)
    • Starts with ./ or ../ = a file in your project

    Path Resolution

    When you import a local file, Node.js looks for the file following these rules:

    // If you write:
    import { helper } from "./utils";
    
    // Node.js looks for (in order):
    // 1. ./utils.ts
    // 2. ./utils.js
    // 3. ./utils/index.ts
    // 4. ./utils/index.js

    The index file pattern is common for organizing code:

    src/
      services/
        index.ts        <-- Re-exports everything
        userService.ts
        postService.ts
        authService.ts
    
    // ── services/index.ts ─── Re-export from one place
    export { UserService } from "./userService";
    export { PostService } from "./postService";
    export { AuthService } from "./authService";
    
    // ── app.ts ─── Clean import from the folder
    import { UserService, PostService, AuthService } from "./services";

    Relative Paths Explained

    ./  = current directory
    ../ = parent directory (one level up)
    
    Example project structure:
    src/
      routes/
        users.ts       <-- You're in this file
      services/
        userService.ts
      utils/
        helpers.ts
    
    // From src/routes/users.ts:
    import { UserService } from "../services/userService";  // Go up one, into services
    import { helpers } from "../utils/helpers";              // Go up one, into utils

    TypeScript and Import Resolution

    TypeScript adds a few things to imports:

    Type-Only Imports

    // Import a type (removed at runtime — only used for type checking)
    import type { User } from "./types";
    
    // Import both values and types
    import { createUser, type User, type CreateUserInput } from "./users";

    Path Aliases (tsconfig)

    In TypeScript projects, you'll often see clean paths instead of long relative paths:

    // Without path aliases (messy)
    import { Button } from "../../../components/ui/Button";
    
    // With path aliases (clean)
    import { Button } from "@/components/ui/Button";

    This is configured in tsconfig.json:

    {
      "compilerOptions": {
        "paths": {
          "@/*": ["./src/*"]
        }
      }
    }

    What to ask your AI: "Set up path aliases in my TypeScript project so I can use @/ instead of long relative imports."

    Common Import Patterns in AI Projects

    Here are real-world imports you'll see in AI-generated code:

    // Web server
    import express from "express";
    import cors from "cors";
    
    // AI SDK
    import { OpenAI } from "openai";
    import Anthropic from "@anthropic-ai/sdk";
    
    // Firebase
    import { initializeApp } from "firebase/app";
    import { getFirestore, collection, getDocs } from "firebase/firestore";
    
    // Environment variables
    import dotenv from "dotenv";
    dotenv.config();
    
    // Node.js built-in modules
    import fs from "fs";
    import path from "path";

    What's Next?

    You now understand how Node.js files connect through imports and exports. The next tutorial covers working with files and environment variables — two things you'll do constantly in AI-powered applications.

    What to ask your AI: "I'm getting an import error. Here's the file structure and the import statement: [paste details]. What's wrong?"


    🌐 www.genai-mentor.ai