| |

JavaScript 68 🧬 Node.js basics

For most of JavaScript’s life, it ran only in the browser. Node.js changed that. It takes the V8 engine — the same one inside Chrome — and wraps it with an environment that can read files, open sockets, spawn processes, and run on servers. Suddenly JavaScript could do what PHP, Python, and Ruby did.

Node isn’t a framework or a language. It’s a runtime — a platform that executes JavaScript outside the browser, with a set of built-in modules for talking to the operating system. Understand those modules and you can build servers, CLIs, build tools, and scripts in the same language you use on the front-end.

Key point: Node.js is single-threaded and event-driven. It handles many connections at once not by spawning threads, but by never blocking — every I/O operation is asynchronous, and the event loop juggles them. That’s the mental model that makes Node feel different from Java or Python.


a – What is Node.js

Node is a JavaScript runtime built on Chrome’s V8 engine. It provides a set of built-in modules for files, networking, processes, and streams, and it includes npm — the largest package registry in the world.

What Node is not:

  • Not a browser — no window, no document, no DOM
  • Not a framework — no opinion about how you structure code
  • Not multi-threaded by default — one main thread, async I/O
  • Not just for servers — CLIs, build tools, scripts, and desktop apps use it

The two globals you get instead of window:

console.log(process.version);
console.log(process.platform);
console.log(process.cwd());
console.log(process.env.NODE_ENV);

The process object is how Node talks about itself. globalThis replaces window as the global object.

The built-in modules:

ModulePurpose
fsFiles and directories
pathPath manipulation
http / httpsServers and requests
osSystem info
eventsEvent emitters
streamStreaming data
cryptoHashing, encryption
urlURL parsing
child_processRun other programs
worker_threadsTrue parallelism
utilUtilities (promisify, inspect)

Importing modules — two styles:

// CommonJS (older, still default for .js in most projects)
const fs = require('fs');

// ESM (modern, used in .mjs or "type": "module")
import fs from 'fs';

CommonJS is synchronous and simple. ESM is the standard. New projects should use ESM.

Running a script:

node script.js

Node executes the file, prints any output, and exits when the event loop is empty.

The REPL:

node
> 2 + 2
4
> process.version
'v20.10.0'

The interactive shell is useful for quick experiments.

Global objects unique to Node:

GlobalPurpose
processRuntime info, env vars, exit
BufferBinary data
__dirnameDirectory of the file (CommonJS)
__filenameFull path of the file
globalGlobal object (like window)
globalThisStandard global

In ESM, __dirname and __filename don’t exist — you derive them from import.meta.url.

The main selling points:

  • One language across the stack — JavaScript on both ends
  • Non-blocking I/O — high concurrency with low overhead
  • Huge ecosystem — npm has millions of packages
  • Fast startup — good for CLIs and serverless
  • Built-in tooling — server, file system, streams, without extra installs

Where Node runs well:

  • REST and GraphQL APIs
  • Real-time apps (chat, collaboration)
  • Build tools and bundlers
  • CLIs and scripts
  • Serverless functions
  • Desktop apps (Electron)

Where Node struggles:

  • CPU-intensive work — one thread, one core
  • Heavy numeric computing — Python or C++ win
  • Video encoding, image processing — native tools win

For CPU-bound work, use worker_threads or spawn a separate process.


b – File system and modules

The fs module is your interface to the disk. It reads, writes, renames, and deletes files and directories — synchronously or asynchronously.

The three API styles:

StyleExampleWhen
Callbackfs.readFile(p, cb)Legacy
Syncfs.readFileSync(p)Startup, scripts
Promisefs.promises.readFile(p)Modern async

Reading a file:

import { readFile } from 'fs/promises';

const content = await readFile('data.txt', 'utf8');
console.log(content);

fs/promises gives you Promise-based methods. Always prefer this over callbacks in modern code.

Writing a file:

import { writeFile } from 'fs/promises';

await writeFile('output.txt', 'Hello, Node!');

