👨🏼‍💻

khriztianmoreno's Blog

Home Tags About |

Posts with tag nodejs

Node.js Corepack: Version Control for Package Managers

2024-12-10
javascriptnodejstutorial

The Problem with Traditional Package ManagersFor years, npm has been the de facto package manager for Node.js. While it offers robust features like private package access and two-factor authentication, it also comes with some drawbacks:Slow installation speeds: npm can be notoriously slow, especially for large projects.Bloated node_modules directories: These directories can consume significant disk space.Complex configuration: npm's configuration can be intricate and challenging to master.To address these issues, alternative package managers like Yarn and pnpm have emerged. Yarn is known for its speed, while pnpm optimizes disk space by sharing dependencies.What is Corepack?Corepack is a new experimental feature in Node.js that allows you to manage the versions of package managers on your machines and environments. This means that all team members will use the same version of the package manager, which can help avoid compatibility issues.{ "name": "my-project", "scripts": { "start": "node index.js" }, "packageManager": "pnpm@8.5.1" // what is this? (Corepack) }Getting Started with CorepackTo enable Corepack, you can run the following command:corepack enableOnce Corepack is enabled, to set your project’s package manager, run corepack use. This command updates your package.json automatically.corepack use pnpm@8.x # sets the latest 8.x pnpm version in the package.json corepack use yarn@* # sets the latest Yarn version in the package.jsonWhy Use Corepack?Corepack can help you avoid compatibility issues by ensuring that all team members use the same version of the package manager. It can also help you manage package manager versions across different environments, such as development, production, and testing.The Future of CorepackCorepack represents a significant step forward in Node.js package management. By providing a unified interface for different package managers, it simplifies the development workflow and reduces the complexity associated with managing dependencies. As Corepack matures, it has the potential to become the standard way to manage Node.js packages.ReferencesCorepack DocumentationCorepack : Managing the Package ManagersHow To Use CorepackI hope this has been helpful and/or taught you something new!Profile@khriztianmoreno �

How to mock an HTTP request with Jest đź’»

2024-05-07
javascripttestingnodejsjestweb-development

Today I wanted to show you how to properly write a test.But anyone can figure out how to run a simple test. And here, we're looking to help you find answers you won't find anywhere else.So I thought we'd take things one step further.Let's run a more complex test, where you'll have to mock 1 or 2 parts of the function you're testing.[In case you're new here: mock is like using a stunt double in a movie. It's a way to replace a complicated part of your code (like calling an API) with something simpler that pretends to be real, so you can test the rest of your code easily.]MY testing framework of choice is Jest, because it makes everything so much easier:Zero Configuration: One of the main advantages of Jest is its zero-configuration setup. It is designed to work out of the box with minimal configuration, making it very attractive for projects that want to implement tests quickly and efficiently.Snapshot Testing: Jest introduced the concept of Snapshot Testing, which is particularly useful for testing UI components. It takes a snapshot of a component’s rendered output and ensures that it doesn’t change unexpectedly in future tests.Built-In Mocking and Spies: Jest comes with built-in support for mock functions, modules, and timers, making it easy to test components or functions in isolation without worrying about their dependencies.Asynchronous Testing Support: Jest supports asynchronous testing out of the box, which is essential for testing in modern JavaScript applications that often rely on asynchronous operations like API calls or database queries.Image descriptionDe todos modos, entremos en las pruebas:Step 1: Setting up your projectCreate a new project directory and navigate to itInitialize a new npm project: npm init -yInstall Jest: npm install --save-dev jestInstall axios to make HTTP requests: npm install axiosThese are the basic requirements. Nothing new or fancy here. Let's get started.Step 2: Write a function with an API callNow, let's say you log into some kind of application. StackOverflow, for example. Most likely, at the top right you'll see information about your profile. Maybe your full name and username, for example.In order to get these, we typically have to make an API call to get them. So, let's see how we would do that.Create a file called user.jsInside user.js, write a function that makes an API call. For example, using axios to retrieve user data:// user.js import axios from "axios"; export const getUser = async (userId) => { const response = await axios.get(`https://api.example.com/users/${userId}`); return response.data; };Step 3: Create the Test FileOkay, now that we have a function that brings us the user based on the ID we requested, let's see how we can test it.Remember, we want something that works always and for all developers.Which means we don't want to depend on whether the server is running or not (since this is not what we are testing).And we don't want to depend on the users we have in the database.Because in my database, ID1 could belong to my admin user, while in your database, ID1 could belong to YOUR admin user.This means that the same function would give us different results. Which would cause the test to fail, even though the function works correctly.Read on to see how we tackled this problem using mocks.Create a file called user.test.js in the same directory.Inside this file, import the function you want to test:import axios from "axios"; jest.mock("axios"); import { getUser } from "./user";Write your test case, mock the call, and retrieve mock data.test("should fetch user data", async () => { // Mock data to be returned by the Axios request const mockUserData = { id: "1", name: "John Doe" }; axios.get.mockResolvedValue({ data: mockUserData }); // Call the function const result = await getUser("1"); // Assert that the Axios get method was called correctly expect(axios.get).toHaveBeenCalledWith("https://api.example.com/users/1"); // Assert that the function returned the correct data expect(result).toEqual(mockUserData); });Step 4: Run the testAdd a test script to your package.json:"scripts": { "test": "jest" }Run your tests with npm test.Step 5: Review the resultsJest will display the result of your test in the terminal. The test should pass, indicating that getUser is returning the mocked data as expected.Congratulations, you now have a working test with Jest and Mocking.I hope this was helpful and/or made you learn something new!Profile@khriztianmoren

