JavaScript and TypeScript course Β· Module 5: Advanced JavaScript
CommonJS (require/module.exports)
In this lesson11
In the previous exercise we learned about ES Modules - JavaScript's modern module system. Now we'll dive into another, older but still very important module system: CommonJS. Although our Jurassic Park needs the latest technologies to monitor dinosaurs in the browser, its solid server-side infrastructure is built on Node.js, where CommonJS dominates.
What is CommonJS?
CommonJS is a module system created primarily with server-side environments in mind, and it has become the standard in the Node.js ecosystem. It provides a way to organize JavaScript code into separate modules, similar to ES Modules, but with different syntax and behavior.
Unlike ES Modules, which were added to JavaScript in ES6 (2015), CommonJS existed earlier and became the de facto standard for Node.js. Even now, despite official ES Modules support in Node.js, CommonJS remains the dominant format for Node.js modules and in the npm ecosystem.
CommonJS Basics
The main CommonJS concepts are:
- The
require()function for importing modules - The
module.exportsorexportsobject for exporting elements
Let's see how these work in the context of our Jurassic Park system.
Exporting Elements in CommonJS
1. Exporting by Assignment to module.exports
The simplest way to export is by assigning a value to module.exports:
1// dinosaur.js - Dinosaur model
2class Dinosaur {
3 constructor(name, species, diet, dangerLevel) {
4 this.name = name;
5 this.species = species;
6 this.diet = diet;
7 this.dangerLevel = dangerLevel;
8 this.health = 100;
9 this.isContained = true;
10 }
11
12 feed() {
13 this.health = Math.min(100, this.health + 20);
14 return `${this.name} has been fed. Health level: ${this.health}%`;
15 }
16
17 checkStatus() {
18 return {
19 name: this.name,
20 health: this.health,
21 isContained: this.isContained,
22 warning: this.dangerLevel > 7 ? "HIGH DANGER LEVEL" : ""
23 };
24 }
25}
26
27// Export the entire class
28module.exports = Dinosaur;2. Exporting an Object with Multiple Elements
We can also export multiple elements by assigning an object to module.exports:
1// securityUtils.js - Security utility functions
2const SECURITY_LEVELS = {
3 GREEN: "normal",
4 YELLOW: "elevated",
5 ORANGE: "high",
6 RED: "critical"
7};
8
9function checkFenceStatus(sectorId) {
10 // Simulate checking fence status in a given sector
11 const status = Math.random() > 0.1 ? "active" : "failure";
12 return {
13 sector: sectorId,
14 status,
15 timestamp: new Date().toISOString()
16 };
17}
18
19function triggerAlarm(sector, reason) {
20 console.log(`ALARM! Sector ${sector}: ${reason}`);
21 // Alarm activation logic
22 return {
23 success: true,
24 alarmId: "ALR-" + Math.floor(Math.random() * 10000)
25 };
26}
27
28function getSecurityStatus() {
29 // Returns overall park security status
30 return {
31 level: SECURITY_LEVELS.GREEN,
32 activeAlarms: 0,
33 lastChecked: new Date().toISOString()
34 };
35}
36
37// Export multiple elements as an object
38module.exports = {
39 SECURITY_LEVELS,
40 checkFenceStatus,
41 triggerAlarm,
42 getSecurityStatus
43};3. Incrementally Adding to exports
We can also add exports one by one using the exports object (which is a reference to module.exports):
1// parkStats.js - Jurassic Park statistics
2let totalVisitors = 0;
3let revenue = 0;
4const incidents = [];
5
6// Export first function
7exports.addVisitors = function(count) {
8 totalVisitors += count;
9 return totalVisitors;
10};
11
12// Export second function
13exports.addRevenue = function(amount) {
14 revenue += amount;
15 return revenue;
16};
17
18// Export third function
19exports.recordIncident = function(type, location, description) {
20 const incident = {
21 id: incidents.length + 1,
22 type,
23 location,
24 description,
25 timestamp: new Date().toISOString()
26 };
27 incidents.push(incident);
28 return incident;
29};
30
31// Export fourth function
32exports.getDailyStats = function() {
33 return {
34 totalVisitors,
35 revenue,
36 incidentCount: incidents.length
37 };
38};NOTE: Remember that exports is just a reference to module.exports. If you assign something directly to module.exports, you lose that reference, so you cannot mix these styles in one file:
1// This will NOT work
2exports.someFunction = function() { /* ... */ };
3module.exports = SomeClass; // This overwrites the previous export
4
5// This will work
6exports.someFunction = function() { /* ... */ };
7exports.SomeClass = SomeClass;
8
9// This will work too
10module.exports.someFunction = function() { /* ... */ };
11module.exports.SomeClass = SomeClass;
12
13// And this will work (assigning the whole object)
14module.exports = {
15 someFunction: function() { /* ... */ },
16 SomeClass: SomeClass
17};Importing Modules in CommonJS
1. Importing an Entire Module
1// app.js
2const Dinosaur = require('./dinosaur.js');
3
4// Create new dinosaur instances
5const trex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore", 9);
6const brachi = new Dinosaur("Longneck", "Brachiosaurus", "herbivore", 4);
7
8console.log(trex.checkStatus());
9console.log(brachi.feed());2. Importing and Destructuring
1// securityMonitor.js
2const security = require('./securityUtils.js');
3
4// Use imported functions and constants from the object
5function monitorSecurity() {
6 const sectors = ["A1", "B2", "C3", "D4"];
7
8 // Check each sector
9 sectors.forEach(sector => {
10 const fenceStatus = security.checkFenceStatus(sector);
11
12 if (fenceStatus.status === "failure") {
13 const alarmResult = security.triggerAlarm(
14 sector,
15 "Electric fence failure!"
16 );
17 console.log(`Alarm ${alarmResult.alarmId} triggered in sector ${sector}`);
18
19 // Update security status
20 console.log("Changing security level to:", security.SECURITY_LEVELS.RED);
21 }
22 });
23}
24
25setInterval(monitorSecurity, 60000); // Check every minute3. Destructuring on Import
In newer Node.js versions we can also use destructuring directly on import:
1// dashboard.js
2const { checkFenceStatus, triggerAlarm, SECURITY_LEVELS } = require('./securityUtils.js');
3
4// Now we can use functions directly
5function checkAllSectors() {
6 ["A1", "B2", "C3", "D4"].forEach(sector => {
7 console.log(`Checking sector ${sector}: `, checkFenceStatus(sector));
8
9 // Use the imported constant
10 if (Math.random() < 0.1) {
11 console.log(`Setting security level: ${SECURITY_LEVELS.ORANGE}`);
12 }
13 });
14}Differences Between CommonJS and ES Modules
It's important to understand the key differences between CommonJS and ES Modules:
| Feature | CommonJS | ES Modules |
|---|---|---|
| Import syntax | require() | import ... from ... |
| Export syntax | module.exports = ... or exports.x = ... | export ... or export default ... |
| Loading | Synchronous | Asynchronous (loaded before execution) |
| Execution | Code executed on each import | Code executed only once, then module is cached |
| Static analysis | Not supported (dynamic paths) | Supported (paths are static) |
| Browser support | Not natively supported | Supported in modern browsers |
| Hoisting | Imports are not hoisted | Imports are hoisted (usable before declaration) |
| Tree-shaking | Harder to implement | Easier due to static analysis |
Node.js Project Structure (Jurassic Park Backend)
Let's look at how we might organize the backend of our Jurassic Park management system using Node.js and CommonJS:
1jurassic-park-backend/
2βββ config/
3β βββ database.js # Database configuration
4β βββ environment.js # Environment variables
5β βββ security.js # Security settings
6βββ models/
7β βββ dinosaur.js # Dinosaur model
8β βββ employee.js # Employee model
9β βββ facility.js # Facility model
10β βββ index.js # Model aggregator
11βββ controllers/
12β βββ dinosaurController.js # Dinosaur controller
13β βββ securityController.js # Security controller
14β βββ employeeController.js # Employee controller
15βββ routes/
16β βββ api.js # Main API router
17β βββ dinosaurs.js # Dinosaur-related routes
18β βββ security.js # Security-related routes
19βββ services/
20β βββ monitoringService.js # Monitoring service
21β βββ notificationService.js # Notification service
22β βββ loggingService.js # Logging service
23βββ utils/
24β βββ logger.js # Logging utility
25β βββ validators.js # Data validators
26βββ middleware/
27β βββ auth.js # Authentication middleware
28β βββ errorHandler.js # Error handling
29βββ app.js # Main application
30βββ server.js # Server startup fileLet's look at example implementations of these modules:
Configuration File (config/database.js)
1// config/database.js
2module.exports = {
3 host: process.env.DB_HOST || 'localhost',
4 port: process.env.DB_PORT || 27017,
5 name: process.env.DB_NAME || 'jurassic_park',
6 user: process.env.DB_USER || 'admin',
7 password: process.env.DB_PASSWORD || 'secret',
8
9 // Helper function to build the connection URI
10 getConnectionString: function() {
11 return `mongodb://${this.user}:${this.password}@${this.host}:${this.port}/${this.name}`;
12 }
13};Model (models/dinosaur.js)
1// models/dinosaur.js
2const mongoose = require('mongoose');
3const Schema = mongoose.Schema;
4
5// Dinosaur schema
6const dinosaurSchema = new Schema({
7 name: { type: String, required: true },
8 species: { type: String, required: true },
9 diet: { type: String, enum: ['carnivore', 'herbivore', 'omnivore'], required: true },
10 dangerLevel: { type: Number, min: 1, max: 10, required: true },
11 enclosureId: { type: Schema.Types.ObjectId, ref: 'Enclosure' },
12 health: { type: Number, default: 100 },
13 status: {
14 isContained: { type: Boolean, default: true },
15 lastCheck: { type: Date, default: Date.now }
16 },
17 createdAt: { type: Date, default: Date.now },
18 updatedAt: { type: Date, default: Date.now }
19});
20
21// Schema methods
22dinosaurSchema.methods.feed = function() {
23 this.health = Math.min(100, this.health + 20);
24 this.updatedAt = Date.now();
25 return this.save();
26};
27
28dinosaurSchema.methods.checkStatus = function() {
29 return {
30 name: this.name,
31 health: this.health,
32 isContained: this.status.isContained,
33 warning: this.dangerLevel > 7 ? "HIGH DANGER LEVEL" : ""
34 };
35};
36
37// Static methods
38dinosaurSchema.statics.findDangerous = function() {
39 return this.find({ dangerLevel: { $gte: 8 } });
40};
41
42// Pre-save hook
43dinosaurSchema.pre('save', function(next) {
44 this.updatedAt = Date.now();
45 next();
46});
47
48// Create the model
49const Dinosaur = mongoose.model('Dinosaur', dinosaurSchema);
50
51// Export the model
52module.exports = Dinosaur;Controller (controllers/dinosaurController.js)
1// controllers/dinosaurController.js
2const Dinosaur = require('../models/dinosaur');
3const logger = require('../utils/logger');
4
5// Controller object with multiple methods
6const dinosaurController = {
7 // Get all dinosaurs
8 getAllDinosaurs: async (req, res) => {
9 try {
10 const dinosaurs = await Dinosaur.find().populate('enclosureId');
11 res.json(dinosaurs);
12 } catch (error) {
13 logger.error('Error fetching dinosaurs:', error);
14 res.status(500).json({ error: 'Server error' });
15 }
16 },
17
18 // Get dinosaur by ID
19 getDinosaurById: async (req, res) => {
20 try {
21 const dinosaur = await Dinosaur.findById(req.params.id).populate('enclosureId');
22
23 if (!dinosaur) {
24 return res.status(404).json({ error: 'Dinosaur not found' });
25 }
26
27 res.json(dinosaur);
28 } catch (error) {
29 logger.error(`Error fetching dinosaur ID=${req.params.id}: `, error);
30 res.status(500).json({ error: 'Server error' });
31 }
32 },
33
34 // Add a new dinosaur
35 createDinosaur: async (req, res) => {
36 try {
37 const { name, species, diet, dangerLevel, enclosureId } = req.body;
38
39 const newDinosaur = new Dinosaur({
40 name,
41 species,
42 diet,
43 dangerLevel,
44 enclosureId
45 });
46
47 await newDinosaur.save();
48 logger.info(`Added new dinosaur: ${name} (${species})`);
49
50 res.status(201).json(newDinosaur);
51 } catch (error) {
52 logger.error('Error creating dinosaur:', error);
53 res.status(400).json({ error: error.message });
54 }
55 },
56
57 // Feed a dinosaur
58 feedDinosaur: async (req, res) => {
59 try {
60 const dinosaur = await Dinosaur.findById(req.params.id);
61
62 if (!dinosaur) {
63 return res.status(404).json({ error: 'Dinosaur not found' });
64 }
65
66 await dinosaur.feed();
67 logger.info(`Fed dinosaur: ${dinosaur.name}`);
68
69 res.json({
70 message: `${dinosaur.name} has been fed. Health level: ${dinosaur.health}%`,
71 health: dinosaur.health
72 });
73 } catch (error) {
74 logger.error(`Error feeding dinosaur ID=${req.params.id}: `, error);
75 res.status(500).json({ error: 'Server error' });
76 }
77 },
78
79 // Find dangerous dinosaurs
80 getDangerousDinosaurs: async (req, res) => {
81 try {
82 const dangerousDinos = await Dinosaur.findDangerous();
83 res.json(dangerousDinos);
84 } catch (error) {
85 logger.error('Error fetching dangerous dinosaurs:', error);
86 res.status(500).json({ error: 'Server error' });
87 }
88 }
89};
90
91// Export the entire controller object
92module.exports = dinosaurController;Routes (routes/dinosaurs.js)
1// routes/dinosaurs.js
2const express = require('express');
3const router = express.Router();
4const dinosaurController = require('../controllers/dinosaurController');
5const authMiddleware = require('../middleware/auth');
6
7// Dinosaur-related routes
8router.get('/', dinosaurController.getAllDinosaurs);
9router.get('/dangerous', dinosaurController.getDangerousDinosaurs);
10router.get('/:id', dinosaurController.getDinosaurById);
11
12// Routes requiring authentication
13router.post('/', authMiddleware.verifyToken, dinosaurController.createDinosaur);
14router.post('/:id/feed', authMiddleware.verifyToken, dinosaurController.feedDinosaur);
15
16// Export the router
17module.exports = router;Main Application File (app.js)
1// app.js
2const express = require('express');
3const mongoose = require('mongoose');
4const cors = require('cors');
5const morgan = require('morgan');
6const helmet = require('helmet');
7
8// Config and middleware imports
9const dbConfig = require('./config/database');
10const errorHandler = require('./middleware/errorHandler');
11const logger = require('./utils/logger');
12
13// Route imports
14const apiRouter = require('./routes/api');
15const dinosaurRoutes = require('./routes/dinosaurs');
16const securityRoutes = require('./routes/security');
17
18// Initialize Express app
19const app = express();
20
21// Middleware
22app.use(helmet()); // Security
23app.use(cors()); // Cross-Origin Resource Sharing
24app.use(morgan('dev')); // HTTP request logging
25app.use(express.json()); // JSON parsing
26app.use(express.urlencoded({ extended: false })); // Form parsing
27
28// Connect to database
29mongoose.connect(dbConfig.getConnectionString(), {
30 useNewUrlParser: true,
31 useUnifiedTopology: true
32})
33.then(() => {
34 logger.info('Connected to MongoDB database');
35})
36.catch(err => {
37 logger.error('Database connection error:', err);
38 process.exit(1);
39});
40
41// Routes
42app.use('/api', apiRouter);
43app.use('/api/dinosaurs', dinosaurRoutes);
44app.use('/api/security', securityRoutes);
45
46// Handle 404 errors
47app.use((req, res, next) => {
48 res.status(404).json({ error: 'Not found' });
49});
50
51// Centralized error handling
52app.use(errorHandler);
53
54// Export the application
55module.exports = app;Server Startup File (server.js)
1// server.js
2const http = require('http');
3const app = require('./app');
4const logger = require('./utils/logger');
5
6// Port settings
7const port = process.env.PORT || 3000;
8app.set('port', port);
9
10// Create HTTP server
11const server = http.createServer(app);
12
13// Listen for connections
14server.listen(port, () => {
15 logger.info(`Jurassic Park server started on port ${port}`);
16});
17
18// Handle server errors
19server.on('error', (error) => {
20 if (error.syscall !== 'listen') {
21 throw error;
22 }
23
24 logger.error(`Server error: ${error}`);
25 process.exit(1);
26});Circular Dependencies
One challenge when working with CommonJS (and modules in general) is circular dependencies, which occur when module A imports module B and module B imports module A:
1// dinosaurUtils.js
2const Dinosaur = require('./dinosaur');
3
4function isDangerous(dino) {
5 return dino.dangerLevel > 7;
6}
7
8module.exports = { isDangerous };
9
10// dinosaur.js
11const dinosaurUtils = require('./dinosaurUtils'); // Circular dependency!
12
13class Dinosaur {
14 // ... class implementation
15
16 isHighRisk() {
17 return dinosaurUtils.isDangerous(this);
18 }
19}
20
21module.exports = Dinosaur;CommonJS handles circular dependencies, but it can lead to strange behavior like incomplete objects. To avoid such problems:
- Restructure the code to avoid circular dependencies
- Use the dependency injection pattern
- Move shared code to a third module that both import from
Transpiling CommonJS to ES Modules and Back
In many projects we need to handle both CommonJS and ES Modules. For example, the frontend of our Jurassic Park might use ES Modules while the backend uses CommonJS. What do we do when we want to share code?
Babel for Transpilation
We can use Babel to transpile between formats:
1// babel.config.js for ES Modules -> CommonJS
2module.exports = {
3 presets: [
4 ['@babel/preset-env', {
5 targets: {
6 node: 'current'
7 },
8 modules: 'commonjs' // Convert import/export to require/exports
9 }]
10 ]
11};Dual Package Hazard
Another approach is to create packages that support both module formats. In package.json you can define:
1{
2 "name": "jurassic-park-utils",
3 "version": "1.0.0",
4 "main": "dist/index.js",
5 "module": "dist/index.mjs",
6 "exports": {
7 "import": "./dist/index.mjs",
8 "require": "./dist/index.js"
9 }
10}Modern Approach: Node.js with ES Modules
Newer versions of Node.js (from 13.2.0) officially support ES Modules. We can enable this support in a few ways:
- Using the
.mjsextension for ES Module files - Setting
"type": "module"in package.json
However, many npm packages still use CommonJS, so you'll likely need to work with both systems.
Practical Example: Migrating from CommonJS to ES Modules
Let's say we want to migrate part of our backend from CommonJS to ES Modules. Here's how such a migration might look:
Original CommonJS Version (dinosaurController.js)
1// controllers/dinosaurController.js (CommonJS)
2const Dinosaur = require('../models/dinosaur');
3const logger = require('../utils/logger');
4
5function getAllDinosaurs(req, res) {
6 // implementation
7}
8
9function getDinosaurById(req, res) {
10 // implementation
11}
12
13module.exports = {
14 getAllDinosaurs,
15 getDinosaurById
16};Migration to ES Modules (dinosaurController.mjs)
1// controllers/dinosaurController.mjs (ES Modules)
2import Dinosaur from '../models/dinosaur.mjs';
3import logger from '../utils/logger.mjs';
4
5export function getAllDinosaurs(req, res) {
6 // implementation
7}
8
9export function getDinosaurById(req, res) {
10 // implementation
11}
12
13// We can also export a default object with functions
14export default {
15 getAllDinosaurs,
16 getDinosaurById
17};Compatibility Wrapper
Sometimes we need to create a wrapper that allows importing an ES Module from CommonJS code:
1// controllers/dinosaurControllerWrapper.js (CommonJS)
2// This file loads an ES Module and exports it as CommonJS
3const importESM = async () => {
4 const controller = await import('./dinosaurController.mjs');
5 return controller.default; // or selected functions
6};
7
8// Export a function that returns a Promise with the controller
9module.exports = importESM();Summary
CommonJS is a widely used module system in Node.js that allows organizing code into separate modules, which is critical for building scalable applications like the backend of our Jurassic Park management system.
The main CommonJS concepts are:
- The
require()function for importing modules - The
module.exportsorexportsobject for exporting elements - Synchronous module loading
- Essential module caching for performance
Despite the growing popularity of ES Modules, CommonJS remains an important module system, especially in the Node.js ecosystem. Understanding the differences between CommonJS and ES Modules is key to effectively developing JavaScript applications that can run both in the browser and on the server.
In our Jurassic Park, we use CommonJS for the backend (monitoring, database, security control) and ES Modules for the frontend (user interface, dashboards, notifications). This combination gives us the best of both worlds and lets us efficiently manage a system that must be reliable - after all, the safety of the park and its guests depends on our code!
In the next exercise we'll look at dynamic module importing, which provides even greater flexibility in loading code on demand, which can be critical in emergency situations at Jurassic Park.
Code for this lesson: main.js
1// CommonJS in Jurassic Park - Node.js backend
2// Demo of the require/module.exports system
3
4console.log("Jurassic Park backend - CommonJS");
5console.log("Node.js server management system\n");
6
7// ===========================================
8// CommonJS module simulation
9// ===========================================
10
11// === dinosaur.js - Dinosaur model (CommonJS) ===
12class Dinosaur {
13 constructor(name, species, diet, dangerLevel) {
14 this.name = name;
15 this.species = species;
16 this.diet = diet;
17 this.dangerLevel = dangerLevel;
18 this.health = 100;
19 this.id = `DINO-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
20 }
21
22 feed() {
23 this.health = Math.min(100, this.health + 20);
24 return { success: true, message: `${this.name} fed`, health: this.health };
25 }
26
27 getDetails() {
28 return {
29 id: this.id,
30 name: this.name,
31 species: this.species,
32 diet: this.diet,
33 dangerLevel: this.dangerLevel,
34 health: this.health
35 };
36 }
37}
38
39// CommonJS export - main class
40const DinosaurModule = Dinosaur;
41
42// === database.js - Database simulation (CommonJS) ===
43const database = {
44 dinosaurs: new Map(),
45 enclosures: new Map(),
46 logs: []
47};
48
49function saveDinosaur(dinosaur) {
50 database.dinosaurs.set(dinosaur.id, dinosaur);
51 database.logs.push({ action: 'SAVE_DINOSAUR', id: dinosaur.id, timestamp: Date.now() });
52 return { success: true, id: dinosaur.id };
53}
54
55function getDinosaur(id) {
56 const dino = database.dinosaurs.get(id);
57 return dino || null;
58}
59
60function getAllDinosaurs() {
61 return Array.from(database.dinosaurs.values());
62}
63
64function deleteDinosaur(id) {
65 const deleted = database.dinosaurs.delete(id);
66 if (deleted) {
67 database.logs.push({ action: 'DELETE_DINOSAUR', id, timestamp: Date.now() });
68 }
69 return { success: deleted };
70}
71
72function getDatabaseStats() {
73 return {
74 dinosaurCount: database.dinosaurs.size,
75 enclosureCount: database.enclosures.size,
76 totalLogs: database.logs.length
77 };
78}
79
80// CommonJS multiple exports
81const DatabaseModule = {
82 saveDinosaur,
83 getDinosaur,
84 getAllDinosaurs,
85 deleteDinosaur,
86 getDatabaseStats
87};
88
89// === config.js - Configuration (CommonJS) ===
90const config = {
91 server: {
92 port: 3000,
93 host: 'localhost',
94 environment: 'development'
95 },
96 database: {
97 url: 'mongodb://localhost:27017/jurassic-park',
98 maxConnections: 10
99 },
100 security: {
101 maxDangerLevel: 10,
102 alertThreshold: 7
103 },
104 park: {
105 maxDinosaurs: 50,
106 maxVisitors: 1000,
107 openingHours: { start: 9, end: 18 }
108 }
109};
110
111// CommonJS config export
112const ConfigModule = config;
113
114// === logger.js - Logging system (CommonJS) ===
115const LOG_LEVELS = {
116 INFO: 'INFO',
117 WARN: 'WARN',
118 ERROR: 'ERROR',
119 DEBUG: 'DEBUG'
120};
121
122function formatLogMessage(level, message) {
123 const timestamp = new Date().toISOString();
124 return `[${timestamp}] [${level}] ${message}`;
125}
126
127function log(level, message) {
128 const formatted = formatLogMessage(level, message);
129 console.log(formatted);
130 database.logs.push({ level, message, timestamp: Date.now() });
131}
132
133function info(message) { log(LOG_LEVELS.INFO, message); }
134function warn(message) { log(LOG_LEVELS.WARN, message); }
135function error(message) { log(LOG_LEVELS.ERROR, message); }
136function debug(message) { log(LOG_LEVELS.DEBUG, message); }
137
138// CommonJS logger exports
139const LoggerModule = {
140 LOG_LEVELS,
141 log,
142 info,
143 warn,
144 error,
145 debug
146};
147
148// === api-routes.js - API endpoints (CommonJS) ===
149class DinosaurAPI {
150 static createDinosaur(req) {
151 const { name, species, diet, dangerLevel } = req.body;
152
153 LoggerModule.info(`Creating dinosaur: ${name}`);
154
155 const dinosaur = new DinosaurModule(name, species, diet, dangerLevel);
156 DatabaseModule.saveDinosaur(dinosaur);
157
158 LoggerModule.info(`Dinosaur created: ${dinosaur.id}`);
159
160 return {
161 status: 201,
162 data: dinosaur.getDetails()
163 };
164 }
165
166 static getDinosaur(req) {
167 const { id } = req.params;
168 LoggerModule.debug(`Fetching dinosaur: ${id}`);
169
170 const dinosaur = DatabaseModule.getDinosaur(id);
171
172 if (!dinosaur) {
173 LoggerModule.warn(`Dinosaur not found: ${id}`);
174 return {
175 status: 404,
176 error: 'Dinosaur not found'
177 };
178 }
179
180 return {
181 status: 200,
182 data: dinosaur.getDetails()
183 };
184 }
185
186 static listDinosaurs() {
187 LoggerModule.debug('Listing all dinosaurs');
188
189 const dinosaurs = DatabaseModule.getAllDinosaurs();
190
191 return {
192 status: 200,
193 data: dinosaurs.map(d => d.getDetails()),
194 count: dinosaurs.length
195 };
196 }
197
198 static deleteDinosaur(req) {
199 const { id } = req.params;
200 LoggerModule.info(`Deleting dinosaur: ${id}`);
201
202 const result = DatabaseModule.deleteDinosaur(id);
203
204 if (!result.success) {
205 return {
206 status: 404,
207 error: 'Dinosaur not found'
208 };
209 }
210
211 LoggerModule.info(`Dinosaur deleted: ${id}`);
212
213 return {
214 status: 200,
215 data: { deleted: true }
216 };
217 }
218
219 static getServerStats() {
220 LoggerModule.debug('Fetching server stats');
221
222 const dbStats = DatabaseModule.getDatabaseStats();
223
224 return {
225 status: 200,
226 data: {
227 ...dbStats,
228 config: {
229 maxDinosaurs: ConfigModule.park.maxDinosaurs,
230 environment: ConfigModule.server.environment
231 }
232 }
233 };
234 }
235}
236
237// CommonJS API export
238const APIModule = DinosaurAPI;
239
240// ===========================================
241// COMMONJS SYSTEM DEMO
242// ===========================================
243console.log("=== SERVER INITIALIZATION ===\n");
244
245LoggerModule.info('Server starting...');
246LoggerModule.info(`Port: ${ConfigModule.server.port}`);
247LoggerModule.info(`Environment: ${ConfigModule.server.environment}`);
248
249console.log("\n=== CREATING DINOSAURS (API CALLS) ===\n");
250
251// API request simulation
252const req1 = {
253 body: { name: 'Rexy', species: 'T-Rex', diet: 'carnivore', dangerLevel: 9 }
254};
255const response1 = APIModule.createDinosaur(req1);
256console.log('POST /api/dinosaurs:', JSON.stringify(response1, null, 2));
257
258const req2 = {
259 body: { name: 'Blue', species: 'Velociraptor', diet: 'carnivore', dangerLevel: 7 }
260};
261const response2 = APIModule.createDinosaur(req2);
262console.log('\nPOST /api/dinosaurs:', JSON.stringify(response2, null, 2));
263
264const req3 = {
265 body: { name: 'Gentle Giant', species: 'Brachiosaurus', diet: 'herbivore', dangerLevel: 2 }
266};
267const response3 = APIModule.createDinosaur(req3);
268console.log('\nPOST /api/dinosaurs:', JSON.stringify(response3, null, 2));
269
270console.log("\n=== FETCHING DATA (API CALLS) ===\n");
271
272// List of all dinosaurs
273const listResponse = APIModule.listDinosaurs();
274console.log('GET /api/dinosaurs:', JSON.stringify(listResponse, null, 2));
275
276console.log("\n=== SERVER STATISTICS ===\n");
277
278const statsResponse = APIModule.getServerStats();
279console.log('GET /api/stats:', JSON.stringify(statsResponse, null, 2));
280
281console.log("\n=== DIFFERENCES: CommonJS vs ES Modules ===\n");
282
283console.log("CommonJS (Node.js):");
284console.log(" - Uses: require() and module.exports");
285console.log(" - Synchronous loading");
286console.log(" - Dynamic imports at runtime");
287console.log(" - Value is copied\n");
288
289console.log("ES Modules (ES6+):");
290console.log(" - Uses: import and export");
291console.log(" - Asynchronous loading");
292console.log(" - Static imports at compile time");
293console.log(" - Value is a reference (live binding)\n");
294
295console.log("CommonJS example:");
296console.log(" // dinosaur.js");
297console.log(" module.exports = class Dinosaur { ... };");
298console.log("");
299console.log(" // main.js");
300console.log(" const Dinosaur = require('./dinosaur');\n");
301
302console.log("ES Modules example:");
303console.log(" // dinosaur.js");
304console.log(" export default class Dinosaur { ... }");
305console.log("");
306console.log(" // main.js");
307console.log(" import Dinosaur from './dinosaur.js';\n");
308
309LoggerModule.info('Demo completed successfully!');
310
311console.log("\nJurassic Park backend ready!");Spotted a mistake in this lesson?
Hands-on tasks in the game
- Horizontal ordering
Arrange the function export elements: