Slide 1

Slide 1 text

Make Your Node.js API Super Fast Tamar Twena-Stern

Slide 2

Slide 2 text

Tamar Twena-Stern twitter @sterntwena linkedin /tamarstern tamar.twena@gmail.com

Slide 3

Slide 3 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 4

Slide 4 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 5

Slide 5 text

General Tips - DB Related ● Optimize you DB queries with indexes and efficient operations ● Denormalize your data ● Scale your DB - Read/ write replica, replica set , sharding

Slide 6

Slide 6 text

How Can I Improve My Node.js Application Related Database Work ?

Slide 7

Slide 7 text

Let’s Start With A Common Stack

Slide 8

Slide 8 text

Which Parameters We Should Look At When We Take A Performance Benchmark ? ● Latency - 99% ● Average requests per second ● Total requests

Slide 9

Slide 9 text

Using Mongoose VS Native Mongo Driver

Slide 10

Slide 10 text

Server With Mongoose import express from 'express'; import mongoose from 'mongoose'; // Create an Express application const app = express(); // Connect to MongoDB using Mongoose mongoose.connect('mongodb://localhost/mydatabase'); const db = mongoose.connection;

Slide 11

Slide 11 text

Server With Mongoose // Define a Mongoose schema for the person object const personSchema = new mongoose.Schema({ name: String, age: Number, gender: String }); const Person = mongoose.model('Person', personSchema);

Slide 12

Slide 12 text

Server With Mongoose app.post('/persons', async (req, res) => { try { // Extract person data from the request body const { name, age, gender } = req.body; // Create a new person document using the Person model const person = new Person({ name, age, gender }); // Save the person to the database await person.save(); res.status(201).json({ message: 'Person saved successfully' }); } catch (error) { console.error('Error saving person:', error); res.status(500).json({ error: 'Error saving person' }); } });

Slide 13

Slide 13 text

Server With Native Mongo Driver import express from 'express'; import { MongoClient } from 'mongodb'; // Connection URL const url = 'mongodb://localhost:27017'; // Database Name const dbName = 'mydatabase'; const client = new MongoClient(url); try { client.connect(); } catch (err) { console.log(err); }

Slide 14

Slide 14 text

Server With Native Mongo Driver app.post('/persons', async (req, res) => { try { // Extract person data from the request body const { name, age, gender } = req.body; // Create a new person document const person = { name: name, age: age, gender: gender }; // Insert the person document const result = await collection.insertOne(person); res.status(201).json({ message: 'Person saved successfully' }); } catch (error) { console.error('Error saving person:', error); res.status(500).json({ error: 'Error saving person' }); } });

Slide 15

Slide 15 text

Results - Mongoose

Slide 16

Slide 16 text

Results - Mongo Driver

Slide 17

Slide 17 text

Performance Comparison - Mongoose VS Native Mongo Driver

Slide 18

Slide 18 text

Performance Comparison - Mongoose VS Native Mongo Driver

Slide 19

Slide 19 text

Why Mongo Driver Improved Performance Over Mongoose By 30% ?

Slide 20

Slide 20 text

Work With DB Connection Pool

Slide 21

Slide 21 text

DB Connection Pool

Slide 22

Slide 22 text

Compare Results When Working With A Connection Pool

Slide 23

Slide 23 text

Results - No Connection Pool

Slide 24

Slide 24 text

Results - With Connection Pool

Slide 25

Slide 25 text

Performance Comparison - Optimizing The Connection Pool

Slide 26

Slide 26 text

Performance Comparison - Connection Pool vs No Connection Pool

Slide 27

Slide 27 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 28

Slide 28 text

Node.js Architecture

Slide 29

Slide 29 text

Use Efficient Serialization - It Is A Synchronous Operation That Blocks The Event Loop

Slide 30

Slide 30 text

Efficient Response Serialization with fast-json-stringify

Slide 31

Slide 31 text

Default Express Serializer And Get API // Middleware to parse JSON request body app.use(express.json()); app.get('/persons', async (req, res) => { try { const gender = req.query.gender; const person = await collection.findOne({ gender: gender }, { limit: 1 }); if (person) { res.json(person); } else { res.status(404).json({ message: 'Person not found' }); } } catch (err) { console.error(err); res.status(500).json({ message: 'Internal server error' }); } });

Slide 32

Slide 32 text

fast-json-serializer Middleware import fastJson from 'fast-json-stringify'; const stringify = fastJson({ title: 'Person', type: 'object', properties: { name: { type: 'string' }, gender: { type: 'string' }, age: { description: 'Age in years', type: 'integer' } } })

Slide 33

Slide 33 text

fast-json-serializer Middleware // Custom JSON serialization middleware const customJsonMiddleware = (req, res, next) => { // Override the res.json() method with custom serialization logic res.json = function(data) { const jsonData = stringify(data); // Custom serialization logic res.setHeader('Content-Type', 'application/json'); res.end(jsonData); }; // Call the next middleware or route handler next(); }; // Use the custom JSON serialization middleware for all routes app.use(customJsonMiddleware);

Slide 34

Slide 34 text

Performance Comparison - express.json vs fast-json-stringify

Slide 35

Slide 35 text

Performance Comparison - express.json vs fast-json-stringify

Slide 36

Slide 36 text

Fast-json-stringify will give the performance advantage You can look for other libraries that will give you the performance edge

Slide 37

Slide 37 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 38

Slide 38 text

Plan : Build perfect systems that will be always stable Reality : Applications crash, random bugs, missing data in production

Slide 39

Slide 39 text

Logging Will Cause Performance Degradation

Slide 40

Slide 40 text

Results - Without Logs

Slide 41

Slide 41 text

Results - With One Log Line

Slide 42

Slide 42 text

Different Log Libraries // Winston import winston from 'winston'; const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [new winston.transports.Console()], }); logger.info("Person object saved successfully"); // pinojs import pino from 'pino'; const logger = pino({}); logger.info("Person object saved successfully");

Slide 43

Slide 43 text

Different Log Libraries // bunyun import bunyan from 'bunyan'; const logger = bunyan.createLogger({ name: 'myapp' }); logger.info("Person object saved successfully"); // log4js import log4js from'log4js'; const logger = log4js.getLogger(); logger.level = 'info'; logger.info("Person object saved successfully");

Slide 44

Slide 44 text

Node.js Log Libraries - Performance Comparison

Slide 45

Slide 45 text

Node.js Log Libraries - Performance Comparison

Slide 46

Slide 46 text

Conclusion - Performance Wise - Prefer Pinojs

Slide 47

Slide 47 text

Log Library To Do The Message Building And Not Your Service logger.info("Person object saved successfully %O", person); logger.info(`Person ${JSON.stringify(person)} saved successfully`);

Slide 48

Slide 48 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 49

Slide 49 text

Web Framework For Writing Node.js Microservices

Slide 50

Slide 50 text

Express Vs Fastify Vs Nest.js Performance benchmark

Slide 51

Slide 51 text

Result - Express

Slide 52

Slide 52 text

Result - Nest.js

Slide 53

Slide 53 text

Results - Fastify

Slide 54

Slide 54 text

Comparing Results - Nest.js vs Express vs Fastify

Slide 55

Slide 55 text

Comparing Results - Nest.js vs Express vs Fastify

Slide 56

Slide 56 text

If you are able to refactor your microservices code or You are starting a new project Choosing a performance efficient web framework will contribute a lot to fast request processing

Slide 57

Slide 57 text

1. 2. 3.

Slide 58

Slide 58 text

Database Work Performance Efficient Log Library Should We Choose A different web Framework ? Caching Efficient JSON parsing and stringify

Slide 59

Slide 59 text

Authenticating Users With JWT Tokens

Slide 60

Slide 60 text

Adding JWT Authentication To The Server

Slide 61

Slide 61 text

Validating Request With JWT Token import jwt from 'jsonwebtoken'; // Middleware to parse the JWT token from the request app.use((req, res, next) => { const authHeader = req.headers.authorization; if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.split(' ')[1]; try { // Verify and decode the JWT token const decodedToken = jwt.verify(token, secretKey); // Attach the decoded token to the request object req.decodedToken = decodedToken; } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } } next(); });

Slide 62

Slide 62 text

Performance Comparison After Adding JWT Authentication

Slide 63

Slide 63 text

Results - Without Authentication

Slide 64

Slide 64 text

Results - With Authentication

Slide 65

Slide 65 text

To Solve The Performance Problem, Lets Add Caching

Slide 66

Slide 66 text

Results - Without Authentication

Slide 67

Slide 67 text

Results - With Authentication

Slide 68

Slide 68 text

Results - Authentication And Caching

Slide 69

Slide 69 text

Performance Comparison - With Caching

Slide 70

Slide 70 text

Performance Comparison - With Caching

Slide 71

Slide 71 text

Performance Comparison - With Caching

Slide 72

Slide 72 text

Node-Cache - In memory caching import NodeCache from 'node-cache'; const cache = new NodeCache({ stdTTL: 3600 }); const cachedToken = cache.get(token); cache.set(token, decodedToken);

Slide 73

Slide 73 text

Prefer Using Redis - Distributed Cache import { createClient } from 'redis'; const client = await createClient().connect(); await client.set('key', 'value'); const value = await client.get('key'); await client.disconnect();

Slide 74

Slide 74 text

Cache Computationally Intensive Operations I/O - DB Queries I/O - HTTP Requests From Third Party Encoding Encryption / Decryption

Slide 75

Slide 75 text

Cache best practices ● Use read-through pattern — first check cache, fallback to DB/upstream API. ● Cache based on request parameters to avoid stale data. ● Set proper cache TTL. ● Watch cache hit ratio and fine tune expiry times accordingly. ● Avoid caching user-specific or sensitive data.

Slide 76

Slide 76 text

Summary ● Optimize your DB queries ● For large scale - work with the DB driver ● Have the ability to optimize your connection pool ● Use caching when needed ● Choose efficient - ○ Serializer ○ Log library ○ Web framework

Slide 77

Slide 77 text

Tamar Twena-Stern twitter @sterntwena linkedin /tamarstern tamar.twena@gmail.com