writeFile overwrites by default. Use { flag: 'a' } to append.

Sync versions for startup code:

import { readFileSync } from 'fs';

const config = JSON.parse(readFileSync('config.json', 'utf8'));

Sync is fine for reading config at startup — the app isn’t serving anyone yet. Avoid sync in request handlers.

Directory operations:

import { readdir, mkdir, rm } from 'fs/promises';

const files = await readdir('./src');
await mkdir('./build', { recursive: true });
await rm('./tmp', { recursive: true, force: true });

readdir lists a directory. mkdir creates. rm deletes. Both mkdir and rm need { recursive: true } for nested paths.

Checking existence:

import { access } from 'fs/promises';

try {
  await access('file.txt');
  console.log('exists');
} catch {
  console.log('missing');
}

access throws if the file doesn’t exist — cleaner than existsSync, which has race conditions.

Watching files:

import { watch } from 'fs';

watch('./src', { recursive: true }, (event, filename) => {
  console.log(event, filename);
});

Fires when files change. Used by dev servers and build tools.

The path module:

Path concatenation is deceptively tricky across operating systems. Use path, not string concatenation.

import path from 'path';

path.join('src', 'utils', 'file.js');   // 'src/utils/file.js'
path.resolve('./src');                   // '/absolute/path/to/src'
path.basename('/a/b/c.txt');             // 'c.txt'
path.dirname('/a/b/c.txt');              // '/a/b'
path.extname('file.txt');                // '.txt'
path.parse('/a/b/c.txt');                // { root, dir, base, ext, name }

join combines with the right separator for the OS. resolve produces an absolute path. The others extract pieces.

Paths in ESM:

import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const configPath = join(__dirname, 'config.json');

import.meta.url is a file URL. fileURLToPath converts it to a normal path. This is the ESM equivalent of __dirname.

Creating your own modules:

// math.js
export function add(a, b) { return a + b; }
export const PI = 3.14159;

// main.js
import { add, PI } from './math.js';

Node supports ESM modules natively. Bare imports ('react') resolve from node_modules; relative imports ('./math.js') resolve from the file.

npm — Node’s package manager:

npm init -y
npm install express
npm install --save-dev jest
npm run test

npm installs packages into node_modules and records them in package.json. --save-dev marks a package as a development-only dependency.

The package.json file:

{
  "name": "my-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "test": "node --test"
  },
  "dependencies": {
    "express": "^4.18.0"
  },
  "devDependencies": {
    "jest": "^29.0.0"
  }
}
  • type: "module" — enables ESM
  • scripts — custom commands run with npm run
  • dependencies — needed at runtime
  • devDependencies — needed only for development

Common fs operations:

OperationAPI
Read filereadFile
Write filewriteFile
AppendwriteFile(f, data, { flag: 'a' })
Deleterm / unlink
Renamerename
List dirreaddir
Create dirmkdir
Delete dirrmdir / rm
Statstat
Watchwatch
CopycopyFile

Streaming large files:

Reading a 5GB file into memory is a bad idea. Use streams:

import { createReadStream } from 'fs';

const stream = createReadStream('big.log', 'utf8');

stream.on('data', chunk => process(chunk));
stream.on('end', () => console.log('done'));

Streams process data in chunks — memory stays low regardless of file size.


c – HTTP servers and the event loop

Node’s http module lets you build web servers without a framework. It’s minimal, but understanding it means understanding how frameworks like Express work underneath.

A basic server:

import { createServer } from 'http';

const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, world!');
});

server.listen(3000, () => {
  console.log('Listening on http://localhost:3000');
});

createServer takes a function that runs on every request. req is the request, res is the response.

Routing by hand:

const server = createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.end('Home');
  } else if (req.method === 'GET' && req.url === '/users') {
    res.end('Users');
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

Frameworks exist to make this cleaner — but the primitive is just a function that inspects method and url.

Sending JSON:

res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: [] }));

