Programming
Facundo Spira • 12 JUN 2023
Node.js in action: our winning tech stack
Over time, Node.js has become one of our top technologies for creating web applications. When building applications with Node.js, we use a variety of frameworks, libraries, and tools that complement Node.js and enhance its capabilities. This combination of different technologies is referred to as a stack.
In this blog post, we will discuss the different components of our Node.js stack, including the programming languages, frameworks, libraries, and tools we use to build our web and mobile applications. We'll explain how each of these components works together to create a seamless development process and how they work smoothly for us.
Why do we choose Node.js?
Node.js is an open source Javascript runtime environment that runs on the V8 engine, an execution engine that was initially built for Google Chrome. Written in C++, V8 compiles JavaScript source code to native machine code.
One of the reasons why it is one of the technologies we use the most is because it is perfectly suited for microservices architecture. With microservices, applications are split up into smaller, independent services that can be developed, deployed, and scaled independently. This makes the process of managing an application much more flexible and efficient and makes a software product easily scalable. By utilizing Node.js, we are able to provide our clients with web applications that are robust, reliable, and capable of meeting their specific needs.
Some of the advantages of Node.js:
- Speed and Performance: Using the V8 JavaScript engine makes Node.js very fast and efficient.
- Scalability: Node.js is highly scalable and can handle a large number of simultaneous connections without degrading performance.
- Active Community: Node.js has an active community of developers and a large number of libraries and modules available for use. Furthermore, the community can also help drive innovation and improve the overall quality of the software being developed, helping on ensuring that the software remains up-to-date and relevant to the current necessities.
Our Node.js Stack
In addition to Node.js, we use several other tools that complement its capabilities.
Our Node.js stack is designed to provide a robust and flexible foundation for building high-performing and scalable applications. By leveraging the power of Node.js and its ecosystem of tools and technologies, we can deliver applications that meet the needs of our clients and users. These are the tools we rely on to build Node apps:
Typescript
TypeScript is a statically typed superset of JavaScript that provides additional features and functionality to the language. Let’s discuss some of the benefits that Typescript introduces:
-
Static typing system: TypeScript introduces a static typing system that allows developers to catch errors during development, before the code is executed. This helps to prevent runtime errors and makes it easier to organize and maintain code. Static typing system also enables code editors to provide better code suggestions and autocomplete functionality. This can help developers write code more quickly and accurately.
-
Improved code readability and maintainability: by making types explicit, it's easier to understand what a specific piece of code is doing, and it's easier to make changes without introducing errors. This feature makes doing refactors much easier, since we can immediately know which sections of our code are affected when we make a change, providing an amazing developer experience.
-
Compatibility with existing JavaScript code: as it was already mentioned, TypeScript is a superset of JavaScript, which means that any valid JavaScript code is also valid TypeScript code. This makes it really easy to integrate TypeScript into existing projects and to gradually adopt TypeScript without having to rewrite everything from scratch.
GraphQL
GraphQL is a query language that provides an approach to developing web APIs and has been compared and contrasted with REST and other web service architectures.
Some common problems developers face when building REST APIs are underfetching and overfetching.
-
Underfetching: it occurs when a client needs to retrieve additional data to display a resource, resulting in additional network requests and reduced performance. For example, if a client needs to display a list of users along with their profile pictures, a traditional RESTful API may require two separate requests to retrieve this information. With GraphQL, the client can specify that it needs both the user data and their profile pictures in a single query, eliminating the need for additional requests.
-
Overfetching: occurs when a client retrieves more data than it actually needs, resulting in unnecessary network traffic and reduced performance. An example of overfetching is when a client only needs a user's name and email address but receives their entire profile, including their address, phone number and other fields that it will not be using. By using GraphQL, the client can specify exactly what data it needs in the query, preventing unnecessary data from being sent over the network.
This way, by allowing clients to request exactly the data they need, GraphQL eliminates the need for multiple round trips to the server and reduces network traffic, resulting in faster load times and improved performance.
Another benefit of using GraphQL is its strong typing and introspection capabilities. When building a GraphQL API, schema is defined, serving as a contract between the client and the server. This schema includes types, fields, and operations that are available in the API, as well as the input and output types for each field.
Taking advantage of GraphQL's introspection, the client is able to query the schema itself, allowing them to discover the available types, fields and operations without the need of any additional documentation. This way, by using tools such as GraphQL Playground developers can explore the API Schema and build and test queries. Furthermore, the strong typing in GraphQL eliminates the need for manual type checking, reducing the chance of runtime errors and making development faster and less error-prone. By providing a clear and well-documented schema, GraphQL improves the developer experience and reduces the time and effort required to build and maintain APIs.
Taking it one step further
We also use a set of auxiliary tools that are widely used in the software community. They allow us to maintain a high level of code quality while maintaining development efficiency. Some of these are Eslint, Husky and Conventional Commits.
ESlint
ESLint is a static code analysis tool for identifying problematic patterns found in JavaScript/Typescript code.
It’s really easy to set up. Once we have our project created, we can just initialize ESlint by using our package manager (npm in this example).
npm init @eslint/config
This command will install the necessary dependencies and initialize our .eslintrc
file with all the predefined rules. These rules can be further customized to the need of the specific project.
Husky
We use Husky to automate the execution of scripts before committing or pushing changes to Git. This ensures that for example, the code is formatted correctly and that the tests pass before the changes are pushed to the repository.
# Install husky
npx husky-init && npm install
# Add new hooks for pre-commit and pre-push actions.
npx husky add .husky/pre-commit "npx eslint ."
npx husky add .husky/pre-push "npm test"
Conventional Commits
We like to use Conventional Commits to ensure that the commit messages are consistent and informative. It is also really helpful to automatically generating CHANGELOGs. Furthermore, if you are working on an open source project, it is easier for other contributors to understand what changes were made.
When using Conventional Commits, commit messages should be structured as follows:
[optional scope]:
[optional body]
[optional footer(s)]
So a commit message could look like this:
refactor!: drop support for Node 6
Starting with the next major version we will no longer support Node 6.
BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.
However, it's usual that most commits doesn't require a body or footer. So, in that case, most commits will look like this:
feat(landing): add spanish translations to the landing page
Or even without a scope:
fix: add missing semicolon on Cart controller
To help follow this convention, we use a tool called commitlint
along with Husky. This tool will check if the commit messages follow the conventional commits convention. If not, it will fail the commit and will not allow you to push the changes to the repository.
To install it, we need to follow these steps:
npm install -D @commitlint/{config-conventional,cli}
Then we need to create a commitlint.config.js
file in the root of the project with the following content:
module.exports = { extends: ['@commitlint/config-conventional'] }
This uses the config-conventional
preset, which is based on the Angular convention. However, one can decide to further configure the rules to fit the needs of the project, for example by modifying the type-enum
rule to only allow certain types of commits.
Enhancing Node.js with powerful tools
By incorporating these powerful tools alongside Node.js, we can enhance our development process, streamline workflows, and build robust and reliable applications. These tools address various aspects of web development, including API creation, database interaction, testing, and process management, enabling developers to deliver high-quality applications that meet the demands of modern web development.
This is the stack we chose to create efficient applications based on Node.js. Of course, they are not the only ones, and there are infinite options when it comes to developing scalable applications.
While Node.js serves as the backbone for executing JavaScript on the server side, we lean on these complementary tools that significantly contribute to augment the capabilities of Node.js and enable us to overcome common challenges in web development.
Visit our blog for more information about Node.js and how we use this technology.