├── 01-throw-context-err.js ├── 01-throw-context-solution.js ├── 02-custom-exception-err.js ├── 02-custom-exception-solution.js ├── 03.1-kubernetes-simulator.js ├── 03.2-server-let-it-crash.js ├── README.md └── package.json /01-throw-context-err.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | 3 | try { 4 | fs.readFile( 5 | 'not-found.txt', 6 | (err, result) => { 7 | if(err) { throw err } 8 | } 9 | ) 10 | } catch (error) { 11 | console.log('nunca é chamado!', error) 12 | } -------------------------------------------------------------------------------- /01-throw-context-solution.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | // function abc(callback) { 3 | // setTimeout(() => { 4 | // try { 5 | // callback('error') 6 | // } catch (error) { 7 | 8 | // } 9 | // }) 10 | // } 11 | 12 | try { 13 | // abc((err) => { 14 | // if(err) { 15 | // throw err 16 | // } 17 | // }) 18 | await new Promise((resolve, reject) => { 19 | fs.readFile( 20 | 'not-found.txt', 21 | (err, result) => err ? reject(err) : resolve(result) 22 | ) 23 | }) 24 | } catch (error) { 25 | console.log('agora é chamado!', error) 26 | } -------------------------------------------------------------------------------- /02-custom-exception-err.js: -------------------------------------------------------------------------------- 1 | class InvalidFormatError extends Error { 2 | constructor(message) { 3 | super(message) 4 | this.name = 'InvalidFormatError' 5 | } 6 | } 7 | 8 | class InvalidLengthError extends Error { 9 | constructor(message) { 10 | super(message) 11 | this.name = 'InvalidLengthError' 12 | } 13 | } 14 | 15 | function validateCpf(cpf) { 16 | if (isNaN(cpf)) { 17 | throw new InvalidFormatError(`O CPF [${cpf}] deve conter apenas numeros`) 18 | } 19 | if (cpf.length !== 11) 20 | throw new InvalidLengthError(`O CPF [${cpf}] deve ter 11 digitos`) 21 | } 22 | 23 | for (const cpf of ['123', 'abc', '12345678901']) { 24 | try { 25 | validateCpf(cpf) 26 | console.log(`O CPF [${cpf}] é válido`) 27 | } catch (error) { 28 | if ( 29 | error instanceof InvalidFormatError || 30 | error instanceof InvalidLengthError 31 | ) { 32 | console.log(error.message) 33 | continue 34 | } 35 | 36 | console.log('Erro desconhecido', error.message) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /02-custom-exception-solution.js: -------------------------------------------------------------------------------- 1 | function validateCpf(cpf) { 2 | const errors = [] 3 | if (isNaN(cpf)) { 4 | errors.push({ 5 | message: `O CPF [${cpf}] deve conter apenas numeros`, 6 | name: 'InvalidFormatError' 7 | }) 8 | } 9 | if (cpf.length !== 11) { 10 | errors.push({ 11 | message: `O CPF [${cpf}] deve ter 11 digitos`, 12 | name: 'InvalidLenghtError' 13 | }) 14 | } 15 | 16 | return { 17 | valid: !errors.length, 18 | errors, 19 | } 20 | } 21 | 22 | for (const cpf of ['123', 'abc', '12345678901']) { 23 | 24 | const { valid, errors } = validateCpf(cpf) 25 | if (valid) { 26 | console.log(`O CPF [${cpf}] é válido`) 27 | continue 28 | } 29 | 30 | errors.forEach(({ message, name}) => { 31 | console.log(`[${name}] - ${message}`) 32 | }) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /03.1-kubernetes-simulator.js: -------------------------------------------------------------------------------- 1 | import { spawn } from 'node:child_process' 2 | const prepareLog = pid => msg => console.log(`pid: [${pid}] - ${msg}`) 3 | 4 | const INSTANCES = 3 5 | 6 | function spinUpInstance() { 7 | const cp = spawn('node', ['03.2-server-let-it-crash.js']) 8 | const log = prepareLog(cp.pid) 9 | log('starting...') 10 | cp.stdout.on('data', msg => console.log(msg.toString().trim())) 11 | cp.on('exit', (code) => { 12 | // 0 significa terminou com sucesso! 13 | // 1 significa terminou com error! 14 | log(`exited with code ${code}`) 15 | if (code === 0) { 16 | return; 17 | } 18 | spinUpInstance() 19 | }) 20 | 21 | } 22 | 23 | for (let i = 0; i < INSTANCES; i++) { 24 | spinUpInstance() 25 | } -------------------------------------------------------------------------------- /03.2-server-let-it-crash.js: -------------------------------------------------------------------------------- 1 | 2 | const UNKNOWN_ERROR = 1 3 | const knownErrors = [ 4 | { exitCode: UNKNOWN_ERROR, event: 'uncaughtException' }, 5 | { exitCode: UNKNOWN_ERROR, event: 'unhandledRejection' }, 6 | ] 7 | const log = msg => console.log(`pid: [${process.pid}] - ${msg}`) 8 | 9 | process.on('exit', (code) => { 10 | // Fecha a porta de novos requests 11 | // e aguarda os usuarios conectados encerrarem os requests 12 | // daria db.stop, server.close 13 | log(`Server closed with sucess`) 14 | log(`DB closed with sucess`) 15 | process.exit(code) 16 | }) 17 | 18 | knownErrors.forEach(({ exitCode, event }) => { 19 | process.on(event, (error) => { 20 | log(`Process exiting due to ${event}`, error.message) 21 | if (exitCode === UNKNOWN_ERROR) { 22 | // process.abort() 23 | process.exit(exitCode) 24 | return; 25 | } 26 | 27 | process.exit(exitCode) 28 | }) 29 | }) 30 | log('Process started') 31 | 32 | let counter = 0 33 | const connectToDB = () => { 34 | const random = Math.random() 35 | // throw new Error('ah não!!') 36 | if (random < 0.5) 37 | return Promise.reject('Could not connect to DB') 38 | log('DB connected with success') 39 | if(++counter > 3) process.exit(0) 40 | } 41 | 42 | setInterval(() => connectToDB(), 200); 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # High Reliability in JavaScript - Tutorial 2 | 3 | Welcome, this repo is part of my [**youtube video**](https://youtu.be/iC_tKAyLeag?si=Pz2SXwOM3t9VkbVZ) about **Custom error handling + graceful shutdown + let it crash strategy in JavaScript (pt-br)** 4 | 5 | First of all, leave your star 🌟 on this repo. 6 | 7 | Access our [**exclusive telegram channel**](https://t.me/ErickWendelContentHub) so I'll let you know about all the content I've been producing 8 | 9 | ![JavaScript is unreliable](https://github.com/ErickWendel/high-reliability-js/assets/8060102/3685122f-b2a2-4f50-9b40-418c6d0b1be0) 10 | 11 | ## Checkout the examples 12 | - [Understanding the issue while throwing errors from callbacks](./01-throw-context-err.js) 13 | - [How to throw errors from async contexts](./01-throw-context-solution.js) 14 | - [Common pattern for custom error classes in JavaScript](./02-custom-exception-err.js) 15 | - [Handling errors as values, not exceptions (domain notification pattern)](./02-custom-exception-solution.js) 16 | - [Kubernetes simulator (once an application crashes it spins up another)](./03.1-kubernetes-simulator.js) 17 | - [Let it crash! Handling global errors + graceful shutdown](./03.2-server-let-it-crash.js) 18 | 19 | Try them out [online here](https://gitpod.io#snapshot/c0f01607-ecd2-4a66-81d5-13f1a9fcb0b6) 20 | 21 | ## Author 22 | - [Erick Wendel](https://linktr.ee/erickwendel) 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "erickwendel", 11 | "license": "ISC", 12 | "engines": { 13 | "node": "v20.10.0" 14 | }, 15 | "type": "module" 16 | } 17 | --------------------------------------------------------------------------------