Logo
Published on

Export Syntax

How Export Syntax Creates Clear Module APIs

ES6 export syntax offers multiple ways to expose module functionality - inline exports, grouped declarations, and mixed patterns. Choosing the right export style communicates intent and makes your module's public API obvious. Teams using consistent export patterns report better code organization and easier refactoring.

TL;DR

  • Export inline with export function getName() {}
  • Group exports with export { func1, func2, constant }
  • Mix exports: combine inline and grouped as needed
  • Rename exports with export { localName as publicName }
const moduleAPI = { getName: () => 'user', func1: true, func2: true }

The Module API Design Challenge

You're creating a utility module that needs to expose various functions, constants, and classes. Some functions are closely related, others are standalone, and you want to provide aliases for certain exports. You need a clear export strategy that makes the module's public API obvious at a glance.

// Inconsistent export patterns
function formatDate(date) {
  return date.toLocaleDateString()
}
const API_URL = 'https://api.example.com'
class UserService {
  getUser(id) {
    return fetch(`${API_URL}/users/${id}`)
  }
}
// Mixed export approaches at the end
const exported = { formatDate, API_URL, UserService }
console.log('Inconsistent exports hide API structure')
// module.exports = exported

ES6 export syntax provides consistent patterns that make the module API clear and maintainable:

// Clean, consistent export patterns
function formatDate(date) {
  const formatted = date.toLocaleDateString('en-US')
  console.log('Formatted date:', formatted)
  return formatted
}
const API_URL = 'https://api.example.com'
class UserService {
  getUser(id) {
    console.log(`Fetching user ${id} from ${API_URL}`)
    return fetch(`${API_URL}/users/${id}`)
  }
}
console.log('Clean export patterns improve API clarity')

Best Practises

Use inline exports when:

  • ✅ Function definition is the main focus
  • ✅ Exports are scattered throughout the file
  • ✅ Class or function is immediately ready to export
  • ✅ Following a declare-and-export pattern

Use grouped exports when:

  • 🔄 Organizing multiple related functions
  • 🔄 Need to rename exports for public API
  • 🔄 Want exports visible at bottom of file
  • 🔄 Creating a clear "public interface" section

Avoid when:

  • 🚩 Mixing too many export styles creates confusion
  • 🚩 Export patterns don't match team conventions
  • 🚩 Circular dependencies between modules
  • 🚩 Exporting mutable variables or state

System Design Trade-offs

AspectInline ExportsGrouped ExportsMixed Pattern
VisibilityImmediate - at definitionDelayed - at end of fileVaried - context dependent
RefactoringEasy - change one placeComplex - update two placesMixed - depends on pattern
API OverviewScattered - need to scan fileClear - all exports togetherPartial - some obvious
IntentFunctional - what it doesArchitectural - what's publicFlexible - best of both
MaintenanceSimple - declaration is exportCareful - keep list updatedModerate - watch consistency
ToolingGood - IDE highlights exportsExcellent - clear export listGood - mixed support

More Code Examples

❌ Scattered export chaos
// math-utils.js - Inconsistent and unclear exports
function add(a, b) {
  console.log(`Adding: ${a} + ${b}`)
  return a + b
}
const PI = 3.14159
// Some exports scattered throughout
function multiply(a, b) {
  console.log(`Multiplying: ${a} * ${b}`)
  return a * b
}
function subtract(a, b) {
  console.log(`Subtracting: ${a} - ${b}`)
  return a - b
}
class Calculator {
  constructor() {
    console.log('Calculator instance created')
    this.history = []
  }
  calculate(operation, a, b) {
    let result
    switch (operation) {
      case 'add':
        result = add(a, b)
        break
      case 'subtract':
        result = subtract(a, b)
        break
      default:
        result = 0
    }
    this.history.push({ operation, a, b, result })
    console.log('Calculation result:', result)
    return result
  }
}
// More exports at the end, completely disconnected
const exportedItems = { add, subtract, PI, Calculator }
// What's exported? You have to scan the entire file!'
console.log('Scattered exports make API unclear')
console.log('Need to hunt through file to find all exports')
✅ Organized export patterns
// math-utils.js - Clean, organized module patterns
// Functions for primary operations
function add(a, b) {
  console.log(`Adding: ${a} + ${b} = ${a + b}`)
  return a + b
}
function multiply(a, b) {
  const result = a * b
  console.log(`Multiplying: ${a} * ${b} = ${result}`)
  return result
}
function subtract(a, b) {
  const result = a - b
  console.log(`Subtracting: ${a} - ${b} = ${result}`)
  return result
}
function divide(a, b) {
  const result = b !== 0 ? a / b : 0
  console.log(`Dividing: ${a} / ${b} = ${result}`)
  return result
}
// Constants defined inline
const PI = 3.14159265359
const E = 2.71828182846
// Main calculator class
class Calculator {
  constructor() {
    console.log('Calculator instance created')
    this.history = []
  }
  calculate(operation, a, b) {
    let result
    switch (operation) {
      case 'add':
        result = add(a, b)
        break
      case 'multiply':
        result = multiply(a, b)
        break
      case 'subtract':
        result = subtract(a, b)
        break
      case 'divide':
        result = divide(a, b)
        break
      default:
        console.log('Unknown operation:', operation)
        result = 0
    }
    this.history.push({ operation, a, b, result, timestamp: Date.now() })
    console.log(`Calculator: ${operation}(${a}, ${b}) = ${result}`)
    return result
  }
  getHistory() {
    console.log('Returning calculation history:', this.history.length, 'entries')
    return [...this.history]
  }
}

Technical Trivia

The Missing Export Catastrophe of 2020: A healthcare app's critical module had functions defined but not exported due to inconsistent export patterns. The sanitizePatientData function was defined but missing from the export statement, causing patient records to be stored unsanitized. This violated HIPAA compliance and exposed sensitive data.

Why scattered exports failed: The team mixed inline exports with grouped exports inconsistently. When adding new functions, developers forgot to update the export list at the bottom of files. Code reviews focused on functionality, not export completeness.

Consistent export patterns prevent disasters: Adopting either all-inline or all-grouped patterns makes missing exports obvious. Modern bundlers warn about unused functions, and TypeScript catches missing exports during build. ESLint rules can enforce consistent export patterns across projects.


Master Export Syntax: Choose Your Pattern

Decide on inline exports for immediate visibility or grouped exports for API clarity, then stay consistent. Inline exports work well for focused modules, while grouped exports shine for complex utilities. Mix patterns thoughtfully - use inline for primary exports and groups for secondary functions. Consistency across your codebase matters more than the specific pattern chosen.