Always set the Content-Type header. The browser won’t parse the response as JSON otherwise.

Reading the request body:

let body = '';

req.on('data', chunk => (body += chunk));
req.on('end', () => {
  const data = JSON.parse(body);
  res.end('received');
});

The body arrives as a stream. Collect the chunks, then parse. For large bodies or file uploads, stream to disk instead of buffering.

The events module:

Node is built on EventEmitter — the observer pattern from JavaScript 64.

import { EventEmitter } from 'events';

const emitter = new EventEmitter();

emitter.on('greet', name => console.log(`Hello, ${name}`));

emitter.emit('greet', 'Alice');

Everything that streams or fires events — servers, sockets, files — is an EventEmitter. Understanding it is understanding Node.

The event loop in Node:

┌──────────────────────────────────────────────┐
│             Node.js Event Loop               │
│                                              │
│  1. Timers (setTimeout, setInterval)         │
│  2. Pending callbacks (I/O)                  │
│  3. Idle / prepare (internal)                │
│  4. Poll (incoming I/O)                      │
│  5. Check (setImmediate)                     │
│  6. Close callbacks                          │
│                                              │
│  Between each phase:                         │
│    - process.nextTick queue                  │
│    - Promise microtasks                      │
│                                              │
└──────────────────────────────────────────────┘

The phases cycle. Non-blocking operations schedule callbacks. When the loop has nothing to do, it blocks on the poll phase until an event arrives.

process.nextTick vs setImmediate:

process.nextTick(() => console.log('nextTick'));
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));

// Output:
// [ nextTick ]
// [ promise ]
// [ immediate ]
  • process.nextTick — runs before the loop continues, after the current operation
  • Promise .then — microtask, same idea
  • setImmediate — runs in the check phase of the next loop iteration

Why this matters: Never block the event loop. A synchronous loop over a million items stops everything — other requests, timers, and callbacks wait.

Blocking vs non-blocking:

// ❌ Blocks the event loop
const data = fs.readFileSync('big.json');

// ✅ Non-blocking
const data = await fs.promises.readFile('big.json');

The sync version works — but every other request during that read is stuck. In a server, that’s a denial-of-service.

Environment variables:

const PORT = process.env.PORT || 3000;
const DB_URL = process.env.DATABASE_URL;

Config comes from the environment, not hardcoded values. Use dotenv to load a .env file in development.

Graceful shutdown:

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Shut down cleanly');
    process.exit(0);
  });
});

Catch termination signals, close connections, then exit. Otherwise in-flight requests fail.

A minimal Express-like server:

import { createServer } from 'http';

const routes = new Map();

function get(path, handler) {
  routes.set(`GET ${path}`, handler);
}

get('/users', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify([{ id: 1, name: 'Alice' }]));
});

const server = createServer((req, res) => {
  const key = `${req.method} ${req.url}`;
  const handler = routes.get(key);
  if (handler) handler(req, res);
  else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(3000);

Express and Fastify build on this. Knowing the primitive helps when debugging, even if you use a framework.


Complete Example Session

// ============================================
// PART 1: PROCESS INFO
// ============================================

console.log(process.version);
// [ 'v20.10.0' ]

console.log(process.platform);
// [ 'linux' ]

console.log(process.cwd());
// [ '/home/user/project' ]

// ============================================
// PART 2: READ A FILE
// ============================================

import { readFile } from 'fs/promises';

const content = await readFile('data.txt', 'utf8');
console.log(content);
// [ file contents ]

// ============================================
// PART 3: WRITE A FILE
// ============================================

import { writeFile } from 'fs/promises';

await writeFile('output.txt', 'Hello, Node!');

// ============================================
// PART 4: LIST DIRECTORY
// ============================================

import { readdir } from 'fs/promises';

const files = await readdir('./src');
console.log(files);
// [ [ 'index.js', 'utils.js', 'api.js' ] ]

// ============================================
// PART 5: PATH UTILITIES
// ============================================

import path from 'path';

console.log(path.join('src', 'utils.js'));
// [ 'src/utils.js' ]

console.log(path.basename('/a/b/c.txt'));
// [ 'c.txt' ]

console.log(path.extname('file.txt'));
// [ '.txt' ]

// ============================================
// PART 6: ESM __DIRNAME
// ============================================

import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

console.log(join(__dirname, 'config.json'));
// [ '/home/user/project/config.json' ]

// ============================================
// PART 7: BASIC HTTP SERVER
// ============================================

import { createServer } from 'http';

const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, world!');
});

