Node.js: The commonly used NPM packages — Part 4
There is no denying that Node.js’s power comes from the 1.5M NPM packages. Without NPM, Node.js is still usable, but not much. In this article series, we will explore the commonly used NPM packages that every developer should be aware of. In this part, we’ll cover packages 31 to 40.
The other parts in this series are:
- Part 1: Packages 1 to 10
- Part 2: Packages 11 to 20
- Part 3: Packages 21 to 30
- Part 5: Packages 41 to 50 (last part)
31. Prisma
Prisma serves as a next-generation ORM (Object-Relational Mapper) for Node.js and TypeScript applications, streamlining interactions with databases. It offers a type-safe API, auto-generated queries, and migrations, fostering a productive and error-free development experience.
Statistics
- Weekly downloads: 1.7 M
- Size: 14.3 M
- Files: 87
Code Samples
Defining a Model
// prisma/schema.prisma
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
createdAt DateTime @default(now())
}
Fetching Data
const users = await prisma.user.findMany();
Creating Data
const newUser = await prisma.user.create({
data: {
name: 'Alice',
email: 'alice@example.com',
},
});
Updating Data
const updatedUser = await prisma.user.update({
where: { id: 1 },
data: { name: 'Bob' },
});
Pros
- Type safety: Ensures data integrity and prevents errors at compile time.
- Auto-generated queries: Saves time and reduces the potential for errors.
- Migrations: Manages database schema changes smoothly.
- Performance: Optimized for efficient database interactions.
Cons
- Learning curve: Understanding Prisma’s concepts and configuration might require some effort.
- Abstraction: ORMs inherently abstract database interactions, potentially limiting control in certain scenarios.
- Vendor lock-in: Primarily supports PostgreSQL, MySQL, SQLite, and SQL Server.
32. Day.js
Day.js offers a minimalist and performant JavaScript library for parsing, validating, manipulating, and displaying dates and times. It serves as a lightweight alternative to Moment.js, providing a similar API with a smaller footprint, enhancing efficiency and reducing bundle sizes.
Statistics
- Weekly downloads: 15.5 M
- Size: 664 KB
- Files: 447
Code Samples
Creating a Date Object
import dayjs from 'dayjs';
const now = dayjs(); // Current date and time
const tomorrow = dayjs().add(1, 'day');
const yesterday = dayjs().subtract(1, 'day');
Formatting a Date
const formattedDate = now.format('YYYY-MM-DD HH:mm:ss'); // Output: 2024-01-26 18:40:00
Comparing Dates
const isFuture = tomorrow.isAfter(now); // true
const isPast = yesterday.isBefore(now); // true
Modifying Dates
const nextWeek = now.add(7, 'day');
const lastMonth = now.subtract(1, 'month');
Pros
- Performance: Efficient and lightweight, often outperforming Moment.js.
- Small size: Reduces bundle size, contributing to faster page loads.
- Chainable: Methods can be chained for a fluent style.
- Internationalization: Supports multiple languages and locales.
- Plugin system: Extendable with plugins for additional functionality.
Cons
- Limited features: May not offer the extensive feature set of Moment.js.
- Community support: Smaller community compared to Moment.js.
- Breaking changes: Potential for breaking changes as the library evolves.
33. Cypress
Cypress stands as a comprehensive end-to-end testing framework, specifically designed for web applications. It enables developers to write and execute tests that directly interact with the application in a browser, ensuring features function as intended from a user’s perspective.
Statistics
- Weekly downloads: 4.4 M
- Size: 7.2 M
- Files: 862
Code Samples
Basic Test
describe('Login functionality', () => {
it('allows users to log in with valid credentials', () => {
cy.visit('/login');
cy.get('#username').type('johndoe');
cy.get('#password').type('password123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard'); // Assert successful login
});
});
Handling Asynchronous Actions
it('waits for data to load before asserting', () => {
cy.visit('/products');
cy.get('.product-list').should('be.empty'); // Initially empty
cy.get('#load-more-button').click();
cy.get('.product-list').should('have.length.greaterThan', 0); // Assert products loaded
});
Mocking and Stubbing
it('mocks API requests for offline testing', () => {
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers');
cy.visit('/users');
cy.wait('@getUsers'); // Wait for mocked response
cy.get('.user-list').should('have.length', 3); // Assert users displayed
});
Pros
- Direct interaction: Tests run directly in the browser, closely mimicking user actions.
- Readability: Emphasizes clear and concise test syntax, enhancing maintainability.
- Headless testing: Supports execution without a visible browser, enabling integration into CI/CD pipelines.
- Debugging: Offers powerful debugging tools for pinpointing test failures.
- Video recording: Captures visual evidence of test execution for analysis and sharing.
- Ecosystem: Integrates seamlessly with various testing tools and frameworks.
Cons
- Performance: Can be slower than unit tests due to browser interactions.
- Setup: Requires initial configuration and setup.
- Learning curve: Understanding Cypress concepts and best practices might necessitate some effort.
34. Winston
Winston is a flexible and versatile logging library for Node.js applications. It empowers developers to log messages across various transport mechanisms, including consoles, files, cloud services, and third-party services, enabling comprehensive monitoring and debugging.
Statistics
- Weekly downloads: 9.5 M
- Size: 268 K
- Files: 38
Code Samples
Basic Logging
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'app.log' }),
],
});
logger.info('Application started');
logger.error('Error occurred:', error);
Custom Transports
const winstonRotatingFile = require('winston-daily-rotate-file');
logger.add(new winstonRotatingFile({ filename: 'error.log', level: 'error' }));
Multiple Levels
logger.debug('Debug message');
logger.info('Informational message');
logger.warn('Warning message');
logger.error('Error message');
Pros
- Flexibility: Supports various transports, formats, and levels.
- Customizable: Adaptable to specific logging needs.
- Performance: Optimized for efficiency in production environments.
- Ecosystem: Integrates with numerous third-party logging tools and services.
Cons
- Configuration: Can require some setup for optimal usage.
- Dependencies: Certain transports might introduce additional dependencies.
35. Express-rate-limit
Express-rate-limit serves as a middleware for Express.js applications, designed to protect against excessive requests and potential abuse. It enforces rate limits, ensuring a controlled flow of incoming traffic and safeguarding resources.
Statistics
- Weekly downloads: 1.4 M
- Size: 106 K
- Files: 9
Code Samples
Basic Usage
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
});
app.use(limiter);
Customizing Limits
// Limit based on IP address
app.get('/api/resource', limiter({ max: 10 }), (req, res) => {
// ...
});
// Limit based on route
app.get('/login', limiter({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 requests per hour
}), (req, res) => {
// ...
});
Handling Rate Limit Exceeded
app.use((req, res, next) => {
if (req.headers['x-ratelimit-remaining'] === '0') {
return res.status(429).json({ message: 'Too many requests' });
}
next();
});
Pros
- Simple integration: Easy to incorporate into Express applications.
- Flexible configuration: Supports various rate-limiting strategies.
- Storage options: Can use memory, Redis, or other stores for persistence.
- Customizable responses: Allows tailored responses to rate-limited requests.
Cons
- Potential for false positives: IP-based rate limiting might impact legitimate users behind shared IP addresses.
- Additional complexity: Introduces configuration and storage considerations.
- Performance overhead: Rate-limiting logic can slightly impact request processing time.
36. Semver
The semver package provides utilities for parsing, comparing, and manipulating semantic version strings. These strings adhere to a specific format (MAJOR.MINOR.PATCH) that communicates compatibility and indicates the significance of changes in software releases.
Statistics
- Weekly downloads: 251 M
- Size: 93 K
- Files: 51
Code Samples
Parsing a Version String
const semver = require('semver');
const version = '1.2.3-beta.4+build.5';
const parsedVersion = semver.parse(version);
console.log(parsedVersion); // Output: { major: 1, minor: 2, patch: 3, prerelease: ['beta', '4'], build: ['build', '5'] }
Comparing Versions
semver.compare('1.2.3', '1.2.2'); // Output: 1 (greater than)
semver.lt('2.0.0', '1.10.0'); // Output: true (less than)
semver.satisfies('1.2.4', '>=1.2.0 <2.0.0'); // Output: true (satisfies range)
Creating Version Ranges
const range = semver.range('~1.2.0'); // Matches 1.2.x releases
const valid = semver.satisfies('1.2.3', range); // Output: true
Incrementing Versions
semver.inc('1.2.3', 'minor'); // Output: 1.3.0
semver.inc('1.2.3-beta.1', 'prerelease'); // Output: 1.2.3-beta.2
Pros
- Standardization: Enforces consistent versioning practices.
- Dependency management: Facilitates compatibility checks and conflict resolution.
- Version ranges: Enables flexible dependency specifications.
- Change communication: Clearly conveys the significance of updates.
- Wide adoption: Supported by numerous tools and registries.
Cons
- Limited flexibility: Strict adherence might not always align with release strategies.
- Potential for confusion: Misunderstanding of versioning rules can lead to issues.
37. Superagent
Superagent offers a lightweight, progressive client-side HTTP request library with a flexible API, supporting both browser and Node.js environments. It streamlines interactions with web servers and APIs, making it a popular choice for various web development tasks.
Statistics
- Weekly downloads: 7.3 M
- Size: 539 K
- Files: 20
Code Samples
Basic GET Request
const request = require('superagent');
request
.get('https://api.example.com/users')
.then(response => {
console.log(response.body);
})
.catch(error => {
console.error(error);
});
POST Request with Data
request
.post('https://api.example.com/users')
.send({ name: 'John Doe', email: 'johndoe@example.com' })
.then(response => {
console.log(response.body);
});
Handling Headers and Authentication
request
.get('https://api.example.com/protected-resource')
.set('Authorization', 'Bearer YOUR_API_TOKEN')
.then(response => {
// ...
});
Chaining Requests
request
.get('https://api.example.com/posts')
.then(response => {
const postId = response.body[0].id;
return request.get(`https://api.example.com/posts/${postId}`);
})
.then(response => {
console.log(response.body);
});
Pros
- Browser-friendly: Functions seamlessly in both browsers and Node.js.
- Chainable API: Facilitates building complex request flows with ease.
- Promise-based: Simplifies asynchronous handling and error management.
- Customizable: Offers various options for tailoring requests and responses.
Cons
- Not as feature-rich: Might lack certain advanced features found in other libraries.
- Learning curve: Chaining syntax might require some practice.
38. Axios-retry
Axios-retry extends the popular Axios HTTP client library with automatic retry functionality. It enables applications to gracefully handle transient errors and network issues by automatically reattempting failed requests, enhancing resilience and reliability.
Statistics
- Weekly downloads: 2.1 M
- Size: 25 K
- Files: 9
Code Samples
Basic Usage with Axios
const axios = require('axios');
const axiosRetry = require('axios-retry');
const instance = axios.create();
axiosRetry(instance, {
retries: 3, // Retry up to 3 times
});
instance.get('https://api.example.com/data')
.then(response => {
// Handle successful response
})
.catch(error => {
// Handle errors after retries have been exhausted
});
Customizing Retry Behavior
axiosRetry(instance, {
retries: 5,
retryDelay: (retryCount) => Math.min(1000 * Math.pow(2, retryCount), 10000), // Exponential backoff
retryCondition: (error) => error.response.status >= 500, // Only retry on 5xx errors
});
Pros
- Improved resilience: Enhances application robustness against temporary network issues.
- Ease of use: Simple integration with Axios instances.
- Customizable: Configurable retry strategies and conditions.
Cons
- Dependency: Relies on the Axios library.
- Potential for abuse: Over-reliance on retries might mask underlying issues.
- Additional configuration: Retry logic requires careful setup.
39. JS-yaml
js-yaml serves as a YAML parser and stringifier for JavaScript, allowing seamless integration of YAML data into your Node.js applications. YAML is a human-readable data serialization format commonly used for configuration files, data exchange, and more.
Statistics
- Weekly downloads: 67 M
- Size: 405 K
- Files: 33
Code Samples
Parsing YAML
const yaml = require('js-yaml');
const data = yaml.safeLoad('name: John Doe\nage: 30\noccupation: Developer');
console.log(data); // Output: { name: 'John Doe', age: 30, occupation: 'Developer' }
Stringifying JavaScript objects
const obj = { name: 'Alice', hobbies: ['coding', 'reading'] };
const yamlString = yaml.safeDump(obj);
console.log(yamlString); // Output: 'name: Alice\nhobbies:\n - coding\n - reading\n'
Pros
- Ease of use: Straightforward parsing and stringifying methods.
- Safe mode: Protects against arbitrary code execution during parsing.
- Customizable: Offers options for handling schema validation and circular references.
Cons
- Performance: Can be slower than native JSON parsing for large datasets.
- Schema validation: Lacks built-in schema validation for enforcing data structure.
40. Mime-types
The mime-types package provides comprehensive utilities for working with MIME types (Multipurpose Internet Mail Extensions), which are standardized identifiers used to describe the content type of files and data over the internet. It’s essential for correctly handling various file formats in Node.js applications.
Statistics
- Weekly downloads: 41 M
- Size: 18 K
- Files: 5
Code Samples
Retrieving MIME Type by Extension
const mime = require('mime-types');
const mimeType = mime.lookup('filename.jpg'); // Output: 'image/jpeg'
Determining MIME Type by Content
const mimeType = mime.lookup(Buffer.from('Some text content')); // Output: 'text/plain'
Getting Extensions for a MIME Type
const extensions = mime.extensions('text/html'); // Output: ['html', 'htm']
Pros
- Extensive database: Covers a wide range of MIME types.
- Simple API: Easy to use with intuitive methods.
- Reliable: Accurately identifies MIME types for common file formats.
Cons
- Limited customizability: Cannot easily add unknown MIME types.
- Potential for errors: Might misidentify MIME types for less common or custom file formats.
That’s all for part 4.
The other parts in this series are: