Best Practice in Optimising Node.js Applications and Services

Optimising your NodeJS/Javascript applications, APIs or micro-services can help achieve the best performance, reduce resource usage and improve user experience.

January 3, 2023

In the latest "Best Practice" guide, we look at some of the approaches that should be considered at each and every design review, to ensure your application is efficient and providing the best user experience. Again, this is a summary of the checklists that we have used and developed over the years, developed from a number of guides, texts and practical experience.

Optimising your NodeJS/Javascript applications, APIs or micro-services can help achieve the best performance, reduce resource usage and improve user experience. Equally it can also help to reduce operational issues like timeouts, dead-locks etc. "Full stack development" with NodeJS provides a number of ways to optimise execution, improve efficiency and drive simplicity.

The following 9 approaches should be on the checklist at each and every design review, to make sure they have been considered and used where appropriate.

1. Asynchronous Functions

Async functions are like the heart of NodeJS/JavaScript and Full Stack Development. By using Asynchronous functions to perform non-blocking I/O operations ( including read and write data operations) , applications can reduce CPU & resource usage ( including resources associated with databases, cloud storage, and any local storage disk ) .

Asynchronous functions enable the CPU to use non-blocking I/O, and hence to handle multiple requests simultaneously. NodeJS can use Promises (in a number of flavours) and Observables from the RxJS framework to manage multiple asynchronous operations, although both can introduce complexity in to the code. 

Nodejs is almost impossible to use without embracing and understanding Promises and Async functions. However, Observables are the next step in reactive development, and its well worth the effort to understand and exploit this very powerful feature.

2. Avoid Sessions and Cookies

Send Only Data in Micro-services or NodeJs responses, and look to avoid using Cookies and sessions information to store temporary states in server code. Server side session information is expensive in terms of resources and overheads, and stateless APIs are more common using JWT, OAuth, and other authentication mechanisms ( with tokens being kept client side ).

JWT's are a JSON-based security token for API Authentication, containing non-modifiable, serialised, and not encrypted, typically using the OAuth standard to obtain a token.

3. Database Queries

Query optimisation is an essential part of building optimized APIs in Node. In large applications, there will often be many database queries. Bad queries (SQL or NOSQL) can reduce the overall performance of the application, and consume un-necessary resources - reducing user experience.

4. Clustering

Clustering is a useful technique that can enable multiple requests to be handle by multiple services concurrently, It can also increase resilience and reducing latency by locating services 'nearer' to clients.

PM2 is a production process manager designed for Node.js applications. It has a built-in load balancer and allows the application to run as multiple processes without code modifications. Application downtime can be reduced using PM2.

Other patterns/architectures to implement Clustering include using a Service Bus and/or Service Orientated Architecture to distribute your application across multiple instances, with the option to elastically scale as demand requires.

Cloud native solutions using kubernetes, Cloud Foundry or AWS Lambda can also provide similar clustering, resillience and performance improvements.

5. Reduce TTFB (Time to First Byte)

Time to the first byte is used as an indication of the responsiveness of a web service or network resource. TTFB measures the duration from the user or client making an HTTP request to the first byte of the page being received by the client's browser. There are a number of coding and development patterns that can be used to improve TTFB. Content Delivery Network (a form of global proxy server) can improve TTFB. By using a CDN ( Like Cloudflare), it is possible to also reduce latency.

6. Error Trapping, Logging & Monitoring

The best way to monitor the proper functioning of services and APIs is to keep track of its activity. At its simplest logging to the console (eg using console.log()), or by using standard modules like Morgan, Buyan, and Winston can provide valuable information.

 Performance and optimisation comes from identifying where resources and time is being consumed and enhancing the structure and approach to mitigate bottlenecks or resource intensive sections. Browsers have built in network tracing tools, but typically specialised applications are used to monitor performance in Microservices or backend environments.

7. Use HTTP/2 Instead of HTTP

HTTP/2 over HTTP has many advantages including Multiplexing,Header compression,Server push, and Binary format transfer. HTTP/2 focuses on performance and reduces some of the issues that HTTP has. It also can make web browsing faster/easier, while consuming less bandwidth.

8. Multi-thread/Parallel Execution

Parallelizing tasks can have a great impact on the performance of your API. It can reduce latency and minimizes blocking operations. "async.js", Promises and 'Observables' can help run multiple tasks at the same time within a service or application. However, when things are run in parallel, additional care needs to be take around execution order, and error handling.

9. Avoid Server Side Static Files

Avoid serving static files from your NodeJS application or Service, opting to use a productiuon web server like NGINX and Apache instead.

Avoid sending the full HTML page(s) in the API responses: Node servers work better when only data is sent by the API, optimised for returning data ( JSON or the such ). NodeJS servers arentt efficient at replicating the functionality and security of complex Web Server technology like Apache or Nginx.

Summary

Full Stack Development , with NodeJS/Javascript at the core is a great platform and base to build advanced maintainable services. NodeJs has evolved with a number of advanced features to provide resilient, performant cloud based services, that are elastic and efficient in delivery. By considering this simple set of recommendations and approaches, you can look to achieve great user experience without complex re-engineering or expensive platform investments.

As part of our series of NodeJS guides, our blog [Achieving Efficiency and Effectiveness in NodeJs/Full Stack Development](here is a good companion to this guide, and can be found here

Integrating these checklists into your design reviews and architecture assurance activities will help reduce your risks, meet your obligations and ensure you have made significant in-roads to ensuring efficient and effective services.

image