server.listen(3000, () => {
  console.log('http://localhost:3000');
});

// ============================================
// PART 8: ROUTING
// ============================================

const server2 = createServer((req, res) => {
  if (req.url === '/') {
    res.end('Home');
  } else if (req.url === '/users') {
    res.end(JSON.stringify([{ id: 1 }]));
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

// ============================================
// PART 9: EVENT EMITTER
// ============================================

import { EventEmitter } from 'events';

const emitter = new EventEmitter();

emitter.on('greet', name => console.log(`Hello, ${name}`));
emitter.emit('greet', 'Alice');
// [ Hello, Alice ]

// ============================================
// PART 10: PROCESS.NEXTTICK VS SETIMMEDIATE
// ============================================

process.nextTick(() => console.log('nextTick'));
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));

// Output:
// [ nextTick ]
// [ promise ]
// [ immediate ]

// ============================================
// PART 11: ENV VARIABLES
// ============================================

const PORT = process.env.PORT || 3000;
console.log(`Starting on port ${PORT}`);
// [ Starting on port 3000 ]

// ============================================
// PART 12: GRACEFUL SHUTDOWN
// ============================================

process.on('SIGTERM', () => {
  console.log('Shutting down');
  server.close(() => process.exit(0));
});

// ============================================
// PART 13: READING JSON CONFIG
// ============================================

import { readFileSync } from 'fs';

const config = JSON.parse(readFileSync('config.json', 'utf8'));
console.log(config.port);
// [ 3000 ]

// ============================================
// PART 14: STREAMING A FILE
// ============================================

import { createReadStream } from 'fs';

const stream = createReadStream('big.log', 'utf8');

stream.on('data', chunk => {
  // process each chunk
});
stream.on('end', () => console.log('done'));

// ============================================
// PART 15: SIMPLE CLI
// ============================================

// $ node greet.js Alice
const name = process.argv[2] || 'World';
console.log(`Hello, ${name}`);
// [ Hello, Alice ]

// ============================================
// PART 16: SPAWN A PROCESS
// ============================================

import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

const { stdout } = await execAsync('ls -la');
console.log(stdout);

Quick Reference

Global Objects

ObjectPurpose
processRuntime info, env, exit
globalGlobal object
globalThisStandard global
BufferBinary data
__dirnameFile’s directory (CJS)
__filenameFull path (CJS)
import.meta.urlFile URL (ESM)

process Properties

PropertyMeaning
process.versionNode version
process.platformOS
process.cwd()Current dir
process.envEnvironment vars
process.argvCLI arguments
process.pidProcess ID
process.exit(code)Exit
process.nextTick(fn)Run before next phase

fs/promises API

MethodPurpose
readFile(p, enc)Read file
writeFile(p, data)Write file
appendFile(p, data)Append
readdir(dir)List dir
mkdir(p, opts)Create dir
rm(p, opts)Delete
rename(a, b)Rename
stat(p)File info
copyFile(a, b)Copy
access(p)Check exists

path Module

MethodReturns
path.join(...)Joined path
path.resolve(...)Absolute path
path.basename(p)Filename
path.dirname(p)Directory
path.extname(p)Extension
path.parse(p)Parts object

http Module

MethodPurpose
createServer(handler)Create server
server.listen(port, cb)Start
server.close(cb)Stop
res.writeHead(status, headers)Status + headers
res.write(data)Write chunk
res.end(data)Finish

Event Loop Phases

PhaseHandles
TimerssetTimeout, setInterval
PendingI/O callbacks
PollNew I/O
ChecksetImmediate
CloseClose callbacks

Microtask Priority

SourcePriority
process.nextTickHighest
Promise .thenHigh
setImmediateLoop phase
setTimeoutLoop phase

CLI Arguments

CodeMeaning
process.argv[0]Node path
process.argv[1]Script path
process.argv[2..]User args

Best Practices

Do This:

// Use fs/promises for async work
import { readFile } from 'fs/promises';

// Use path.join for cross-platform paths
const p = path.join('src', 'file.js');

// Use import.meta.url in ESM
const __dirname = dirname(fileURLToPath(import.meta.url));

// Use env vars for config
const PORT = process.env.PORT || 3000;

// Handle SIGTERM for graceful shutdown
process.on('SIGTERM', () => server.close(() => process.exit(0)));

// Use streams for large files
createReadStream('big.log').pipe(dest);

// Prefer async over sync in servers
const data = await readFile('data.json');

// Set Content-Type headers
res.writeHead(200, { 'Content-Type': 'application/json' });

Don’t Do This:

// Don't block the event loop
const data = readFileSync('big.json');           // ❌ in server

// Don't concatenate paths with strings
const p = 'src' + '/' + 'file.js';               // ❌ cross-platform

// Don't hardcode config
const PORT = 3000;                               // ⚠️  use env

// Don't forget Content-Type
res.end(JSON.stringify(data));                   // ❌ client can't parse

// Don't ignore SIGTERM
// (in-flight requests fail)                     // ❌

// Don't read huge files into memory
const big = await readFile('5GB.log');           // ❌ use streams

// Don't use __dirname in ESM
console.log(__dirname);                          // ❌ ReferenceError

// Don't block startup with unnecessary sync calls
readFileSync('big.json');                        // ⚠️  slow startup

Common Pitfalls

PitfallProblemSolution
__dirname in ESMReferenceErrorUse import.meta.url
Sync I/O in serverBlocks event loopUse async
Path string concatBreaks on Windowspath.join
Missing Content-TypeClient can’t parseSet header
No SIGTERM handlerRequests fail on restartHandle signal
Read whole fileMemory blowupStream
require in ESMSyntaxErrorUse import
Forgot type: moduleimport failsSet in package.json
Blocking CPU workEverything waitsworker_threads

Real-World Examples

1. Read a file

import { readFile } from 'fs/promises';
const text = await readFile('data.txt', 'utf8');

The modern async read.

2. Write a file

import { writeFile } from 'fs/promises';
await writeFile('log.txt', 'entry\n', { flag: 'a' });

Append with { flag: 'a' }.

3. List a directory

import { readdir } from 'fs/promises';
const files = await readdir('./src');

Returns filenames as an array.

4. Join paths

import path from 'path';
const file = path.join('src', 'utils', 'helper.js');

Cross-platform path building.

5. ESM __dirname

import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));