Hacks for effective Fullstack development with React and Node

2023-04-17
javascriptnodejsreact

Today I'm going to show you an optimal workflow for effective development with Node.js and React. If you've ever worked on a project with multiple package.json files, you might know the pain of juggling multiple terminal tabs, remembering which commands start which server, or handling CORS errors.Fortunately, there are some tools available that can alleviate some of these headaches.FullstackMonorepo Setup for React and NodeLet's say we're working on a monorepo with two package.json files: one is in a client directory for a React front-end powered by Create React App, and one is at the root of the repository for a Node back-end that exposes an API that our React app uses. Our React app runs on localhost:3000 and our Node app runs on localhost:8080. Both apps are started with npm startSince we have two package.json files, this means that in order to get our front-end and back-end up and running, we need to make sure we've run npm install and npm start in both the root directory and the client directory. Here's how we simplify this.1. Running two servers at the same timeOne improvement we can make to our development workflow is to add a build tool to run multiple npm commands at the same time to save us the trouble of running npm start in multiple terminal tabs. To do this, we can add an npm package called concurrently to the root of our project.In the root of our project, we will install it as a development dependency.npm install -D concurrentlyThen, in our root package.json scripts, we will update our start script to use them simultaneously.{ "name": "my-app", "version": "1.0.0", "main": "index.js", "scripts": { "start": "concurrently --kill-others-on-fail npm run server npm run client", "server": "node index.js", "client": "cd client && npm start" }, "dependencies": { "express": "^4.17.1" }, "devDependencies": { "concurrently": "^6.0.1" } }Now, we have three npm scripts:npm run server starts our Node application,npm run client runs npm start in the client directory to start our React application,npm start runs npm run server and npm run client at the same time.2. Installing front-end and back-end dependencies with a single commandAnother aspect of our workflow that we can improve is the installation of dependencies. Currently, we need to manually run npm install for every package.json file we have when setting up the project. Instead of going through that hassle, we can add a postinstall script to our root package.json to automatically run npm install in the client directory after the installation has finished in the root directory.{ "name": "my-app", "scripts": { ..., "postinstall": "cd client && npm install" }, }Now, when we install our monorepo, all we need to do to get it up and running is run npm install and then npm start at the root of the project. There is no need to enter any other directory to run other commands.3. Proxy API requests from the backendAs I mentioned earlier, our Node backend exposes the API endpoints that our React app will use. Let's say our Node app has a /refresh_token endpoint.Out of the box, if we tried to send a GET request to http://localhost:8080/refresh_token from our React app at http://localhost:3000, we would run into CORS issues. CORS stands for cross-origin resource sharing.Typically when you encounter CORS errors, it's because you're trying to access resources from another domain (i.e., http://localhost:3000 and http://localhost:8080), and the domain you're requesting resources from is not allowed.To tell the development server to proxy any unknown requests to our API server in development, we can set up a proxy in our React app's package.json file. In client/package.json, we'll add a proxy for http://localhost:8080 (where our Node app is running).{ "name": "client-app", "proxy": "http://localhost:8080", "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, ... }Now, if we restart the server and set a request to our Node app's /refresh_token endPoint (without http://localhost:8080) using fetch(), the CORS error should be resolved.fetch("/refresh_token") .then((res) => res.json()) .then((data) => console.log(data)) .catch((err) => console.error(err));Next time you're working on a monorepo project like this, try these three tips to streamline your development workflow.That's all folks! I hope this helps you become a better dev!Profile@khriztianmoren

