SlideShare a Scribd company logo
Structure and architecture, API, security, asynchronity, routing,
middlewares, data access, memory, cpu, state management, etc.
Timur Shemsedinov
8-9 NOV ‘19 KIEV, UKRAINE
Node.js
Antipatterns
JS FEST PROFESSIONAL JS CONFERENCE
Node.js Antipatterns Classification
- Structure and arch.
- Initialization
- Dependency issues
- Application state
- Middlewares
- Context isolation
- Security issues
- Asynchronity issues
- Blocking operations
- Memory leaks
- Databases and ORM
- Error handling
No layers, everything mixed:
- Configuration and Dependency management
- Network protocols related code (http, tcp, tls…)
- Request parsing, Cookies, Sessions
- Logging, Routing, Business-logic
- I/O: fs, Database queries
- Generating responses and error generation
- Templating, etc.
github.com/HowProgrammingWorks/AbstractionLayers
Mixed Layers
router.get('/user/:id', (req, res, next) => {
const id = parseInt(req.params.id);
const query = 'SELECT * FROM users WHERE id = $1';
pool.query(query, [id], (err, data) => {
if (err) throw err;
res.status(200).json(data.rows);
next();
});
});
What do we want?
async (arg1, arg2, arg3) => {
const data1 = await getData(arg1);
if (!data1) throw new Error('Message');
const [data2, data3] = await Promise.all(
[getData(arg2), getData(arg3)]
);
return await processData(data1, data2, data3);
}
github.com/HowProgrammingWorks/API
Transport agnostic, Framework agnostic
Middlewares
Middlewares is an extremely bad idea for low
coupling and high cohesion
Middlewares changes:
- Socket state
- Db connection state
- Server state
Don’t use globals to pass state
let groupName;
app.use((req, res, next) => {
groupName = 'idiots'; next();
});
app.get('/user', (req, res) => {
if (res.groupName === 'idiots') {
res.end('I know you!');
}
});
Don’t mixins to req and res
app.use((req, res, next) => {
res.groupName = 'idiots';
next();
});
app.get('/user', (req, res) => {
if (res.groupName === 'idiots') {
res.end('I know you!');
}
});
Don’t mixins locals to res
app.use((req, res, next) => {
res.locals.groupName = 'idiots';
next();
});
app.get('/user', (req, res) => {
if (res.locals.groupName === 'idiots') {
res.end('I know you!');
}
});
Don’t mixins methods
app.get('/user/:id', (req, res, next) => {
req.auth = (login, password) => { /* auth */ };
next();
});
app.get('/user/:id', (req, res) => {
if (req.auth(req.params.id, '111')) {
res.end('I know you!');
}
});
Don’t require in middleware / handler
app.get((req, res, next) => {
req.db = new require('pg').Client();
req.db.connect();
next();
});
app.get('/user/:id', (req, res) => {
req.db.query('SELECT * from USERS', (e, r) => {
});
});
Don’t connect db in handlers
app.get((req, res, next) => {
req.db = new Pool(config);
next();
});
app.get('/user/:id', (req, res) => {
req.db.query('SELECT * from USERS', (e, r) => {
});
});
Don’t loose connection on error
const db = new Pool(config);
app.get('/user/:id', (req, res) => {
req.db.query('SELECT * from USERS', (err, r) => {
if (err) throw err;
// Prepare data to reply client
});
});
Why execution context isolation?
- Errors, crashes
- Memory leaks and other resources
- Application: data, database connections
- File system and root directory
- OS environment, PID, IPC
- OS Security: users, groups
- Networking: socket descriptors, ports, hosts
Execution isolation levels
- Hardware: servers, networks
- Virtual machine (hypervisor)
- Container (docker)
- Process (node)
- Thread (worker_threads)
- Sandbox (vm.createContext, vm.Script)
- Software context (object, closure)
Dependency issues
- Almost all we need is in
- JavaScript and in Node.js API (just check)
- Adding dependencies from NPM tell yourself:
- It’s my responsibility
- authors give no warranties,
- it’s just a good will if they help and support
- and I will support fork
Security issues
- Malicious modules from NPM
- Path traversal
- Injections: SQL, NoSQL, Blind, JavaScript
- Sandbox escaping (vm)
- Buffer vulnerabilities
- Regular expressions
Path traversal
const serveFile = fileName => {
const filePath = path.join(STATIC_PATH, fileName);
return fs.createReadStream(filePath);
};
http.createServer((req, res) => {
const url = decodeURI(req.url);
serveFile(url).pipe(res);
}).listen(8000);
curl -v http://127.0.0.1:8000/%2e%2e/1-traversal.js
Path traversal fixed
const serveFile = fileName => {
const filePath = path.join(STATIC_PATH, fileName);
if (!filePath.startsWith(STATIC_PATH)) {
throw new Error(`Access denied: ${name}`);
}
return fs.createReadStream(filePath);
};
http.createServer((req, res) => {
const url = decodeURI(req.url);
serveFile(url).pipe(res);
}).listen(8000);
Asynchronity issues
- Callback hell and promise hell
- Ignoring errors in callbacks
- Ignoring errors in promises
- Throwing errors and releasing resources
- Race conditions and deadlocks
- Queuing theory: need to limit concurrency
Callback hell
select(id, (err, data) => {
check(data, (err, valid) => {
convert(data, (err, res) => {
сache(id, res, err => {
send(data);
});
});
});
});
Flat, decomposed, named
const saved = (err, data) =>
err ? throw err : send(data1);
const converted = (err, res) =>
err ? throw err : сache(res, saved);
const checked = (err, valid) =>
err ? throw err : convert(data, converted);
const selected = (err, data) =>
err ? throw err : check(data, checked);
select(id, selected);
Promises sequential execution
Promise.resolve(id)
.then(select)
.catch(...)
.then(check)
.catch(...)
.then(convert)
.catch(...)
.then(cache)
.catch(...)
.then(send)
.catch(...);
Promise hell
select(id).then(data => {
check(data).then(valid => {
if (valid) {
Promise.all([
convert(data),
cache(data)
]).then(send);
}
});
});
Compose errback
compose
(send, cache, convert, check, select)
(id);
pipe
(select, check, convert, cache, send)
(id);
Compose AsyncFunction
await compose
(send, cache, convert, check, select)
(id);
await pipe
(select, check, convert, cache, send)
(id);
Universal composition: metasync
metasync
([select, check, [[convert, cache]], send])
(id);
// In arguments: Function | Errback | AsyncFunction
// Process: Parallel | Sequential
// Functionality: Cancelable | Timeout | Throttle
// Result: Thenable | Promise | Errback | EventEmitter
Limit execution concurrency
const queue = metasync.queue(3)
.wait(2000)
.timeout(5000)
.throttle(100, 1000)
.process((item, cb) => cb(err, result))
.success(item => {})
.failure(item => {})
.done(() => {})
.drain(() => {});
Asynchronous programming
27 lectures: https://habr.com/ru/post/452974/
Race condition demo
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
async move(dx, dy) {
this.x = await add(this.x, dx);
this.y = await add(this.y, dy);
}
}
Race condition demo
const random = (min, max) => Math
.floor(Math.random() * (max - min + 1)) + min;
const add = (x, dx) => new Promise(resolve => {
const timeout = random(20, 100);
setTimeout(() => resolve(x + dx), timeout);
});
Race condition demo
const p1 = new Point(10, 10);
console.log(p1);
p1.move(5, 5);
p1.move(6, 6);
p1.move(7, 7);
p1.move(8, 8);
setTimeout(() => {
console.log(p1);
}, 1000);
Race condition demo
Initial
Point { x: 10, y: 10 }
Expected
Point { x: 36, y: 36 }
Actual
Point { x: 18, y: 25 }
Do we need parallel primitives?
- Semaphore: Binary and Counting semaphore
- Condition variable
- Spinlock
- Mutex, Timed mutex, Shared mutex
- Recursive mutex
- Monitor
- Barrier
Do we need parallel primitives?
https://github.com
/HowProgrammingWorks
/Semaphore
/Mutex
/RaceCondition
/Deadlock
https://youtu.be/JNLrITevhRI
Simple Resource Locking
class Lock {
constructor() {
this.active = false;
this.queue = [];
}
leave() {
if (!this.active) return;
this.active = false;
const next = this
.queue.pop();
if (next) next();
}
}
enter() {
return new Promise(resolve => {
const start = () => {
this.active = true;
resolve();
};
if (!this.active) {
start();
return;
}
this.queue.push(start);
});
}
Race condition demo
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
this.lock = new Lock();
}
async move(dx, dy) {
await this.lock.enter();
this.x = await add(this.x, dx);
this.y = await add(this.y, dy);
this.lock.leave();
}
}
Web Locks API
locks.request('resource', opt, async lock => {
if (lock) {
// critical section for `resource`
// will be released after return
}
});
https://wicg.github.io/web-locks/
Web Locks: await
(async () => {
await something();
await locks.request('resource', async lock => {
// critical section for `resource`
});
await somethingElse();
})();
Web Locks: Promise and Thenable
locks.request('resource', lock => new Promise(
(resolve, reject) => {
// you can store or pass
// resolve and reject here
}
));
locks.request('resource', lock => ({
then((resolve, reject) => {
// critical section for `resource`
// you can call resolve and reject here
})
}));
Web Locks: Abort
const controller = new AbortController();
setTimeout(() => controller.abort(), 200);
const { signal } = controller;
locks.request('resource', { signal }, async lock => {
// lock is held
}).catch(err => {
// err is AbortError
});
Web Locks for Node.js
github.com/nodejs/node/issues/22702
Open
github.com/nodejs/node/pull/22719
Closed
Don’t use blocking operations
- Sync calls like fs.readFileSync
- Console output like console.log
- Remember that require is synchronous
- Long loops (including for..of and for await)
- Serialization: JSON.parse, JSON.stringify
- Iteration: loops, Array.prototype.map, etc.
- CPU-intensive: zlib, crypto
Loop: for await of is blocking
(async () => {
let ticks = 0;
const timer = setInterval(() => ticks++, 10);
const numbers = new Array(1000000).fill(1);
let i = 0;
for await (const number of numbers) i++;
clearInterval(timer);
console.dir({ i, ticks });
})();
// { i: 1000, ticks: 0 }
AsyncArray (short version)
class AsyncArray extends Array {
[Symbol.asyncIterator]() {
let i = 0;
return {
next: () => new Promise(resolve => {
setTimeout(() => resolve({
value: this[i], done: i++ === this.length
}), 0);
})
};
}
} // github.com/HowProgrammingWorks/NonBlocking
Loop: for await of + AsyncArray
(async () => {
let ticks = 0;
const timer = setInterval(() => ticks++, 10);
const numbers = new AsyncArray(10000000).fill(1);
let i = 0;
for await (const number of numbers) i++;
clearInterval(timer);
console.dir({ i, ticks });
})();
// { i: 10000, ticks: 1163 }
https://github.com/HowProgrammingWorks/NonBlocking
Memory leaks
- References
- Global variables
- Mixins to built-in Classes
- Singletons, Caches
- Closures / Function contexts
- Recursive closures
- Require in the middle of code
- Functions in loops
Memory leaks
- OS and Language Objects
- Descriptors: files, sockets...
- Timers: setTimeout, setInterval
- Events / Subscription / Promises
- EventEmitter
- Callbacks, Not resolved promises
github.com/HowProgrammingWorks/MemoryLeaks
ORM is an obvious antipattern
- ORM tool for DBMS performance degradation
- SQL is much more easier
- ORM hides DBMS functionality
- Additional security vulnerabilities
- Broken OOP principles
github.com/HowProgrammingWorks/Databases
Error handling antipatterns
- Ignoring callback errors
- Callback returns multiple times
- Unhandled stream errors
- Unhandled rejection
- Promise resolves multiple times
github.com/HowProgrammingWorks/PromiseError
Don’t ignore callback errors
const cbFunc = (arg1, arg2, arg3, callback) => {
readData(arg1, arg2, (error1, data1) => {
const arg4 = data1.field;
checkData(arg3, arg4, (error2, data2) => {
if (error2) callback(new Error('msg'));
callback(null, { data1, data2 });
});
});
};
Don’t ignore stream errors
const sharp = require('sharp');
http.get(url, src => {
const dest = fs.createWriteStream(fileName);
const resize = sharp().resize(300, 300);
const free = () => src.destroyed || src.destroy();
resize.on('error', free);
destination.on('error', free);
src.pipe(resize).pipe(dest);
});
Use FP style, like Pump
const pump = require('pump');
const sharp = require('sharp');
http.get(url, src => {
const dest = fs.createWriteStream(fileName);
const resize = sharp().resize(300, 300);
const fail = err => throw err;
pump(src, resize, dest, fail);
});
https://github.com/tshemsedinov
https://www.youtube.com/TimurShemsedinov
timur.shemsedinov@gmail.com
Thanks! Questions?