The ESM equivalent of the CommonJS global.

6. Basic HTTP server

import { createServer } from 'http';
createServer((req, res) => res.end('Hi')).listen(3000);

The smallest useful server.

7. JSON endpoint

res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));

Set the header, serialize the object.

8. Event emitter

const emitter = new EventEmitter();
emitter.on('data', handler);
emitter.emit('data', payload);

Node’s core communication primitive.

9. Read env var

const port = process.env.PORT || 3000;

Config from the environment.

10. Graceful shutdown

process.on('SIGTERM', () => {
  server.close(() => process.exit(0));
});

Close cleanly when the OS asks you to stop.


Visual: Node’s Architecture

┌──────────────────────────────────────────────┐
│             Your JavaScript                  │
│                                              │
│  app.js, routes, handlers                    │
│                                              │
└─────────────────┬────────────────────────────┘
                  │
                  ▼
┌──────────────────────────────────────────────┐
│             Node.js APIs                     │
│                                              │
│  fs, http, path, events, streams             │
│                                              │
└─────────────────┬────────────────────────────┘
                  │
                  ▼
┌──────────────────────────────────────────────┐
│  libuv  │  V8  │  C++ bindings               │
│                                              │
│  Event loop, thread pool, file system,       │
│  networking, JavaScript engine               │
│                                              │
└──────────────────────────────────────────────┘

