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

What's hot (20)

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

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

PDF
MongoDB World 2019: Life In Stitch-es
MongoDB
 
PDF
node.js practical guide to serverside javascript
Eldar Djafarov
 
KEY
Node.js - Best practices
Felix Geisendörfer
 
PPTX
Nantes Jug - Java 7
Sébastien Prunier
 
PDF
Node Powered Mobile
Tim Caswell
 
PPTX
Developing web-apps like it's 2013
Laurent_VB
 
PDF
NoSQL and JavaScript: a Love Story
Alexandre Morgaut
 
PDF
Secure .NET programming
Ante Gulam
 
PPTX
Anti patterns
Alex Tumanoff
 
PDF
Node.js - A Quick Tour
Felix Geisendörfer
 
PPTX
Java
박 경민
 
PDF
Developing Applications with MySQL and Java for beginners
Saeid Zebardast
 
PPT
JS everywhere 2011
Oleg Podsechin
 
PDF
DEF CON 23 - amit ashbel and maty siman - game of hacks
Felipe Prado
 
PDF
async/await in Swift
Peter Friese
 
PDF
Reduxing like a pro
Boris Dinkevich
 
PDF
Think Async: Asynchronous Patterns in NodeJS
Adam L Barrett
 
PDF
Future Decoded - Node.js per sviluppatori .NET
Gianluca Carucci
 
PPTX
Tools for Making Machine Learning more Reactive
Jeff Smith
 
PDF
Painless Persistence with Realm
Christian Melchior
 
MongoDB World 2019: Life In Stitch-es
MongoDB
 
node.js practical guide to serverside javascript
Eldar Djafarov
 
Node.js - Best practices
Felix Geisendörfer
 
Nantes Jug - Java 7
Sébastien Prunier
 
Node Powered Mobile
Tim Caswell
 
Developing web-apps like it's 2013
Laurent_VB
 
NoSQL and JavaScript: a Love Story
Alexandre Morgaut
 
Secure .NET programming
Ante Gulam
 
Anti patterns
Alex Tumanoff
 
Node.js - A Quick Tour
Felix Geisendörfer
 
Developing Applications with MySQL and Java for beginners
Saeid Zebardast
 
JS everywhere 2011
Oleg Podsechin
 
DEF CON 23 - amit ashbel and maty siman - game of hacks
Felipe Prado
 
async/await in Swift
Peter Friese
 
Reduxing like a pro
Boris Dinkevich
 
Think Async: Asynchronous Patterns in NodeJS
Adam L Barrett
 
Future Decoded - Node.js per sviluppatori .NET
Gianluca Carucci
 
Tools for Making Machine Learning more Reactive
Jeff Smith
 
Painless Persistence with Realm
Christian Melchior
 
Ad

More from Timur Shemsedinov (15)

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

Recently uploaded (20)

PPTX
ERP - FICO Presentation BY BSL BOKARO STEEL LIMITED.pptx
ravisranjan
 
PPTX
Android Notifications-A Guide to User-Facing Alerts in Android .pptx
Nabin Dhakal
 
PDF
Laboratory Workflows Digitalized and live in 90 days with Scifeon´s SAPPA P...
info969686
 
PPTX
Perfecting XM Cloud for Multisite Setup.pptx
Ahmed Okour
 
PPTX
Automatic_Iperf_Log_Result_Excel_visual_v2.pptx
Chen-Chih Lee
 
PPTX
computer forensics encase emager app exp6 1.pptx
ssuser343e92
 
PPTX
NeuroStrata: Harnessing Neuro-Symbolic Paradigms for Improved Testability and...
Ivan Ruchkin
 
PPTX
Mistakes to Avoid When Selecting Policy Management Software
Insurance Tech Services
 
PPTX
IObit Driver Booster Pro 12.4-12.5 license keys 2025-2026
chaudhryakashoo065
 
PPTX
An Introduction to ZAP by Checkmarx - Official Version
Simon Bennetts
 
PDF
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 
PPTX
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
PDF
Transparency into Your Software’s True Reach
team-WIBU
 
PDF
From Chaos to Clarity: Mastering Analytics Governance in the Modern Enterprise
Wiiisdom
 
PDF
Rewards and Recognition (2).pdf
ethan Talor
 
PPTX
Quality on Autopilot: Scaling Testing in Uyuni
Oscar Barrios Torrero
 
PPTX
IObit Driver Booster Pro Crack Download Latest Version
chaudhryakashoo065
 
PDF
WholeClear Split vCard Software for Split large vCard file
markwillsonmw004
 
PPTX
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
PPTX
Cubase Pro Crack 2025 – Free Download Full Version with Activation Key
HyperPc soft
 
ERP - FICO Presentation BY BSL BOKARO STEEL LIMITED.pptx
ravisranjan
 
Android Notifications-A Guide to User-Facing Alerts in Android .pptx
Nabin Dhakal
 
Laboratory Workflows Digitalized and live in 90 days with Scifeon´s SAPPA P...
info969686
 
Perfecting XM Cloud for Multisite Setup.pptx
Ahmed Okour
 
Automatic_Iperf_Log_Result_Excel_visual_v2.pptx
Chen-Chih Lee
 
computer forensics encase emager app exp6 1.pptx
ssuser343e92
 
NeuroStrata: Harnessing Neuro-Symbolic Paradigms for Improved Testability and...
Ivan Ruchkin
 
Mistakes to Avoid When Selecting Policy Management Software
Insurance Tech Services
 
IObit Driver Booster Pro 12.4-12.5 license keys 2025-2026
chaudhryakashoo065
 
An Introduction to ZAP by Checkmarx - Official Version
Simon Bennetts
 
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 
Comprehensive Guide: Shoviv Exchange to Office 365 Migration Tool 2025
Shoviv Software
 
Transparency into Your Software’s True Reach
team-WIBU
 
From Chaos to Clarity: Mastering Analytics Governance in the Modern Enterprise
Wiiisdom
 
Rewards and Recognition (2).pdf
ethan Talor
 
Quality on Autopilot: Scaling Testing in Uyuni
Oscar Barrios Torrero
 
IObit Driver Booster Pro Crack Download Latest Version
chaudhryakashoo065
 
WholeClear Split vCard Software for Split large vCard file
markwillsonmw004
 
3uTools Full Crack Free Version Download [Latest] 2025
muhammadgurbazkhan
 
Cubase Pro Crack 2025 – Free Download Full Version with Activation Key
HyperPc soft
 

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); });