More Related Content

PDF
Node.js middleware: Never again!
PDF
Asynchronous programming with java script and node.js
PDF
Node.js in 2020
PDF
Race-conditions-web-locks-and-shared-memory
PDF
Asynchronous programming and mutlithreading
PDF
How are Race Conditions in single threaded JavaScript possible?
PDF
Node.js in 2020 - part 3
PDF
Private cloud without vendor lock // Serverless
Node.js middleware: Never again!
Asynchronous programming with java script and node.js
Node.js in 2020
Race-conditions-web-locks-and-shared-memory
Asynchronous programming and mutlithreading
How are Race Conditions in single threaded JavaScript possible?
Node.js in 2020 - part 3
Private cloud without vendor lock // Serverless

What's hot (20)

PDF
Metarhia KievJS 22-Feb-2018
PDF
Web Locks API
PDF
Patterns and antipatterns
PDF
Node.js in 2020 - part 1
PDF
JavaScript в браузере: Web API (часть 1)
PDF
Новое в JavaScript: ES.Next, ECMAScript 2020, ES11, ES10, ES9, ES8, ES7, ES6,...
PDF
Serverless Clouds (FaaS) and request context isolation in Node.js
PDF
Node.js in 2020 - part 2
PDF
Programming Languages: comparison, history, future
PDF
Введение в SQL
PDF
How to keep control and safety in the clouds
PDF
Prototype programming in JavaScript
PDF
"let ECMAScript = 6"
PDF
Lập trình Python cơ bản
PPTX
Academy PRO: ES2015
PDF
Flashback, el primer malware masivo de sistemas Mac
PDF
The Ring programming language version 1.6 book - Part 71 of 189
PDF
Sycl 1.2 Reference Card
PPTX
ES6 Overview
PPT
Full-Stack JavaScript with Node.js
Metarhia KievJS 22-Feb-2018
Web Locks API
Patterns and antipatterns
Node.js in 2020 - part 1
JavaScript в браузере: Web API (часть 1)
Новое в JavaScript: ES.Next, ECMAScript 2020, ES11, ES10, ES9, ES8, ES7, ES6,...
Serverless Clouds (FaaS) and request context isolation in Node.js
Node.js in 2020 - part 2
Programming Languages: comparison, history, future
Введение в SQL
How to keep control and safety in the clouds
Prototype programming in JavaScript
"let ECMAScript = 6"
Lập trình Python cơ bản
Academy PRO: ES2015
Flashback, el primer malware masivo de sistemas Mac
The Ring programming language version 1.6 book - Part 71 of 189
Sycl 1.2 Reference Card
ES6 Overview
Full-Stack JavaScript with Node.js
Ad

