Understanding Node.js: Overview & Uses
Understanding Node.js: Overview & Uses
What is [Link]?
Definition
[Link] is an open-source, cross-platform, JavaScript runtime environment that allows developers to
run JavaScript code outside the browser — typically on the server side.
It uses Google Chrome’s V8 JavaScript engine, the same engine that powers the Chrome browser, to
execute code.
Key Points
• Built on V8 Engine (fast execution of JS code).
• Uses Event-driven, non-blocking I/O model — making it lightweight and efficient.
• Ideal for building real-time, data-intensive applications.
• Runs on various OS like Windows, Linux, macOS.
Think of [Link] as a “JavaScript runtime factory” — instead of only running JS inside browsers (like
Chrome or Firefox), Node lets you run JS directly on your computer/server to build web apps or backend
services.
Example
File: [Link]
// Print a message
[Link]("Hello, [Link]!");
Run in terminal:
node [Link]
Output:
Hello, [Link]!
Explanation:
Node executes the JS file using its V8 engine — no browser needed.
This is the simplest form of Node program.
2. Real-Time Applications
• Chat applications (like Slack) or live gaming platforms.
• Built using WebSockets for two-way communication.
Example Framework: [Link]
3. Streaming Applications
Node supports streaming of data — useful for video/audio streaming platforms (like Netflix).
[Link] Architecture
• [Link] uses a single-threaded event-driven architecture, built on top of Google’s V8 JavaScript
engine and libuv library (for asynchronous I/O operations).
• It efficiently handles multiple concurrent operations without creating multiple threads per request.
1. Application Layer
This is your JavaScript code — the part you write as a developer.
Examples:
const fs = require('fs');
[Link]('[Link]', (err, data) => {
if (err) throw err;
[Link]([Link]());
});
• You write asynchronous JavaScript code.
• When I/O operations (like reading a file, network calls, DB access) are needed, [Link] delegates
these to the libuv system.
• The code runs inside the V8 engine.
2. V8 Engine
• Developed by Google (used in Chrome).
• It converts JavaScript code into machine code for fast execution.
• It handles the execution of your JS logic — variables, loops, functions, etc.
Think of it as the brain that executes all your JavaScript.
3. [Link] Bindings
• These are C/C++ bindings that connect the V8 engine to libuv and the operating system.
• They act like a bridge between your JS code and low-level OS features (like file systems, sockets,
threads, etc.).
Example: When you use [Link](), the call goes through [Link] bindings before reaching the OS.
They are part of Node’s “glue code” that enables JavaScript to talk to native libraries.
Event Loop
The event loop constantly runs in the background and checks:
• Are there any events to handle (like I/O completion)?
• Are there callbacks waiting to be executed?
It follows this cycle:
1. Wait for tasks/events.
2. Pick a callback from the Event Queue.
3. Execute the callback using the V8 engine.
4. Repeat.
This makes [Link] single-threaded but asynchronous.
Event Queue
• A queue (FIFO) that stores all callbacks or events waiting to be executed.
• Once the event loop finds that the V8 thread is free, it picks a callback from the queue and executes
it.
Thread Pool
Even though [Link] is single-threaded, libuv maintains a thread pool (default: 4 threads).
When you do tasks like:
• File system I/O
• DNS lookup
• Network requests
• Compression
libuv delegates these to the thread pool so they run in parallel (background threads).
Once done, results are sent back to the Event Queue, and the callback is executed by the event loop.
Flow of Operations
1. JS Code executes in the V8 engine.
2. I/O requests (e.g., file read) go through [Link] bindings.
3. libuv handles the async request.
4. If it’s a blocking operation (file read, network call), it goes to the thread pool.
5. When the operation completes, libuv pushes the callback to the Event Queue.
6. The event loop picks it up and executes it in the V8 engine.
5. Thread Pool
• Located inside libuv.
• Used for tasks that cannot be executed asynchronously at the OS level.
• It provides concurrency for CPU-intensive or blocking tasks.
• Default pool size = 4, but can be increased using:
• export UV_THREADPOOL_SIZE=8
6. Evented I/O Flow Example
Let’s visualize the file reading operation in [Link]:
User Code → [Link]('[Link]', callback)
↓
[Link] Bindings → Libuv → Thread Pool (File System thread)
↓
File Read Complete → Event added to Event Queue
↓
Event Loop → Executes callback → V8 Engine → Output
The main thread never gets blocked — other tasks can run simultaneously.
Example:
[Link] works like a busy restaurant with one smart waiter (event loop) who handles many customers
efficiently by delegating heavy work (I/O) to the kitchen (libuv) — and everyone gets served quickly
without waiting in line.
Summary Table
Component Role
Application User’s JavaScript code
V8 Engine Executes JavaScript
[Link] Bindings Bridge between JS and C++
libuv Provides event loop and thread pool
Event Loop Manages async operations
Thread Pool Executes blocking operations asynchronously
Event Queue Stores pending callbacks
Limitations
Limitation Explanation
Single Threaded Not ideal for CPU-heavy tasks (like image processing).
Callback Hell Complex nesting of async functions can make code hard to
manage.
Less Suitable for Complex Better for I/O bound apps than CPU-bound ones.
Computations
Error Handling Asynchronous code needs careful error handling.
NPM (Node Package Manager)
Instead of writing hashing logic yourself, you can use the NPM package bcrypt.
Install the package
npm install chalk@4
[Link]
// Import the chalk package
const chalk = require('chalk');
// Use chalk to print colored text
[Link]([Link]('Success! Your app is working fine.'));
[Link]([Link]('This is a message in blue.'));
[Link]([Link]('Error: Something went wrong!'));
[Link]([Link]('Warning: Check your input.'));
Run the code: node [Link]
Explanation:
• const chalk = require('chalk'); → imports the installed chalk module.
• [Link](), [Link](), [Link]() → are functions provided by the package to style
console text.
• You didn’t have to write any color logic yourself — just installed and used it via npm.
2. Installing Global & Local Packages
Local Packages
Installed inside your project directory.
They are project-specific and stored in the node_modules folder.
Commands:
1. npm init -y → initialize the project
2. npm install moment → installing the local package
This installs moment locally and adds it to your [Link] under "dependencies".
3. create an [Link] file
// Import moment package
const moment = require('moment');
// Display current date and time
[Link]("Current Date and Time:", moment().format('MMMM Do YYYY, h:mm:ss a'));
// Display formatted date
[Link]("Today is:", moment().format('dddd'));
// Display date after 7 days
[Link]("One week from now:", moment().add(7, 'days').format('MMMM Do YYYY'));
4. Run local_app.js
output:
Explanation:
• npm install moment → installs the package locally.
• It is stored inside the node_modules folder.
• You import it using require('moment') and use it only in this project.
Global Packages
Installed system-wide and can be used in any project or directly from the command line.
Command:
1. npm install -g nodemon → installs nodemon globally on your system.
2. create a file “[Link]”
[Link]("Server is running...");
[Link]("Server restarted successfully!");
[Link]("server has started again!");
3. Run using nodemon
nodemon global_server.js
Explanation:
• Installed with -g → available globally (system-wide).
• Used from the terminal without requiring require() in code.
• Useful for tools, CLIs, and utilities.
[Link]
This file is automatically generated when you install packages.
Purpose:
• Records exact versions of all installed packages.
• Ensures consistent installations across different machines.
• Helps npm install the same dependency tree every time.
Example:
If your [Link] has:
"express": "^4.18.2"
and it installs version 4.18.2,
the [Link] will record that exact version (and dependencies of express) to lock it down.
5. Semantic Versioning
Semantic versioning defines how versions are numbered and updated.
Format:
[Link]
Type Example Meaning
MAJOR 2.x.x Breaking changes
MINOR x.3.x New features (backward compatible)
PATCH x.x.5 Bug fixes (backward compatible)
Version Symbols
Symbol Example Meaning
^ "^4.18.2" Update to latest minor or patch version
(4.x.x)
~ "~4.18.2" Update only patch versions (4.18.x)
None "4.18.2" Use exact version only
Example:
"dependencies": {
"express": "^4.18.2",
"mongoose": "~7.2.0",
"cors": "2.8.5"
}
Explanation:
• express can update to 4.19.0 or 4.20.0 but not 5.0.0
• mongoose can update to 7.2.1, 7.2.2 but not 7.3.0
• cors is fixed at 2.8.5 only.
6. Using NPX
Definition:
npx (Node Package eXecute) is a tool that allows you to run packages without installing them globally.
Example 1 – Running create-react-app:
npx create-react-app myapp
Explanation:
• This runs the create-react-app package temporarily.
• It downloads, runs, and deletes it automatically after execution.
• No need to install globally with npm install -g.
Summary Table
Concept Description Example
NPM Node Package Manager npm install express
Local package Installed in current project npm install lodash
Global package Installed system-wide npm install -g nodemon
npm init Initializes a project npm init -y
[Link] Project metadata & dependencies Contains "dependencies"
[Link] Locks dependency versions Auto-created by npm
Semantic versioning Defines update levels ^1.2.3, ~1.2.3, 1.2.3
npx Runs packages without installation npx create-react-app myapp
Node Module System
In [Link], modules are simply reusable blocks of code that are separated into different files. They make
your project modular, organized, and easy to maintain.
1. Local Modules (Create & Use) - Local modules are the ones you create yourself inside your project.
Example: Create and Use a Local Module
1. Create a file: ex1_math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a,b){
return a * b;
}
// Export the functions
[Link] = { add, subtract, multiply };
Explanation
• [Link] defines two functions and exports them using [Link].
• [Link] imports them using require('./math').
• The ./ indicates it’s a local file.
• This allows you to organize your project into multiple files.
Output:
Output:
Example: ES Modules
Add this in your [Link]:
{
"type": "module"
}
Then use:
// [Link] // [Link]
export function greet(name) { import { greet } from './[Link]';
[Link](`Hello, ${name}`); greet('John');
}
Explanation:
• CommonJS is older and synchronous — used by default.
• ES Modules are modern and asynchronous — align with browser standards.
• Both works fine, but ES Modules are now recommended for new projects.
// Write to a file
[Link]('[Link]', 'Hello from [Link]!');
Third-Party Modules
Third-party modules are external packages installed from the npm registry.
You install them using:
npm install package-name
[Link]([Link]('Success!'));
[Link]([Link]('Error occurred!'));
[Link]([Link]('Warning!'));
Explanation:
• Installed using npm install chalk.
• Makes console output more readable and attractive.
Summary Table
Module Type Source Installation Example Usage
Local Module Created by you Not needed ./[Link] require('./math')
Core Module Built-in with Not needed fs, os, path, http require('fs')
[Link]
Third-Party npm Registry npm install chalk, nodemon, require('chalk')
Module express
CommonJS Default system Uses Old syntax Compatible
require/[Link] everywhere
ES Modules Modern Uses import/export New syntax More modern and
system async
Core Node Modules
Example:
const path = require('path');
const filePath = '/users/local/[Link]';
[Link]('Directory:', [Link](filePath));
[Link]('File Name:', [Link](filePath));
[Link]('Extension:', [Link](filePath));
[Link]('Joined Path:', [Link]('folder', 'subfolder', '[Link]'));
Output:
Directory: /users/local
File Name: [Link]
Extension: .txt
Joined Path: folder/subfolder/[Link]
2. os — System Information
Purpose:
• Provides information about the operating system and the computer.
Import:
const os = require('os');
Common Methods:
Method Description
[Link]() OS type (Windows_NT, Linux, etc.)
[Link]() OS platform (win32, linux, darwin)
[Link]() CPU architecture (x64, arm)
[Link]() Total memory (in bytes)
[Link]() Free memory (in bytes)
[Link]() System uptime in seconds
[Link]() System hostname
[Link]() Current user information
Example:
const os = require('os');
[Link]('OS Type:', [Link]());
[Link]('Platform:', [Link]());
[Link]('Architecture:', [Link]());
[Link]('Total Memory:', [Link]());
[Link]('Free Memory:', [Link]());
[Link]('System Uptime (seconds):', [Link]());
[Link]('User Info:', [Link]());
Output (example):
OS Type: Windows_NT
Platform: win32
Architecture: x64
Total Memory: 8388608000
Free Memory: 5120000000
System Uptime (seconds): 12452
User Info: { username: 'admin', homedir: 'C:\\Users\\admin' }
Output:
File written successfully.
File content: Hello, [Link]!
Output:
File written asynchronously.
File content: This is async write.
Output:
Server running at [Link]
Visit: [Link] → You’ll see “Hello, World! This is a [Link] server.”
Example Explanation:
• createServer() → Creates an HTTP server.
• req → Request object (from client).
• res → Response object (to send data to client).
• [Link](200, { 'Content-Type': 'text/plain' }) → Sets HTTP header.
• [Link]() → Sends the response and ends the request.
Output:
Host: [Link]
Pathname: /products
Query: { category: 'books', price: '200' }
Example Explanation:
• [Link](urlString, true) → Parses the URL.
• .host → Returns domain name.
• .pathname → Returns path (/products).
• .query → Returns query parameters as an object.
Summary Table
Explanation:
• We create an EventEmitter object.
• The on method attaches a listener to the 'greet' event.
• The listener is a function that receives arguments passed during emission.
• emit triggers the event, passing 'Alice' as an argument to the listener.
• If multiple listeners are registered, they all get called in sequence.
You can register multiple listeners for the same event, and they will all fire when the event is emitted.
Output:
// Registering user: Bob
// User Bob has been registered successfully.
// Sending welcome email to Bob.
Explanation:
• We extend EventEmitter to create a UserRegistration class.
• The registerUser method performs an action and emits a custom 'userRegistered' event with the
username.
• Two listeners are attached: one logs a success message, and the other simulates sending an email.
• When registerUser is called, both listeners are invoked, demonstrating how custom events can trigger
multiple actions.
This pattern is common in libraries for handling asynchronous workflows without tight coupling.
// Handle errors
[Link]('error', (err) => {
[Link]('Server error:', err);
});
Explanation:
• [Link]() returns an EventEmitter instance.
• The 'request' event is handled to process incoming HTTP requests, sending a simple response.
• 'listening' confirms the server has started.
• 'error' catches any server errors, like port conflicts.
• This shows how events drive the HTTP server's lifecycle, allowing custom handling without blocking
the main thread.
For more complex scenarios, you can emit custom events from within the request handler to notify other parts
of the app.
File System & Streams
The fs module lets you work with files (read/write, create/delete, etc.).
Explanation:
• writeFileSync() and readFileSync() block execution until the operation completes.
• Useful for small files or initialization.
Example (Asynchronous):
const fs = require('fs');
// Write asynchronously
[Link]('[Link]', 'Hello Async [Link]!', (err) => {
if (err) throw err;
[Link]('File written successfully!');
});
// Read asynchronously
[Link]('[Link]', 'utf-8', (err, data) => {
if (err) throw err;
[Link](data);
});
Explanation:
• [Link]() and [Link]() do not block other code.
• Callback is executed once the operation finishes.
Using [Link]
Example:
const fs = require('fs').promises;
Explanation:
• [Link] returns a Promise instead of using callbacks.
• Easier to use with async/await.
Streams in [Link]
What is a Stream?
• A Stream in [Link] is a sequence of data that is read or written continuously over time.
• Instead of reading or writing the whole data at once (like in normal file I/O), streams process data
chunk by chunk, making them efficient for large files or real-time data (e.g., video, audio,
network communication).
• Streams are a core part of [Link] and are implemented using the stream module.
Types of Streams
[Link] has four main types of streams:
Type Description Example
Readable Stream Used to read data Reading files, HTTP request data
Writable Stream Used to write data Writing to files, sending HTTP responses
Duplex Stream Both readable and writable TCP sockets
Transform Stream Modifies or transforms the data zlib compression, crypto encryption
Explanation:
• [Link]() creates a readable stream for [Link].
• The 'data' event is triggered every time a chunk of data is read.
• 'end' is triggered when all data has been read.
• Unlike [Link](), this does not load the entire file into memory — it reads it in parts, making it
efficient for large files.
Explanation:
• .pipe() connects the output of one stream to the input of another.
• Data flows automatically from [Link] → [Link].
• It handles backpressure (i.e., not overwhelming the writable stream).
Backpressure:
If the writable stream can’t process data as fast as the readable stream provides it, [Link] automatically
slows down the readable stream. This prevents memory overload.
Buffer in [Link]
What is a Buffer?
• A Buffer is a temporary storage area for raw binary data (bytes).
• It helps [Link] handle binary streams of data (like files, images, or videos).
• Since JavaScript traditionally handles only text (UTF-8 strings), [Link] introduced Buffer to work
with binary data directly.
1. Creating a Buffer
// Create a buffer from a string
const buf = [Link]('Hello World', 'utf-8');
[Link](buf); // <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64>
[Link]([Link]()); // Hello World
Explanation:
• [Link]() converts a string into binary data.
• Each pair of hex numbers (48, 65, etc.) represents ASCII codes.
• toString() converts the buffer back to a string.
2. Writing to Buffer
const buf = [Link](20); // Allocate 20 bytes
[Link]('[Link] Streams');
[Link]([Link]());
Explanation:
• [Link](size) creates an empty buffer.
• write() adds data to the buffer.
• Buffers are useful when working with binary or large data chunks.
Explanation:
• This creates an HTTP server that streams a video file.
• The browser sends a Range header requesting partial data (for fast seek/skip).
• [Link] uses [Link]() to send only the requested chunk of the file.
• The video loads progressively, not all at once.
• This mimics real-world streaming (YouTube, Netflix, etc.).
Summary Table
Concept Description Key Methods
Readable Stream Read data chunk by chunk [Link](), .on('data'), .on('end')
Writable Stream Write data chunk by chunk [Link](), .write(), .end()
Pipe Connect readable → writable .pipe()
Buffer Temporary binary data storage [Link](), [Link](), .toString()
Use Case Efficiently stream media files [Link]() + HTTP server
Error Handling in [Link]
Example:
try {
let data = [Link]('Invalid JSON'); // This will throw a SyntaxError
[Link](data);
} catch (err) {
[Link]('Error occurred:', [Link]);
}
Explanation:
• [Link]('Invalid JSON') throws a SyntaxError because the string is not valid JSON.
• The catch block handles it gracefully, so the program doesn’t crash.
2. Asynchronous Errors
These occur later, usually inside callbacks, Promises, or asynchronous functions.
You cannot catch them using a simple try...catch if they are outside the synchronous flow.
Example (fails to catch):
try {
setTimeout(() => {
throw new Error('Async error');
}, 1000);
} catch (err) {
[Link]('Caught error:', [Link]);
}
Output:
Uncaught Error: Async error
Correct way:
Use an error-first callback or Promise .catch() to handle it.
Example 1: Synchronous
try {
let result = 10 / 0;
[Link]('Result:', result);
} catch (error) {
[Link]('Error:', [Link]);
}
Example:
const fs = require('fs');
[Link]('[Link]', 'utf8', (err, data) => {
if (err) {
[Link]('Error reading file:', [Link]);
} else {
[Link]('File content:', data);
}
});
Explanation:
• If an error occurs (e.g., file not found), err contains the error object.
• If successful, data contains the file content.
• This pattern ensures that errors are handled in the same callback, not thrown unexpectedly.
Explanation:
• The [Link]('uncaughtException') listener catches errors not handled elsewhere.
• It logs the error and exits gracefully instead of crashing abruptly.
• However, you should avoid relying only on this, as the application state may be unstable.
Explanation:
• This is used to catch errors from Promises that weren’t caught with .catch().
• Good practice: Always use .catch() or try...catch with async/await.
Summary Table
Error Type Handling Method Example
Synchronous try...catch JSON parsing, arithmetic errors
Asynchronous (Callback) Error-first callback pattern [Link]()
Asynchronous (Promise) .catch() or try...catch [Link]()
Global uncaught errors [Link]('uncaughtException') Unexpected thrown errors
Unhandled Promise errors [Link]('unhandledRejection') Missing .catch() in Promise
Routing in [Link] (Core http Module)
What is Routing?
• Routing refers to determining how an application responds to a client request to a particular URL
path and HTTP method (GET, POST, PUT, DELETE, etc.).
• In [Link], before using frameworks like Express, you can manually implement routing using the
built-in http module.
Explanation:
• The [Link]() method creates a server.
• req (request) object contains request data (method, URL, headers, etc.).
• res (response) object is used to send data back to the client.
Example:
const http = require('http');
const server = [Link]((req, res) => {
[Link]('Content-Type', 'application/json');
if ([Link] === '/' && [Link] === 'GET') {
[Link]([Link]({ message: 'Welcome Home!' }));
} else if ([Link] === '/about' && [Link] === 'GET') {
[Link]([Link]({ message: 'About Us Page' }));
} else if ([Link] === '/create' && [Link] === 'POST') {
[Link]([Link]({ message: 'Data Created Successfully' }));
} else if ([Link] === '/update' && [Link] === 'PUT') {
[Link]([Link]({ message: 'Data Updated Successfully' }));
} else if ([Link] === '/delete' && [Link] === 'DELETE') {
[Link]([Link]({ message: 'Data Deleted Successfully' }));
} else {
[Link] = 404;
[Link]([Link]({ error: 'Route not found' }));
}
});
[Link](3000, () => [Link]('Server running at [Link]
Explanation:
• We used [Link] to match the route path.
• We used [Link] to match the HTTP method.
• Different responses are sent for each route.
• If no route matches, it returns a 404 Not Found.
Example:
const http = require('http');
const url = require('url');
const server = [Link]((req, res) => {
const parsedUrl = [Link]([Link], true);
const path = [Link]('/');
if (path[1] === 'user' && path[2]) {
const userId = path[2];
[Link](200, { 'Content-Type': 'application/json' });
[Link]([Link]({ message: `User ID received: ${userId}` }));
} else {
[Link](404, { 'Content-Type': 'application/json' });
[Link]([Link]({ error: 'User not found' }));
}
});
[Link](3000, () => [Link]('Server running on [Link]
Explanation:
• /user/101 → splits into ['', 'user', '101']
• Extracted parameter = path[2]
• Server responds dynamically with that value.
Example:
[Link]
• [Link] doesn’t provide [Link] directly (like Express does).
• We can use the built-in url and querystring modules.
Example:
const http = require('http');
const url = require('url');
const querystring = require('querystring');
const server = [Link]((req, res) => {
const parsedUrl = [Link]([Link]);
const query = [Link]([Link]);
if ([Link] === '/search') {
[Link](200, { 'Content-Type': 'application/json' });
[Link]([Link]({ name: [Link], age: [Link] }));
} else {
[Link](404, { 'Content-Type': 'application/json' });
[Link]([Link]({ error: 'Route not found' }));
}
});
[Link](3000, () => [Link]('Server running on [Link]
Explanation:
• [Link]([Link]) extracts the pathname and query string.
• [Link]() converts it into a JavaScript object.
Example:
• /search?name=Alice&age=25
• → { name: 'Alice', age: '25' }
• Response shows parsed values.
Example Explanation:
• POST requests may send data in the body (like JSON).
• [Link]('data') collects the incoming chunks.
• [Link]('end') executes after all chunks are received.
• The data is parsed and used in the response.
routes/[Link]
[Link] = (req, res) => {
if ([Link] === '/user' && [Link] === 'GET') {
[Link]([Link]({ message: 'Get all users' }));
} else if ([Link] === '/user' && [Link] === 'POST') {
[Link]([Link]({ message: 'Create new user' }));
} else {
[Link] = 404;
[Link]([Link]({ error: 'User route not found' }));
}
};
routes/[Link]
[Link] = (req, res) => {
if ([Link] === '/product' && [Link] === 'GET') {
[Link]([Link]({ message: 'Get all products' }));
} else if ([Link] === '/product' && [Link] === 'POST') {
[Link]([Link]({ message: 'Create new product' }));
} else {
[Link] = 404;
[Link]([Link]({ error: 'Product route not found' }));
}
};
[Link]
const http = require('http');
const userRoutes = require('./routes/user');
const productRoutes = require('./routes/product');
const server = [Link]((req, res) => {
[Link]('Content-Type', 'application/json');
if ([Link]('/user')) {
userRoutes(req, res);
} else if ([Link]('/product')) {
productRoutes(req, res);
} else {
[Link] = 404;
[Link]([Link]({ error: 'Invalid route' }));
}
});
[Link](3000, () => [Link]('Server running on [Link]
Explanation:
• Routes are separated into different files (modular design).
• The main server imports and delegates routes based on [Link].
• This pattern mimics Express’s Router system but is pure [Link].
Summary Table
Concept Purpose Key Modules Example Route
Basic Routing Handle static routes http /, /about
Route Parameters Handle dynamic URLs url /user/101
Query Parameters Parse query strings url, querystring /search?name=John
Handling Methods Handle GET/POST/PUT/DELETE http /user
Modular Routing Organize routes into separate files require() /routes/[Link]