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
thisbehaves differently in CommonJS vs ESM modules__dirnameis undefined in ES modules - useimport.meta.urlinsteadrequire.cachecan be poisoned if you’re not carefulBufferis the Node equivalent ofUint8Arraywith extra methodsglobalThisworks everywhere butglobalis Node-only
quick reference index
| Concept | File in backend/node |
|---|---|
| Home / Intro | Node.js Intro |
| Getting Started | Getting Started |
| JS Requirements | JS Requirements |
| Node vs Browser | Node vs Browser |
| V8 Engine | V8 Engine |
| Event Loop | Event Loop |
| Modules (CJS) | Modules |
| ES Modules | ES Modules |
| NPM | NPM |
| package.json | package.json |
| File System | File System |
| Path | Path |
| OS | OS |
| Events | Events |
| Buffers | Buffers |
| Streams | Streams |
| HTTP | HTTP |
| Async | Async/Await |
| Error Handling | Error Handling |
| Security | OWASP |
| Testing | Testing |
| Performance | Profiling |
| Deployment | Env 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