Similar to JS Fest 2019 Node.js Antipatterns (20)

PDF
MongoDB World 2019: Life In Stitch-es
PDF
node.js practical guide to serverside javascript
KEY
Node.js - Best practices
PPTX
Nantes Jug - Java 7
PDF
Node Powered Mobile
PPTX
Developing web-apps like it's 2013
PDF
NoSQL and JavaScript: a Love Story
PDF
Secure .NET programming
PPTX
Anti patterns
PDF
Node.js - A Quick Tour
PPTX
PDF
Developing Applications with MySQL and Java for beginners
PPT
JS everywhere 2011
PDF
DEF CON 23 - amit ashbel and maty siman - game of hacks
PDF
async/await in Swift
PDF
Reduxing like a pro
PDF
Think Async: Asynchronous Patterns in NodeJS
PDF
Future Decoded - Node.js per sviluppatori .NET
PPTX
Tools for Making Machine Learning more Reactive
PDF
Painless Persistence with Realm
MongoDB World 2019: Life In Stitch-es
node.js practical guide to serverside javascript
Node.js - Best practices
Nantes Jug - Java 7
Node Powered Mobile
Developing web-apps like it's 2013
NoSQL and JavaScript: a Love Story
Secure .NET programming
Anti patterns
Node.js - A Quick Tour
Developing Applications with MySQL and Java for beginners
JS everywhere 2011
DEF CON 23 - amit ashbel and maty siman - game of hacks
async/await in Swift
Reduxing like a pro
Think Async: Asynchronous Patterns in NodeJS
Future Decoded - Node.js per sviluppatori .NET
Tools for Making Machine Learning more Reactive
Painless Persistence with Realm
Ad