Visual: The Event Loop in Node

┌──────────────────────────────────────────────┐
│  ┌───────────┐                               │
│  │  timers   │  setTimeout, setInterval      │
│  └─────┬─────┘                               │
│        ▼                                     │
│  ┌───────────┐                               │
│  │  pending  │  I/O callbacks                │
│  └─────┬─────┘                               │
│        ▼                                     │
│  ┌───────────┐                               │
│  │   poll    │  new I/O                      │
│  └─────┬─────┘                               │
│        ▼                                     │
│  ┌───────────┐                               │
│  │   check   │  setImmediate                 │
│  └─────┬─────┘                               │
│        ▼                                     │
│  ┌───────────┐                               │
│  │   close   │  cleanup callbacks            │
│  └─────┬─────┘                               │
│        │                                     │
│        └──────► repeat                       │
│                                              │
│  Between phases: nextTick and microtasks     │
│                                              │
└──────────────────────────────────────────────┘

Visual: Sync vs Async I/O

┌──────────────────────────────────────────────┐
│  Sync                                        │
│                                              │
│  Request 1 ──► readFileSync ──► blocks       │
│  Request 2 ──► waits                         │
│  Request 3 ──► waits                         │
│                                              │
│  One at a time, slow                         │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Async                                       │
│                                              │
│  Request 1 ──► readFile ──► schedules        │
│  Request 2 ──► readFile ──► schedules        │
│  Request 3 ──► readFile ──► schedules        │
│                                              │
│  All in flight at once, fast                 │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptModuleExample
Runtimeprocessprocess.version
Filesfs/promisesreadFile, writeFile
Pathspathpath.join
HTTPhttpcreateServer
EventseventsEventEmitter
StreamsstreamcreateReadStream
URLurlfileURLToPath
Child processeschild_processexec
Configprocess.envPORT
CLI argsprocess.argvargv[2]
Graceful exitprocess.onSIGTERM
ESM dirnameimport.meta.urlfileURLToPath
Package managernpmnpm install
Project configpackage.jsonscripts, deps

Key takeaways:

  • Node.js is a JavaScript runtime — JavaScript outside the browser
  • It provides built-in modules: fs, path, http, events, stream, crypto
  • process replaces window — it exposes env vars, argv, and runtime info
  • fs/promises is the modern way to work with files — always prefer async over sync in servers
  • Use path.join for cross-platform paths, never string concatenation
  • In ESM, derive __dirname from import.meta.url and fileURLToPath
  • http.createServer is the primitive under Express, Fastify, and every Node framework
  • EventEmitter is Node’s core communication pattern — servers, sockets, and streams all use it
  • The event loop runs phases in order: timers, pending, poll, check, close
  • process.nextTick and Promises run before the next phase — highest priority
  • Never block the event loop — sync I/O and CPU loops stall everything
  • Use streams for large files and network data — memory stays low
  • Configure via process.env, not hardcoded values
  • Handle SIGTERM for graceful shutdown — close servers, finish requests, exit clean
  • npm installs packages into node_modules and records them in package.json
  • Set "type": "module" to use ESM natively

Remember: Node.js is JavaScript with an operating system underneath. It’s fast at I/O because it never blocks, and simple because it has one thread. Learn fs/promises, path, http, and events and you can build servers, CLIs, and scripts without a framework. Understand the event loop and you’ll know why sync code is dangerous. Configure with env vars, shut down gracefully, and stream large data. Master Node basics, and you can write JavaScript for servers with the same language you use in the browser.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!