nodejs - Node.js as a Language

Node.js is not a language , it’s a runtime
Same V8 engine that runs Chrome , stripped of the DOM and window , glued to libuv for async I/O , wrapped in C++ bindings for filesystem access , network sockets , and process control. JavaScript in Node looks like browser JS but lives in a completely different world - no document , no fetch without a polyfill (well , now there is but you get the point) , and require() instead of <script> tags

the difference matters for security

In the browser , JS is sandboxed. DOM-based XSS steals cookies but can’t read /etc/passwd. In Node , that same eval() becomes a full RCE with filesystem and network access under the process user. Same language , opposite threat model - and that’s why this section exists

Node-specific JS patterns

CommonJS vs ESM:

// CommonJS - Node's original module system
const fs = require('fs')
const express = require('express')

// ESM - modern standard , same as browser
import fs from 'fs'
import express from 'express'

CJS uses require() / module.exports , ESM uses import / export. Node supports both but they don’t always mix cleanly. "type": "module" in package.json flips the default

Process APIs:

// Environment - always validate , never trust
const dbPass = process.env.DB_PASSWORD // can be undefined , crash or leak

// Command-line args
const port = process.argv[2] || 3000 // always a string , sanitize it

// Process control
process.on('uncaughtException', (err) => {
  console.error('your server ate shit:', err.message)
  process.exit(1) // don't try to recover , just die clean
})

// Current working directory - can be exploited
process.cwd() // attackers might symlink here

File system basics:

import { readFileSync , writeFileSync , existsSync } from 'fs'

// Synchronous - blocks the event loop , fine for startup
const config = JSON.parse(readFileSync('./config.json', 'utf-8'))

// Asynchronous - preferred for request handlers
import { readFile } from 'fs/promises'
const data = await readFile('./data.txt', 'utf-8')

// path traversal check - non-negotiable
import { resolve , normalize } from 'path'
const safePath = normalize(resolve(baseDir, userInput))
if (!safePath.startsWith(baseDir)) throw new Error('path traversal detected')

Event loop awareness:

// This blocks EVERYTHING - one user ruins it for everyone
function heavySync() {
  for (let i = 0; i < 1e9; i++) JSON.parse('{}')
}

// This doesn't
import { setTimeout } from 'timers/promises'
async function heavyAsync() {
  for (let i = 0; i < 1e9; i++) {
    JSON.parse('{}')
    if (i % 1000 === 0) await setTimeout(0) // yield to event loop
  }
}

gotchas that will burn you

  • this behaves differently in CommonJS vs ESM modules
  • __dirname is undefined in ES modules - use import.meta.url instead
  • require.cache can be poisoned if you’re not careful
  • Buffer is the Node equivalent of Uint8Array with extra methods
  • globalThis works everywhere but global is Node-only

quick reference index

ConceptFile in backend/node
Home / IntroNode.js Intro
Getting StartedGetting Started
JS RequirementsJS Requirements
Node vs BrowserNode vs Browser
V8 EngineV8 Engine
Event LoopEvent Loop
Modules (CJS)Modules
ES ModulesES Modules
NPMNPM
package.jsonpackage.json
File SystemFile System
PathPath
OSOS
EventsEvents
BuffersBuffers
StreamsStreams
HTTPHTTP
AsyncAsync/Await
Error HandlingError Handling
SecurityOWASP
TestingTesting
PerformanceProfiling
DeploymentEnv Setup

For the full Node.js backend curriculum , see Node.js Backend

Node is JavaScript with the training wheels off
Everything you learn about browser JS applies , but the attack surface is wider , the consequences are higher , and there’s no try {} catch(e) {} for a production server that just crashed. Learn it right or learn it the hard way - your call