More from Timur Shemsedinov (15)

PDF
How to use Chat GPT in JavaScript optimizations for Node.js
PDF
IT Revolution in 2023-2024: AI, GPT, business transformation, future professi...
PDF
Multithreading in Node.js and JavaScript
PDF
Node.js threads for I/O-bound tasks
PDF
Node.js Меньше сложности, больше надежности Holy.js 2021
PDF
Rethinking low-code
PDF
Hat full of developers
PDF
FwDays 2021: Metarhia Technology Stack for Node.js
PDF
Node.js for enterprise - JS Conference
PDF
Node.js for enterprise 2021 - JavaScript Fwdays 3
PDF
Node.js in 2021
PDF
Information system structure and architecture
PDF
Базы данных в 2020
PDF
Почему хорошее ИТ-образование невостребовано рыночком
PDF
Node.js security
How to use Chat GPT in JavaScript optimizations for Node.js
IT Revolution in 2023-2024: AI, GPT, business transformation, future professi...
Multithreading in Node.js and JavaScript
Node.js threads for I/O-bound tasks
Node.js Меньше сложности, больше надежности Holy.js 2021
Rethinking low-code
Hat full of developers
FwDays 2021: Metarhia Technology Stack for Node.js
Node.js for enterprise - JS Conference
Node.js for enterprise 2021 - JavaScript Fwdays 3
Node.js in 2021
Information system structure and architecture
Базы данных в 2020
Почему хорошее ИТ-образование невостребовано рыночком
Node.js security