Testing framework - Node.js

2020-04-17
javascripttestingnodejs

Once an application is running in production, we might be afraid to make changes. How do we know that a new feature, a fix, or a refactor won't break existing functionality?We can manually use our application to try to find bugs, but without maintaining an exhaustive checklist, it's unlikely we'll cover all possible failure points. And honestly, even if we did, it would take too long to run our entire application after every commit.By using a testing framework, we can write code that verifies our previous code still works. This allows us to make changes without fear of breaking expected functionality.But there are many different testing frameworks, and it can be difficult to know which one to use. Below, I will talk about three of them for Node.js:TapeAvaJestTAPEThis derives its name from its ability to provide structured results through TAP (Test Anything Protocol). The output of our runner is human-friendly, but other programs and applications cannot easily parse it. Using a standard protocol allows for better interoperability with other systems.Additionally, Tape has several convenience methods that allow us to skip and isolate specific tests, as well as verify additional expectations such as errors, deep equality, and throwing.Overall, the advantage of Tape is its simplicity and speed. It is a solid and straightforward harness that gets the job done without a steep learning curve.Here is what a basic test with Tape looks like:const test = require("tape"); test("timing test", (t) => { t.plan(2); t.equal(typeof Date.now, "function"); const start = Date.now(); setTimeout(function () { t.equal(Date.now() - start, 100); }, 100); });And if we run it, it looks like this:$ node example/timing.js TAP version 13 # timing test ok 1 should be strictly equal not ok 2 should be strictly equal --- operator: equal expected: 100 actual: 107 ... 1..2 # tests 2 # pass 1 # fail 1The test() method expects two arguments: the name of the test and the test function. The test function has the t object as an argument, and this object has methods we can use for assertions: t.ok(), t.notOk(), t.equal(), and t.deepEqual() to name a few.AVAAVA has a concise API, detailed error output, embraces new language features, and has process isolation to run tests in parallel. AVA is inspired by Tape's syntax and supports reporting through TAP, but it was developed to be more opinionated, provide more features, and run tests concurrently.AVA will only run tests with the ava binary. With Tape, we could run node my-tape-test.js, but with AVA we must first ensure that AVA is installed globally and available on the command line (e.g., npm i -g ava).Additionally, AVA is strict about how test files are named and will not run unless the file ends with "test.js".One thing to know about AVA is that by default it runs tests in parallel. This can speed up many tests, but it is not ideal in all situations. When tests that read and write to the database run simultaneously, they can affect each other.AVA also has some helpful features that make setup and teardown easier: test.before() and test.after() methods for setup and cleanup.AVA also has test.beforeEach() and test.afterEach() methods that run before or after each test. If we had to add more database tests, we could clear our database here instead of individual tests.Here is what an AVA test looks like:const test = require("ava"); test("foo", (t) => { t.pass(); }); test("bar", async (t) => { const bar = Promise.resolve("bar"); t.is(await bar, "bar"); });When iterating on tests, it can be useful to run AVA in "watch mode". This will watch your files for changes and automatically rerun the tests. This works particularly well when we first create a failing test. We can focus on adding functionality without having to keep switching to restart the tests.AVA is very popular and it's easy to see why. AVA is an excellent choice if we are looking for something that makes it easy to run tests concurrently, provides helpers like before() and afterEach(), and provides better performance by default, all while maintaining a concise and easy-to-understand API.JestIt is a testing framework that has grown in popularity alongside React.js. The React documentation lists it as the recommended way to test React, as it allows using jsdom to easily simulate a browser environment. It also provides features to help mock modules and timers.Although Jest is very popular, it is mainly used for front-end testing. It uses Node.js to run, so it is capable of testing both browser-based code and Node.js applications and modules. However, keep in mind that using Jest to test Node.js server-side applications comes with caveats and additional configuration.Overall, Jest has many features that can be attractive. Here are some key differences from Tape and AVA:Jest does not behave like a normal Node.js module.The test file must be run with jest, and several functions are automatically added to the global scope (e.g., describe(), test(), beforeAll(), and expect()). This makes test files "special" as they do not follow the Node.js convention of using require() to load jest functionality. This will cause issues with linters like standard that restrict the use of undefined globals.Jest uses its global expect() to perform checks, instead of standard assertions. Jest expects it to read more like English. For example, instead of doing something like t.equal(actual, expected, comment) with tape and AVA, we use expect(actual).toBe(expected). Jest also has smart modifiers that you can include in the chain like .not() (e.g., expect(actual).not.toBe(unexpected)).Jest has the ability to mock functions and modules. This can be useful in situations where it is difficult to write or change the code we are testing to avoid slow or unpredictable results in a test environment. An example in the Jest documentation is preventing axios from making a real HTTP request to an external server and instead returning a preconfigured response.Jest has a much larger API and many more configuration options. Some of them do not work well when testing for Node.js. The most important option we need to set is that testEnvironment should be "node". If we do not do this, jest uses the default configuration where our tests will run in a browser-like environment using jsdom.Here is what a Jest test looks like:const sum = require("./sum"); test("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); });Jest has a much larger API and offers more functionality than AVA or tape. However, the larger scope is not without drawbacks. When using Jest to test Node.js code, we have to:Agree to use undefined globals.Not use functions like mocked timers that interfere with packages like Mongoose.Configure the environment correctly so it does not run in a simulated browser by default.Consider that some code may run 20-30 times slower in Jest compared to other test runners.Many teams will choose Jest because they are already using it on the front-end and do not like the idea of having multiple test runners, or they like the built-in features like mocks and do not want to incorporate additional modules. Ultimately, these trade-offs must be made on a case-by-case basis.Other testing toolsThere are plenty of other testing tools like Istanbul, nyc, nock, and replay that we do not have space to go into here.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno ďż˝

Structuring the base of our NodeJS project

2020-01-10
nodejsexpressjsscaffoldingapijavascript

The idea of writing this article came from a need that arose at a meetup in the city I attended. Many of the people there were asking how they would know where the files that make up their project should be, such as: models, events, controllers, views, etc. Since in Node.JS there was no base way to do it and many of the visible examples never substantiated the reason why it was built that way.

NodeSchool Learn on your own

2020-01-02
javascriptnodejstutorial

When it comes to learning a new technology or understanding the features of a language, we always look for tutorials on the internet that teach us its concepts. That's why today I want to talk to you about NodeSchool.io, an initiative that aims to teach these topics through self-guided workshops.