Recently uploaded (20)

PPTX
Odoo POS Development Services by CandidRoot Solutions
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Understanding Forklifts - TECH EHS Solution
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
history of c programming in notes for students .pptx
PPTX
L1 - Introduction to python Backend.pptx
PPT
Introduction Database Management System for Course Database
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPTX
ai tools demonstartion for schools and inter college
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
AI in Product Development-omnex systems
PPTX
Introduction to Artificial Intelligence
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Odoo POS Development Services by CandidRoot Solutions
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Understanding Forklifts - TECH EHS Solution
How to Choose the Right IT Partner for Your Business in Malaysia
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Odoo Companies in India – Driving Business Transformation.pdf
Design an Analysis of Algorithms I-SECS-1021-03
Adobe Illustrator 28.6 Crack My Vision of Vector Design
history of c programming in notes for students .pptx
L1 - Introduction to python Backend.pptx
Introduction Database Management System for Course Database
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
ai tools demonstartion for schools and inter college
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
How to Migrate SBCGlobal Email to Yahoo Easily
AI in Product Development-omnex systems
Introduction to Artificial Intelligence
VVF-Customer-Presentation2025-Ver1.9.pptx
Wondershare Filmora 15 Crack With Activation Key [2025
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool

JS Fest 2019 Node.js Antipatterns

  • 1. Structure and architecture, API, security, asynchronity, routing, middlewares, data access, memory, cpu, state management, etc. Timur Shemsedinov 8-9 NOV ‘19 KIEV, UKRAINE Node.js Antipatterns JS FEST PROFESSIONAL JS CONFERENCE
  • 2. Node.js Antipatterns Classification - Structure and arch. - Initialization - Dependency issues - Application state - Middlewares - Context isolation - Security issues - Asynchronity issues - Blocking operations - Memory leaks - Databases and ORM - Error handling
  • 3. No layers, everything mixed: - Configuration and Dependency management - Network protocols related code (http, tcp, tls…) - Request parsing, Cookies, Sessions - Logging, Routing, Business-logic - I/O: fs, Database queries - Generating responses and error generation - Templating, etc. github.com/HowProgrammingWorks/AbstractionLayers
  • 4. Mixed Layers router.get('/user/:id', (req, res, next) => { const id = parseInt(req.params.id); const query = 'SELECT * FROM users WHERE id = $1'; pool.query(query, [id], (err, data) => { if (err) throw err; res.status(200).json(data.rows); next(); }); });
  • 5. What do we want? async (arg1, arg2, arg3) => { const data1 = await getData(arg1); if (!data1) throw new Error('Message'); const [data2, data3] = await Promise.all( [getData(arg2), getData(arg3)] ); return await processData(data1, data2, data3); } github.com/HowProgrammingWorks/API Transport agnostic, Framework agnostic
  • 6. Middlewares Middlewares is an extremely bad idea for low coupling and high cohesion Middlewares changes: - Socket state - Db connection state - Server state
  • 7. Don’t use globals to pass state let groupName; app.use((req, res, next) => { groupName = 'idiots'; next(); }); app.get('/user', (req, res) => { if (res.groupName === 'idiots') { res.end('I know you!'); } });
  • 8. Don’t mixins to req and res app.use((req, res, next) => { res.groupName = 'idiots'; next(); }); app.get('/user', (req, res) => { if (res.groupName === 'idiots') { res.end('I know you!'); } });
  • 9. Don’t mixins locals to res app.use((req, res, next) => { res.locals.groupName = 'idiots'; next(); }); app.get('/user', (req, res) => { if (res.locals.groupName === 'idiots') { res.end('I know you!'); } });
  • 10. Don’t mixins methods app.get('/user/:id', (req, res, next) => { req.auth = (login, password) => { /* auth */ }; next(); }); app.get('/user/:id', (req, res) => { if (req.auth(req.params.id, '111')) { res.end('I know you!'); } });
  • 11. Don’t require in middleware / handler app.get((req, res, next) => { req.db = new require('pg').Client(); req.db.connect(); next(); }); app.get('/user/:id', (req, res) => { req.db.query('SELECT * from USERS', (e, r) => { }); });
  • 12. Don’t connect db in handlers app.get((req, res, next) => { req.db = new Pool(config); next(); }); app.get('/user/:id', (req, res) => { req.db.query('SELECT * from USERS', (e, r) => { }); });
  • 13. Don’t loose connection on error const db = new Pool(config); app.get('/user/:id', (req, res) => { req.db.query('SELECT * from USERS', (err, r) => { if (err) throw err; // Prepare data to reply client }); });
  • 14. Why execution context isolation? - Errors, crashes - Memory leaks and other resources - Application: data, database connections - File system and root directory - OS environment, PID, IPC - OS Security: users, groups - Networking: socket descriptors, ports, hosts
  • 15. Execution isolation levels - Hardware: servers, networks - Virtual machine (hypervisor) - Container (docker) - Process (node) - Thread (worker_threads) - Sandbox (vm.createContext, vm.Script) - Software context (object, closure)
  • 16. Dependency issues - Almost all we need is in - JavaScript and in Node.js API (just check) - Adding dependencies from NPM tell yourself: - It’s my responsibility - authors give no warranties, - it’s just a good will if they help and support - and I will support fork
  • 17. Security issues - Malicious modules from NPM - Path traversal - Injections: SQL, NoSQL, Blind, JavaScript - Sandbox escaping (vm) - Buffer vulnerabilities - Regular expressions
  • 18. Path traversal const serveFile = fileName => { const filePath = path.join(STATIC_PATH, fileName); return fs.createReadStream(filePath); }; http.createServer((req, res) => { const url = decodeURI(req.url); serveFile(url).pipe(res); }).listen(8000); curl -v http://127.0.0.1:8000/%2e%2e/1-traversal.js
  • 19. Path traversal fixed const serveFile = fileName => { const filePath = path.join(STATIC_PATH, fileName); if (!filePath.startsWith(STATIC_PATH)) { throw new Error(`Access denied: ${name}`); } return fs.createReadStream(filePath); }; http.createServer((req, res) => { const url = decodeURI(req.url); serveFile(url).pipe(res); }).listen(8000);
  • 20. Asynchronity issues - Callback hell and promise hell - Ignoring errors in callbacks - Ignoring errors in promises - Throwing errors and releasing resources - Race conditions and deadlocks - Queuing theory: need to limit concurrency
  • 21. Callback hell select(id, (err, data) => { check(data, (err, valid) => { convert(data, (err, res) => { сache(id, res, err => { send(data); }); }); }); });
  • 22. Flat, decomposed, named const saved = (err, data) => err ? throw err : send(data1); const converted = (err, res) => err ? throw err : сache(res, saved); const checked = (err, valid) => err ? throw err : convert(data, converted); const selected = (err, data) => err ? throw err : check(data, checked); select(id, selected);
  • 24. Promise hell select(id).then(data => { check(data).then(valid => { if (valid) { Promise.all([ convert(data), cache(data) ]).then(send); } }); });
  • 25. Compose errback compose (send, cache, convert, check, select) (id); pipe (select, check, convert, cache, send) (id);
  • 26. Compose AsyncFunction await compose (send, cache, convert, check, select) (id); await pipe (select, check, convert, cache, send) (id);
  • 27. Universal composition: metasync metasync ([select, check, [[convert, cache]], send]) (id); // In arguments: Function | Errback | AsyncFunction // Process: Parallel | Sequential // Functionality: Cancelable | Timeout | Throttle // Result: Thenable | Promise | Errback | EventEmitter
  • 28. Limit execution concurrency const queue = metasync.queue(3) .wait(2000) .timeout(5000) .throttle(100, 1000) .process((item, cb) => cb(err, result)) .success(item => {}) .failure(item => {}) .done(() => {}) .drain(() => {});
  • 29. Asynchronous programming 27 lectures: https://habr.com/ru/post/452974/
  • 30. Race condition demo class Point { constructor(x, y) { this.x = x; this.y = y; } async move(dx, dy) { this.x = await add(this.x, dx); this.y = await add(this.y, dy); } }
  • 31. Race condition demo const random = (min, max) => Math .floor(Math.random() * (max - min + 1)) + min; const add = (x, dx) => new Promise(resolve => { const timeout = random(20, 100); setTimeout(() => resolve(x + dx), timeout); });
  • 32. Race condition demo const p1 = new Point(10, 10); console.log(p1); p1.move(5, 5); p1.move(6, 6); p1.move(7, 7); p1.move(8, 8); setTimeout(() => { console.log(p1); }, 1000);
  • 33. Race condition demo Initial Point { x: 10, y: 10 } Expected Point { x: 36, y: 36 } Actual Point { x: 18, y: 25 }
  • 34. Do we need parallel primitives? - Semaphore: Binary and Counting semaphore - Condition variable - Spinlock - Mutex, Timed mutex, Shared mutex - Recursive mutex - Monitor - Barrier
  • 35. Do we need parallel primitives? https://github.com /HowProgrammingWorks /Semaphore /Mutex /RaceCondition /Deadlock https://youtu.be/JNLrITevhRI
  • 36. Simple Resource Locking class Lock { constructor() { this.active = false; this.queue = []; } leave() { if (!this.active) return; this.active = false; const next = this .queue.pop(); if (next) next(); } } enter() { return new Promise(resolve => { const start = () => { this.active = true; resolve(); }; if (!this.active) { start(); return; } this.queue.push(start); }); }
  • 37. Race condition demo class Point { constructor(x, y) { this.x = x; this.y = y; this.lock = new Lock(); } async move(dx, dy) { await this.lock.enter(); this.x = await add(this.x, dx); this.y = await add(this.y, dy); this.lock.leave(); } }
  • 38. Web Locks API locks.request('resource', opt, async lock => { if (lock) { // critical section for `resource` // will be released after return } }); https://wicg.github.io/web-locks/
  • 39. Web Locks: await (async () => { await something(); await locks.request('resource', async lock => { // critical section for `resource` }); await somethingElse(); })();
  • 40. Web Locks: Promise and Thenable locks.request('resource', lock => new Promise( (resolve, reject) => { // you can store or pass // resolve and reject here } )); locks.request('resource', lock => ({ then((resolve, reject) => { // critical section for `resource` // you can call resolve and reject here }) }));
  • 41. Web Locks: Abort const controller = new AbortController(); setTimeout(() => controller.abort(), 200); const { signal } = controller; locks.request('resource', { signal }, async lock => { // lock is held }).catch(err => { // err is AbortError });
  • 42. Web Locks for Node.js github.com/nodejs/node/issues/22702 Open github.com/nodejs/node/pull/22719 Closed
  • 43. Don’t use blocking operations - Sync calls like fs.readFileSync - Console output like console.log - Remember that require is synchronous - Long loops (including for..of and for await) - Serialization: JSON.parse, JSON.stringify - Iteration: loops, Array.prototype.map, etc. - CPU-intensive: zlib, crypto
  • 44. Loop: for await of is blocking (async () => { let ticks = 0; const timer = setInterval(() => ticks++, 10); const numbers = new Array(1000000).fill(1); let i = 0; for await (const number of numbers) i++; clearInterval(timer); console.dir({ i, ticks }); })(); // { i: 1000, ticks: 0 }
  • 45. AsyncArray (short version) class AsyncArray extends Array { [Symbol.asyncIterator]() { let i = 0; return { next: () => new Promise(resolve => { setTimeout(() => resolve({ value: this[i], done: i++ === this.length }), 0); }) }; } } // github.com/HowProgrammingWorks/NonBlocking
  • 46. Loop: for await of + AsyncArray (async () => { let ticks = 0; const timer = setInterval(() => ticks++, 10); const numbers = new AsyncArray(10000000).fill(1); let i = 0; for await (const number of numbers) i++; clearInterval(timer); console.dir({ i, ticks }); })(); // { i: 10000, ticks: 1163 } https://github.com/HowProgrammingWorks/NonBlocking
  • 47. Memory leaks - References - Global variables - Mixins to built-in Classes - Singletons, Caches - Closures / Function contexts - Recursive closures - Require in the middle of code - Functions in loops
  • 48. Memory leaks - OS and Language Objects - Descriptors: files, sockets... - Timers: setTimeout, setInterval - Events / Subscription / Promises - EventEmitter - Callbacks, Not resolved promises github.com/HowProgrammingWorks/MemoryLeaks
  • 49. ORM is an obvious antipattern - ORM tool for DBMS performance degradation - SQL is much more easier - ORM hides DBMS functionality - Additional security vulnerabilities - Broken OOP principles github.com/HowProgrammingWorks/Databases
  • 50. Error handling antipatterns - Ignoring callback errors - Callback returns multiple times - Unhandled stream errors - Unhandled rejection - Promise resolves multiple times github.com/HowProgrammingWorks/PromiseError
  • 51. Don’t ignore callback errors const cbFunc = (arg1, arg2, arg3, callback) => { readData(arg1, arg2, (error1, data1) => { const arg4 = data1.field; checkData(arg3, arg4, (error2, data2) => { if (error2) callback(new Error('msg')); callback(null, { data1, data2 }); }); }); };
  • 52. Don’t ignore stream errors const sharp = require('sharp'); http.get(url, src => { const dest = fs.createWriteStream(fileName); const resize = sharp().resize(300, 300); const free = () => src.destroyed || src.destroy(); resize.on('error', free); destination.on('error', free); src.pipe(resize).pipe(dest); });
  • 53. Use FP style, like Pump const pump = require('pump'); const sharp = require('sharp'); http.get(url, src => { const dest = fs.createWriteStream(fileName); const resize = sharp().resize(300, 300); const fail = err => throw err; pump(src, resize, dest, fail); });