<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rodion</title>
    <description>The latest articles on DEV Community by Rodion (@rodik).</description>
    <link>https://dev.to/rodik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1045868%2F05ac9102-1a58-419c-94f3-209ab4a837f0.jpeg</url>
      <title>DEV Community: Rodion</title>
      <link>https://dev.to/rodik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rodik"/>
    <language>en</language>
    <item>
      <title>Building an Instagram AI Agent with n8n</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Thu, 13 Mar 2025 14:22:38 +0000</pubDate>
      <link>https://dev.to/rodik/building-an-instagram-ai-agent-with-n8n-5d8n</link>
      <guid>https://dev.to/rodik/building-an-instagram-ai-agent-with-n8n-5d8n</guid>
      <description>&lt;h2&gt;
  
  
  1. About n8n
&lt;/h2&gt;

&lt;p&gt;When I first dove into AI agent development, I was searching for a flexible platform that could handle complex workflows without breaking the bank. That's when n8n caught my attention!&lt;/p&gt;

&lt;p&gt;Founded in 2019 by Jan Oberhauser, &lt;a href="https://n8n.io/" rel="noopener noreferrer"&gt;n8n&lt;/a&gt; (pronounced "n-eight-n") emerged as an open-source alternative to established workflow automation tools. Jan's vision was simple yet powerful: create a tool that gives users complete control over their automation while keeping their data private.&lt;/p&gt;

&lt;p&gt;With the explosion of LLMs and AI capabilities in recent years, n8n has evolved into a perfect playground for building sophisticated AI agents. The platform now offers native integrations with OpenAI, Anthropic, and other AI providers, making it easier than ever to incorporate intelligence into your workflows.&lt;/p&gt;

&lt;p&gt;What makes n8n stand out from Zapier and similar tools?&lt;/p&gt;

&lt;p&gt;🔓 Truly open-source: You can self-host n8n and even modify the code to suit your needs&lt;br&gt;
💰 Fair pricing: Their "fair-code" licensing model means affordable options for businesses of all sizes&lt;br&gt;
🛠️ Ultimate flexibility: Complex branching logic and error handling that other platforms struggle with&lt;br&gt;
🔐 Data privacy: Keep sensitive information on your own servers when self-hosting&lt;/p&gt;

&lt;p&gt;For my project, these advantages made n8n the clear choice for building an AI agent that could handle Instagram business communications autonomously.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. My Task: AI Agent to Automate Business Communications in Instagram 🤖
&lt;/h2&gt;

&lt;p&gt;I was eager to build an AI agent and needed a real-life challenge to tackle. The perfect opportunity came through my fiancé Kateryna Baieva's ceramic studio, &lt;a href="https://instagram.com/osonnya.ceramics" rel="noopener noreferrer"&gt;Osonnya Ceramics&lt;/a&gt;, where I help out whenever possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpam1sphkeoe8yieaa6b.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpam1sphkeoe8yieaa6b.JPG" alt="Osonnya Ceramics" width="800" height="1199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. Osonnya Studio and their communication challenges
&lt;/h3&gt;

&lt;p&gt;Like many small creative businesses, Osonnya Studio relies heavily on Instagram for client communications. The majority of their customers reach out through DMs with predictable questions:&lt;/p&gt;

&lt;p&gt;Workshop pricing and payment options&lt;br&gt;
Available time slots for upcoming classes&lt;br&gt;
Booking procedures and confirmation&lt;/p&gt;

&lt;p&gt;These conversations, while crucial for business, were consuming hours of Kate's time that could be better spent creating or teaching.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2. What I wanted to achieve
&lt;/h3&gt;

&lt;p&gt;After analyzing the communication patterns, I defined a clear set of goals for my AI agent:&lt;/p&gt;

&lt;p&gt;Handle routine Instagram messages automatically for specific delegated topics&lt;br&gt;
Notify an administrator when conversations require human intervention&lt;br&gt;
Integrate with external data sources (primarily Google Sheets) to access current pricing and availability&lt;br&gt;
Automatically update booking information in Google Sheets when workshops are successfully booked&lt;/p&gt;

&lt;p&gt;This narrowed scope gave me a perfect sandbox for experimenting with AI agent development while solving a genuine business problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Implementation 🛠️
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12ao5rrml79udr1ordbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12ao5rrml79udr1ordbd.png" alt="Instagram AI Agent Architecture" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To build an AI communication agent from scratch in Python would require significant development work. But with n8n, the process becomes much more streamlined.&lt;/p&gt;

&lt;p&gt;The architecture follows a simple flow:&lt;/p&gt;

&lt;p&gt;Customer sends a message via Instagram&lt;br&gt;
System identifies if the topic falls within AI's delegated responsibilities&lt;br&gt;
If yes, the AI agent processes the request using available tools and context&lt;br&gt;
The system generates an appropriate response and sends it back to the customer&lt;/p&gt;

&lt;p&gt;All messages are saved to provide better context for future conversations. When a topic falls outside the bot's scope, we notify a human operator (via Telegram in my implementation) and pause automated processing. This architecture allows for seamless handoff between AI and human agents as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Session Management
&lt;/h3&gt;

&lt;p&gt;Each communication with a customer is defined as a session. A customer can initiate multiple sessions over time, but only one session can be active at any given moment. Think of these as separate conversations about different topics with the same person.&lt;/p&gt;

&lt;p&gt;When our system recognizes a message as appropriate for AI handling, we mark the session as "AI" and let our agent manage the entire conversation thread until it ventures outside its zone of responsibility. If the topic shifts beyond the AI's capabilities, we mark the session as "human" and skip automated processing until that session closes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7g3d7lqp663uy7yys4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7g3d7lqp663uy7yys4k.png" alt="Sessions management in AI chatbot" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To implement this session management, I needed a database to track and store conversation states. MongoDB proved ideal for this purpose with its flexible document structure. When a customer message arrives, we first check for an active session with that particular customer. If none exists, we create a new one and classify it as either "human" or "AI" based on message content. All subsequent messages in that conversation thread are stored within the same record to maintain conversation context.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. AI Agent in n8n
&lt;/h3&gt;

&lt;p&gt;The AI Agent node is one of n8n's most powerful building blocks for creating intelligent workflows. This node acts as the brain of your automation, allowing you to connect large language models (like GPT-4 or Claude) directly to your workflow with minimal coding.&lt;/p&gt;

&lt;p&gt;What makes the AI Agent node particularly valuable is its ability to maintain conversation context and use tools. You can define custom functions and data sources that your AI can reference when generating responses, such as pulling pricing information from Google Sheets or checking availability in a calendar. The node handles all the complex prompt engineering behind the scenes, letting you focus on what matters - building useful workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivknjnq390xixuygm574.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivknjnq390xixuygm574.png" alt="AI Agent Node in n8n" width="800" height="873"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the Osonnya Studio project, I configured the AI Agent with specific instructions about ceramic workshops, pricing structures, and appropriate response styles. By connecting it to our session database and external data sources, the agent can provide accurate, contextual responses that feel natural to customers while maintaining business consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4. AI Agent Deployment 🚀
&lt;/h3&gt;

&lt;p&gt;Finally, I needed to find the best place to deploy my Agent to make it live. I could have used n8n cloud for this purpose, but it turned out to be too expensive for a small business like Osonnya Studio—at least 20 euros per month for the starter package.&lt;/p&gt;

&lt;p&gt;So I explored alternatives and eventually settled on Railway: for a hobby project of this size, it costs just 5 dollars a month, which fit my budget perfectly.&lt;/p&gt;

&lt;p&gt;BTW, to easily deploy my n8n project to Railway I used &lt;a href="https://railway.com/template/r2SNX_" rel="noopener noreferrer"&gt;this nice template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9odcfaoymw21qd6hxsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9odcfaoymw21qd6hxsk.png" alt="Railway n8n template" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of this base cost, there's the expense of each LLM call to Anthropic's Claude model, which I chose for this project. Since we weren't expecting thousands of messages daily—just tens at most—the API costs remained reasonable. Overall, this deployment strategy provided an excellent balance of performance and cost-effectiveness that I can definitely recommend for similar small-business automation projects! 💯&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Results and Conclusion 🎉
&lt;/h2&gt;

&lt;p&gt;Building an AI-powered Instagram agent with n8n was an engaging, hands-on exploration into combining low-code automation with modern AI capabilities. Leveraging n8n’s flexibility, seamless LLM integration, and robust session management allowed for rapid development and iterative refinement, ultimately delivering a practical and efficient automation solution tailored specifically to the communication needs of a small creative business.&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Mastering CRUD with NextJS</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Thu, 05 Dec 2024 16:35:32 +0000</pubDate>
      <link>https://dev.to/rodik/mastering-crud-with-nextjs-30if</link>
      <guid>https://dev.to/rodik/mastering-crud-with-nextjs-30if</guid>
      <description>&lt;p&gt;In web development, CRUD operations are fundamental building blocks and crucial for managing data. They are ubiquitous in virtually every application, from simple websites to complex enterprise solutions. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;NestJS Boilerplate&lt;/a&gt; users have already been able to evaluate and use a powerful new tool - CLI, which allows you to automatically create resources and their properties. With this tool, you can make all CRUD operations and add the necessary fields to them without writing a single line of code manually. Meanwhile, as we have repeatedly announced, the BC Boilerplates ecosystem includes a fully compatible &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-React-Boilerplate&lt;/a&gt; to provide full functionality (which, in principle, can be a completely independent solution). Let’s now explore CRUD operations from the frontend perspective.&lt;/p&gt;

&lt;p&gt;In Next.js, a React framework with server-side rendering capabilities, these operations can be efficiently managed with features that enhance performance, SEO, and developer experience. Previously,  we published an article about &lt;a href="https://dev.to/rodik/an-effective-way-to-start-a-nextjs-project-5kn"&gt;an Effective Way To Start a NextJS Project&lt;/a&gt;, and now we want to go further and analyze the details and nuances of working with the APIs in Next.js.&lt;/p&gt;

&lt;p&gt;As we know, the acronym CRUD stands for Create, Read, Update, and Delete. This concept represents the fundamental operations that can be performed on any data. Let's consider working with CRUD operations using the example of the administrative panel user, where functionalities like adding, editing, and deleting users are implemented, along with retrieving information about them. The custom React hooks discussed below, handling data processing in React Query, pagination, error management, and more, are already integrated into the &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-React-Boilerplate&lt;/a&gt;. Naturally, you can leverage this boilerplate directly. In the following sections, we’ll share our insights on implementing these features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Operation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Submitting data to create a new resource (e.g., user registration, adding a new product).&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt; Collect data from the form, send a POST request to the server, handle the response, and update the UI accordingly.&lt;/p&gt;

&lt;p&gt;Let’s observe an example. Making a POST request to the API is incorporated creating a new user. In the snippet below the &lt;code&gt;usePostUserService&lt;/code&gt; hook is used to encapsulate this logic. We’ve specified the data structure for creating a new user by defining the request and response types but omit this part here to help you focus. You can see more detailed information or a more complete picture in the repository &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-React-Boilerplate&lt;/a&gt; because this and all the following code snippets are from there.&lt;br&gt;
So, we’ll create a custom hook &lt;code&gt;usePostUserService&lt;/code&gt; that, uses the &lt;code&gt;useFetch&lt;/code&gt; hook to send a POST request. It takes user data as input and sends it to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function usePostUserService() {
  const fetch = useFetch();
  return useCallback(
    (data: UserPostRequest, requestConfig?: RequestConfigType) =&amp;gt; {
      return fetch(`${API_URL}/v1/users`, {
        method: "POST",
        body: JSON.stringify(data),
        ...requestConfig,
      }).then(wrapperFetchJsonResponse&amp;lt;UserPostResponse&amp;gt;);
    },
    [fetch]
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;wrapperFetchJsonResponse&lt;/code&gt; will be examined later in this article when we get to "error handling".&lt;/p&gt;

&lt;h3&gt;
  
  
  Read Operations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Fetching and displaying a list of resources or a single resource (e.g., fetching user profiles and product lists).&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt; Send a GET request to fetch data, handle loading and error states, and render the data in the UI.&lt;/p&gt;

&lt;p&gt;In our example, reading data involves making GET requests to the API to fetch user data. It can include fetching all users with pagination, filters, and sorting or fetching a single user by ID after defining the request (&lt;em&gt;UsersRequest&lt;/em&gt;) and response types (&lt;em&gt;UsersResponse&lt;/em&gt;). &lt;br&gt;
&lt;strong&gt;To fetch all users&lt;/strong&gt; in the custom  &lt;code&gt;useGetUsersService&lt;/code&gt; hook, we send a GET request with query parameters for pagination, filters, and sorting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useGetUsersService() {
  const fetch = useFetch();

  return useCallback(
    (data: UsersRequest, requestConfig?: RequestConfigType) =&amp;gt; {
      const requestUrl = new URL(`${API_URL}/v1/users`);
      requestUrl.searchParams.append("page", data.page.toString());
      requestUrl.searchParams.append("limit", data.limit.toString());
      if (data.filters) {
        requestUrl.searchParams.append("filters", JSON.stringify(data.filters));
      }
      if (data.sort) {
        requestUrl.searchParams.append("sort", JSON.stringify(data.sort));
      }

      return fetch(requestUrl, {
        method: "GET",
        ...requestConfig,
      }).then(wrapperFetchJsonResponse&amp;lt;UsersResponse&amp;gt;);
    },
    [fetch]
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For fetching a Single User&lt;/strong&gt; the &lt;code&gt;useGetUserService&lt;/code&gt; hook sends a GET request to fetch a user by ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useGetUserService() {
  const fetch = useFetch();
  return useCallback(
    (data: UserRequest, requestConfig?: RequestConfigType) =&amp;gt; {
      return fetch(`${API_URL}/v1/users/${data.id}`, {
        method: "GET",
        ...requestConfig,
      }).then(wrapperFetchJsonResponse&amp;lt;UserResponse&amp;gt;);
    },
    [fetch]
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update Operation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Editing an existing resource (e.g., updating user information, editing a blog post).&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt; Collect updated data, send a PUT or PATCH request to the server, handle the response, and update the UI.&lt;/p&gt;

&lt;p&gt;Let’s carry out updating an existing user, which involves sending a PATCH request to the API with the updated user data. For this, in the custom  &lt;code&gt;usePatchUserService&lt;/code&gt; hook, we send a PATCH request with the user ID and updated data after defining the request &lt;code&gt;UserPatchRequest&lt;/code&gt; and response types &lt;code&gt;UserPatchResponse&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function usePatchUserService() {
  const fetch = useFetch();
  return useCallback(
    (data: UserPatchRequest, requestConfig?: RequestConfigType) =&amp;gt; {
      return fetch(`${API_URL}/v1/users/${data.id}`, {
        method: "PATCH",
        body: JSON.stringify(data.data),
        ...requestConfig,
      }).then(wrapperFetchJsonResponse&amp;lt;UserPatchResponse&amp;gt;);
    },
    [fetch]
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Using PATCH instead of PUT is more advanced for partial data updates, while PUT is typically used for full resource updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete Operation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Removing a resource (e.g., deleting a user or removing an item from a list).&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt; Send a DELETE request to the server, handle the response, and update the UI to reflect the removal.&lt;/p&gt;

&lt;p&gt;In our next example, deleting a user involves sending a DELETE request to your API with the user ID. After defining the request (&lt;em&gt;UsersDeleteRequest&lt;/em&gt;) and response types (&lt;em&gt;UsersDeleteResponse&lt;/em&gt;) in the &lt;code&gt;useDeleteUsersService&lt;/code&gt; hook, a DELETE request is transmitted to remove the user by ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useDeleteUsersService() {
  const fetch = useFetch();
  return useCallback(
    (data: UsersDeleteRequest, requestConfig?: RequestConfigType) =&amp;gt; {
      return fetch(`${API_URL}/v1/users/${data.id}`, {
        method: "DELETE",
        ...requestConfig,
      }).then(wrapperFetchJsonResponse&amp;lt;UsersDeleteResponse&amp;gt;);
    },
    [fetch]
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These hooks abstract the complexity of making HTTP requests and handling responses, Using such an approach ensures a clean and maintainable codebase, as the data-fetching logic is encapsulated and reusable across your components. &lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving data in Next.js
&lt;/h2&gt;

&lt;p&gt;Ok, we have dealt with examples of processing CRUD operations, and let's take a closer look at the methods of obtaining data offered by Next.js because it, as a framework, adds its functions and optimizations over React. It is clear that Next.js, beyond CSR (&lt;em&gt;Client-Side Rendering&lt;/em&gt;), provides advanced features like SSR (&lt;em&gt;Server-Side Rendering&lt;/em&gt;), SSG (&lt;em&gt;Static Site Generation&lt;/em&gt;), built-in API routes, and hybrid rendering. So, let's discuss commonalities and differences in retrieving data in Next.js and React.&lt;/p&gt;

&lt;p&gt;As soon as React apps are purely client-side, so data fetching happens on the client after the initial page load. For dynamic pages that need to fetch data every time a page is loaded, it is &lt;em&gt;more suitable to use SSR&lt;/em&gt;, in this case, data is fetched on the server at the request time. &lt;br&gt;
In the case of SSG, which is suitable for static pages where data doesn’t change often, data is fetched at build time. So, the &lt;code&gt;getStaticProps&lt;/code&gt; method helps us to &lt;em&gt;fetch data at build time&lt;/em&gt; (SSG). If we need pages to be pre-render based on dynamic routes and the data fetched at build time, the &lt;code&gt;getStaticPaths&lt;/code&gt; method is allowing to do this. It is used in conjunction with the &lt;code&gt;getStaticProps&lt;/code&gt; to &lt;em&gt;generate dynamic routes at build time&lt;/em&gt;. It should be noted that starting with Next 14, we can make requests directly in components without these methods, which gives a more "React experience".&lt;/p&gt;

&lt;p&gt;Client-Side Data Fetching with &lt;a href="https://tanstack.com/query/v3" rel="noopener noreferrer"&gt;useQuery&lt;/a&gt; can be used for interactive components that need to fetch data on the client-side, with initial state hydrated from server-side fetched data. For fetching data that changes frequently or for adding client-side interactivity it is useful the &lt;a href="https://swr.vercel.app/" rel="noopener noreferrer"&gt;&lt;code&gt;useSWR&lt;/code&gt; strategy&lt;/a&gt;. It’s a React hook for client-side data fetching with caching and revalidation. It allows fetching data on the client side, usually after the initial page load. Nevertheless, it does not fetch data at build time or on the server for SSR, but it can revalidate and fetch new data when required. &lt;/p&gt;

&lt;p&gt;To summarize the information about the methods above, we can take a look at the table that provides a comprehensive overview of the different data fetching methods in Next.js, highlighting their respective timings and use cases.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Data Fetching&lt;/th&gt;
&lt;th&gt;Timing&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;getStaticPaths&lt;/td&gt;
&lt;td&gt;Static Site Generation (SSG)&lt;/td&gt;
&lt;td&gt;At build time&lt;/td&gt;
&lt;td&gt;Pre-render pages for dynamic routes based on data available at build time.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;getStaticProps&lt;/td&gt;
&lt;td&gt;Static Site Generation (SSG)&lt;/td&gt;
&lt;td&gt;At build time&lt;/td&gt;
&lt;td&gt;Pre-render pages with static content at build time. Ideal for content that doesn't change frequently.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;getServerSideProps&lt;/td&gt;
&lt;td&gt;Server-Side Rendering (SSR)&lt;/td&gt;
&lt;td&gt;On each request&lt;/td&gt;
&lt;td&gt;Fetch data on the server for each request, providing up-to-date content. Ideal for dynamic content that changes frequently.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;useQuery&lt;/td&gt;
&lt;td&gt;Client-Side Rendering (CSR)&lt;/td&gt;
&lt;td&gt;After the initial page load&lt;/td&gt;
&lt;td&gt;Fetch initial data server-side, hydrate, reduce redundant network requests, Background Refetching.&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;useSWR&lt;/td&gt;
&lt;td&gt;Client-Side Rendering (CSR)&lt;/td&gt;
&lt;td&gt;After the initial page load&lt;/td&gt;
&lt;td&gt;Fetch and revalidate data on the client-side, suitable for frequently changing data.&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Using React Query with Next.js
&lt;/h2&gt;

&lt;p&gt;React Query provides hooks for fetching, caching, synchronizing, and updating server-state, making it a great tool for handling data in both React and Next.js applications. Key benefits of its use are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Efficient data fetching:&lt;/strong&gt; It handles caching and background data synchronization, reducing redundant network requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic refetching:&lt;/strong&gt; Data can be automatically refetched in the background when it becomes stale, ensuring that the UI always displays the latest information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrated error handling:&lt;/strong&gt; Built-in support for handling errors and retries, making it easier to manage network failures and server errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic updates:&lt;/strong&gt; The &lt;code&gt;useMutation&lt;/code&gt; hook provides optimistic updates by providing an easy way to handle both the optimistic UI changes and rollback logic if the server request fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of integration with Next.js:&lt;/strong&gt; It can be seamlessly integrated with other Next.js data fetching methods like getStaticProps or getServerSideProps (if needed).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspection of query and mutation:&lt;/strong&gt; the ReactQueryDevtools tool provides the possibility of viewing the status, data, errors, and other details of all active queries and mutations and watching the query states update in real-time as your application runs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  QueryClientProvider
&lt;/h3&gt;

&lt;p&gt;QueryClientProvider is a context provider component that supplies a QueryClient instance to the React component tree. This instance is necessary for using hooks like useQuery.  To set it up, it needs to be placed at the root of your component tree and configure global settings for queries and mutations like retry behavior, cache time, and more. After this, it initializes the React Query client and makes it available throughout the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/[language]/layout.tsx
import ReactQueryDevtools from "@/services/react-query/react-query-devtools";
...
export default function RootLayout({
...
}) {
 return (
   &amp;lt;html lang={language} dir={dir(language)}&amp;gt;
     &amp;lt;body&amp;gt;
       &amp;lt;InitColorSchemeScript /&amp;gt;
       &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
         &amp;lt;ReactQueryDevtools initialIsOpen={false} /&amp;gt;
         …
       &amp;lt;/QueryClientProvider&amp;gt;
     &amp;lt;/body&amp;gt;
   &amp;lt;/html&amp;gt;
 );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, why should it be added to the project? It is beneficial for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized configuration for all queries and mutations.&lt;/li&gt;
&lt;li&gt;Easy to set up and integrate into existing React applications.&lt;/li&gt;
&lt;li&gt;Enables features like caching, background refetching, and query invalidation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  React Query Devtools
&lt;/h3&gt;

&lt;p&gt;The other important feature provided by React Query is React Query Devtools - a development tool for inspecting and debugging React Query states. It can be easily added to your application and accessed via a browser extension or as a component like in the example before.&lt;br&gt;
During development, React Query Devtools can be used for inspection of individual queries and mutations, understanding why certain queries are prefetching and monitoring the state of the query cache, and seeing how it evolves over time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pagination and Infinite Scrolling
&lt;/h2&gt;

&lt;p&gt;To implement pagination controls or infinite scrolling using features in libraries, useInfiniteQuery is a perfect fit. First, we generate unique keys for caching and retrieving queries in React Query. The by method here creates a unique key based on the sorting and filtering options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const usersQueryKeys = createQueryKeys(["users"], {
 list: () =&amp;gt; ({
   key: [],
   sub: {
     by: ({
       sort,
       filter,
     }: {
       filter: UserFilterType | undefined;
       sort?: UserSortType | undefined;
     }) =&amp;gt; ({
       key: [sort, filter],
     }),
   },
 }),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do this, we will use the &lt;code&gt;useInfiniteQuery&lt;/code&gt; function from React Query and take the useGetUsersService hook discussed above in the Read Operations section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const useUserListQuery = ({
 sort,
 filter,
}: {
 filter?: UserFilterType | undefined;
 sort?: UserSortType | undefined;
} = {}) =&amp;gt; {
 const fetch = useGetUsersService();

 const query = useInfiniteQuery({
   queryKey: usersQueryKeys.list().sub.by({ sort, filter }).key,
   initialPageParam: 1,
   queryFn: async ({ pageParam, signal }) =&amp;gt; {
     const { status, data } = await fetch(
       {
         page: pageParam,
         limit: 10,
         filters: filter,
         sort: sort ? [sort] : undefined,
       },
       {
         signal,
       }
     );

     if (status === HTTP_CODES_ENUM.OK) {
       return {
         data: data.data,
         nextPage: data.hasNextPage ? pageParam + 1 : undefined,
       };
     }
   },
   getNextPageParam: (lastPage) =&amp;gt; {
     return lastPage?.nextPage;
   },
   gcTime: 0,
 });


 return query;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;QueryFn here retrieves the user data based on the current page, filter, and sort parameters, and the &lt;code&gt;getNextPageParam&lt;/code&gt; function determines the next page to fetch based on the response of the last page. When the user scrolls or requests more data, &lt;code&gt;useInfiniteQuery&lt;/code&gt; automatically retrieves the next set of data based on the &lt;code&gt;nextPage&lt;/code&gt; parameter - this is how infinite scrolling happens. The cache time for the query is set by the gcTime parameter.&lt;/p&gt;

&lt;p&gt;Overall, React Query provides a comprehensive solution for managing and debugging server-state in React applications. QueryClientProvider ensures a centralized and consistent configuration for all queries and mutations, while ReactQueryDevtools offers powerful tools for inspecting and understanding query behavior during development. &lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;Implementing CRUD operations always requires proper error handling to ensure user-friendliness and application reliability. Server errors are usually associated with failed processing of a client request, errors in server code, resource overload, infrastructure misconfiguration, or failures in external services. For error handling, &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-react-boilerplate&lt;/a&gt; suggests using the &lt;code&gt;wrapperFetchJsonResponse&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function wrapperFetchJsonResponse&amp;lt;T&amp;gt;(
 response: Response
): Promise&amp;lt;FetchJsonResponse&amp;lt;T&amp;gt;&amp;gt; {
 const status = response.status as FetchJsonResponse&amp;lt;T&amp;gt;["status"];
 return {
   status,
   data: [
     HTTP_CODES_ENUM.NO_CONTENT,
     HTTP_CODES_ENUM.SERVICE_UNAVAILABLE,
     HTTP_CODES_ENUM.INTERNAL_SERVER_ERROR,
   ].includes(status)
     ? undefined
     : await response.json(),
 };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we covered the fundamental CRUD operations, explored data retrieval techniques in NextJS. We delved into using React Query to manage state, also outlining the capabilities of QueryClientProvider and ReactQueryDevtools for debugging and optimizing data retrieval. Additionally, we discussed implementing pagination and infinite scrolling to handle large datasets and addressed error handling to make your applications more resilient and ensure a smooth user experience.&lt;/p&gt;

&lt;p&gt;By following the examples and techniques outlined in this article, you should now be well-equipped to handle CRUD operations in your NextJS projects. Alternatively, you can use our &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-react-boilerplate&lt;/a&gt; template for your project. It has a fully compatible &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;Nestjs-boilerplate&lt;/a&gt; backend that implements the ability to work with CRUD operations in minutes, without a single line of code using the CLI, we've covered this in more detail here and here for entity relationships. Keep experimenting, stay updated with best practices, and welcome to try this boilerplate if you find it useful. &lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://bcboilerplates.com/#team" rel="noopener noreferrer"&gt;BC Boilerplates team&lt;/a&gt; is always seeking ways to enhance development. We’d love to hear your thoughts on &lt;a href="https://github.com/brocoders/extensive-react-boilerplate/discussions" rel="noopener noreferrer"&gt;GitHub discussions&lt;/a&gt; or in the comments below.&lt;/p&gt;

&lt;p&gt;Full credits for this article to Olena Vlasenko and Vlad Shchepotin 🇺🇦&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>Enhanced CLI Capabilities in NestJS Boilerplate</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Wed, 06 Nov 2024 10:07:35 +0000</pubDate>
      <link>https://dev.to/rodik/enhanced-cli-capabilities-in-nestjs-boilerplate-59jc</link>
      <guid>https://dev.to/rodik/enhanced-cli-capabilities-in-nestjs-boilerplate-59jc</guid>
      <description>&lt;p&gt;In our previous post about &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bc boilerplates&lt;/a&gt;, we discussed the integration of a CLI for generating resources like controllers, services, and entities. This feature significantly speeds up setup time, especially considering the use of hexagonal architecture, which provides the boilerplate with the flexibility to choose between document-oriented and relational databases (e.g., PostgreSQL with TypeORM) or even both simultaneously.&lt;/p&gt;

&lt;p&gt;The introduction of CLI commands has greatly simplified the generation of resources such as controllers, services, and entities. However, since the types available for new properties were limited to strings, numbers, and booleans, we received many requests for establishing relationships One-To-One, One-To-Many, Many-To-One, or Many-To-Many within the hexagonal architecture. Recognizing that modeling relationships between entities is essential in many backend projects, we understood the importance of enhancing the boilerplate CLI to support these features automatically. This not only addresses a widespread need but also aligns with our goal of further reducing setup time, enabling developers to focus on the more complex aspects of their applications. &lt;/p&gt;

&lt;p&gt;In a previous &lt;a href="https://dev.to/rodik/nestjs-boilerplate-update-new-features-and-improvements-934"&gt;post&lt;/a&gt; we demonstrated the use of the CLI to create endpoints through examples and wrote about future work on expanding property types. Now we're pleased to announce that this idea has been implemented, and the CLI in the latest version of the &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;NestJS Boilerplate &lt;/a&gt; offers significantly more capabilities for creating resources and handling relationships between objects — all without writing a single line of code.&lt;/p&gt;

&lt;p&gt;To create a new resource, use the following command, replacing &lt;code&gt;&amp;lt;name&amp;gt;&lt;/code&gt; with the desirable entity name (e.g., Publication):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run generate:resource:relational -- --name=&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate all necessary files (controller, module, service) in a new directory at &lt;code&gt;src/publication&lt;/code&gt;.&lt;br&gt;
To add a new field to this entity, use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run add:property:to-relational
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interactive mode will now prompt you to specify the entity to which the new field should be added and enter the name of the new property. After that, you will be prompted to select the field type. You can choose from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primitive types (string, number, boolean);&lt;/li&gt;
&lt;li&gt;Reference to another entity (e.g., relationships like  one-to-one, one-to-many);&lt;/li&gt;
&lt;li&gt;Duplication of data from another entity (if you want to store a &lt;em&gt;snapshot of data&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you select a reference to another entity, you’ll then choose from specific relationship types:  &lt;em&gt;one-to-one&lt;/em&gt;, &lt;em&gt;one-to-many&lt;/em&gt;, &lt;em&gt;many-to-many&lt;/em&gt;. Following these steps in interactive mode means you don’t have to worry about manual configuration, as it’s handled automatically. For more information on adding properties, you can check the &lt;a href="https://github.com/brocoders/nestjs-boilerplate/blob/main/docs/cli.md" rel="noopener noreferrer"&gt;documentation on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s go through the step-by-step commands useful for creating a database. Assume we're setting up a blog. We'll go through the CLI commands to show how, in just a few steps, you can build the database structure for a blog without writing any code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2wyvgksrwru6st3byig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2wyvgksrwru6st3byig.png" alt="the CLI commands step-by-step" width="800" height="1180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By combining object generation and relationships directly in the CLI,  we’ve eliminated the need to manually create template code. The boilerplate now allows you to create a fully functional backend with a basic design and CRUD operations in just minutes, without touching the code. This allows developers to focus on business logic and complex use cases instead of tedious setups.&lt;/p&gt;

&lt;p&gt;If you’re already using a previous version of the &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;NestJS Boilerplate&lt;/a&gt;, update to the latest version to take advantage of these new features. And if you’re a newcomer, you can also use this boilerplate to create your backend, as it will significantly simplify the development process.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>NestJS Boilerplate Update: New Features and Improvements</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Wed, 14 Aug 2024 15:27:56 +0000</pubDate>
      <link>https://dev.to/rodik/nestjs-boilerplate-update-new-features-and-improvements-934</link>
      <guid>https://dev.to/rodik/nestjs-boilerplate-update-new-features-and-improvements-934</guid>
      <description>&lt;p&gt;We are excited to share the latest news about our &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;NestJS Boilerplate&lt;/a&gt;! Recently, we’ve received many requests from you and the community regarding the following improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic removal of unnecessary functionality during project installation.&lt;/li&gt;
&lt;li&gt;The ability to add resources (controllers, services, entities, etc.) through the CLI, as this process was previously quite labor-intensive, especially considering the use of hexagonal architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These improvements have now been implemented😊 So let's take a closer look at how users of the boilerplate can take advantage of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  New CLI for Generating Resources
&lt;/h2&gt;

&lt;p&gt;We’ve added a CLI that allows you to create resources and their properties automatically. To demonstrate its use, let's look at an example of creating endpoints for a blog. We’ll start the backend using the &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;NestJS Boilerplate&lt;/a&gt; (in this example, we’ll use MongoDB). If you have any questions or difficulties with deploying the boilerplate itself, we recommend watching the video below, where all the steps are shown step by step. The video also covers the use of the CLI with a relational database.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/XZaceN5A6sU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;After launching the boilerplate, we already have routes for &lt;em&gt;Users&lt;/em&gt;, &lt;em&gt;Files&lt;/em&gt;, &lt;em&gt;Auth&lt;/em&gt;, and &lt;em&gt;Home&lt;/em&gt;, which can be seen in Swagger. To generate a new resource, we’ll use the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm run generate:resource:document -- --name=Publication


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After executing the command, a new module with the files &lt;code&gt;publication.controller.ts&lt;/code&gt;, &lt;code&gt;publication.module.ts&lt;/code&gt;, &lt;code&gt;publication.service.ts&lt;/code&gt;, as well as nested folders &lt;code&gt;domain&lt;/code&gt; and &lt;code&gt;infrastructure&lt;/code&gt;, where entities and other necessary files will be stored, will appear in the &lt;code&gt;src/publication&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpay86vmhbrkjk8iwemsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpay86vmhbrkjk8iwemsq.png" alt="a new module with the files in IDE"&gt;&lt;/a&gt;&lt;br&gt;
The newly created resource will also be added to Swagger, where you can see new endpoints for creating, retrieving all publications with pagination, retrieving one by &lt;em&gt;id&lt;/em&gt;, updating, and deleting publications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdln3hwxabw3cvfrpo02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzdln3hwxabw3cvfrpo02.png" alt="a new endpoints for creating in Swagger"&gt;&lt;/a&gt;&lt;br&gt;
At the time of generation, the model contains only the fields &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;createdAt&lt;/code&gt;, and &lt;code&gt;updatedAt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3nfvaaasrsakpngfs7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3nfvaaasrsakpngfs7o.png" alt="the fields of a new model"&gt;&lt;/a&gt;&lt;br&gt;
In our example, each blog publication is expected to have a title and body. To add the &lt;em&gt;"title"&lt;/em&gt; field, you need to use the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm run add:property:to-document


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In interactive mode, specify the name of the entity (&lt;code&gt;Entity name&lt;/code&gt;) where the property should be added (in our example, it's &lt;em&gt;Publication&lt;/em&gt;), enter the &lt;code&gt;Property name&lt;/code&gt;, choose the type as &lt;code&gt;string&lt;/code&gt;, and &lt;code&gt;add to DTO&lt;/code&gt; for further modification. As a result, we see the added property &lt;em&gt;"title"&lt;/em&gt; in the &lt;code&gt;dto&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt;, &lt;code&gt;schema&lt;/code&gt;, and &lt;code&gt;mapper&lt;/code&gt;. We then update Swagger and see that the Schema Example Value in both Parameters and Responses now includes the created field &lt;em&gt;{"title": "string"}&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;The next field we intend to add is &lt;em&gt;"body"&lt;/em&gt;. The steps to add it are similar to the previous ones and can be seen in the screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsl6qns45m4o8sonhfqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsl6qns45m4o8sonhfqk.png" alt="the steps to add a field "&gt;&lt;/a&gt;&lt;br&gt;
That's it, a new resource and its properties are created, and you can use it. Try working with the new API routes in Swagger.&lt;/p&gt;

&lt;p&gt;We’d like to emphasize once again that &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;nestjs-boilerplate&lt;/a&gt; uses a hexagonal architectural approach (ports and adapters), which makes it easy to change the database or other infrastructure without affecting the business logic. You can read more about this in the &lt;a href="https://github.com/brocoders/nestjs-boilerplate/blob/main/docs/architecture.md" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. Therefore, the commands for working with resources will differ when working with a relational database (e.g., PostgreSQL with TypeORM) or with two types of databases simultaneously.&lt;/p&gt;

&lt;p&gt;Thus, we created five CRUD endpoints and added the necessary fields without writing a single line of code manually!&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans and Improvements
&lt;/h2&gt;

&lt;p&gt;We continue to work on improving the &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bc boilerplates&lt;/a&gt;. For example, we are currently working on expanding the data types for fields, as only string, number, and boolean are available at the moment. In the future, users will be able to choose not only between primitive types but also references to other entities with the ability to create one-to-one, one-to-many, and many-to-many relations.&lt;/p&gt;

&lt;p&gt;We are always open to dialogue and would love to hear your ideas on how we can enhance our boilerplates. And your stars &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; inspire us to keep working!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>Experiencing the Migration from Cypress to Playwright</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Thu, 08 Aug 2024 13:45:49 +0000</pubDate>
      <link>https://dev.to/rodik/experiencing-the-migration-from-cypress-to-playwright-2e24</link>
      <guid>https://dev.to/rodik/experiencing-the-migration-from-cypress-to-playwright-2e24</guid>
      <description>&lt;p&gt;In our post about extensive-react-boilerplate &lt;a href="https://dev.to/rodik/bc-boilerplates-recent-updates-37eh"&gt;updates&lt;/a&gt;, we mentioned that we migrated e2e-testing from Cypress to Playwright. Now, let's delve a little deeper into this change.&lt;/p&gt;

&lt;p&gt;At the time of writing the automated tests, we had a small amount of functionality to cover and didn't face significant limitations when using Cypress. Yet, we decided to turn our attention to Playwright for several reasons. We wanted to explore the framework created by Microsoft and understand why it is gaining popularity. Additionally, similar to the case when we added MongoDB support, we received requests from the community and colleagues who wanted to start a project based on boilerplate with Playwright tests.&lt;/p&gt;

&lt;p&gt;As we began the process of migrating tests, we acknowledged that the volume of tests was insignificant. Therefore, we decided to rewrite the tests manually in order to familiarize ourselves with the new framework in more detail. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, we start discussing the documentation. We can confidently say that Cypress documentation surpasses Playwright. Cypress documentation is very detailed and contains numerous examples and tutorials. There is also an entire project on GitHub with examples for every action that can be performed on a typical website. Additionally, the Cypress community is larger in comparison to Playwright. Although experienced developers may be content with the information provided in Playwright’s documentation, less experienced developers may find learning Cypress more enjoyable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Moving on to setting up the configuration file. We don’t find significant differences between the two frameworks. We only need to configure the timeouts and the base URL. We also explored some new capabilities that Playwright offers in this regard, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting timeouts for each test, including test and Before/After hooks in config parameters

&lt;code&gt;timeout: 2 * 60 * 1000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;support of testing on WebKit, which is based on Apple Safari, whereas Cypress lacks such support&lt;/li&gt;
&lt;li&gt;Playwright also has the ability to start a local development server with your project before running tests, which can be easily implemented using the webServer parameter.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     webServer: {
       command: process.env.CI
         ? "npm run build:e2e &amp;amp;&amp;amp; npm run start"
         : "npm run dev",
       url: "http://127.0.0.1:3000",
       reuseExistingServer: !process.env.CI,
     },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we write our first test. The difference in syntax between the two frameworks is notable. Cypress uses chainable syntax and has its own implementation of asynchrony, while Playwright supports the ECMAScript 2015 standard (ES6) and works with convenient async/await construction for asynchronous functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a Playwright code sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  test("should be successful open page and check data is displayed", async ({
    page,
  }) =&amp;gt; {
    await page.getByTestId("profile-menu-item").click();
    await page.getByTestId("user-profile").click();
    await page.waitForURL(/\/profile/);
    await expect(page.getByTestId("user-name")).toHaveText(
      `${firstName} ${lastName}`
    );
    await expect(page.getByTestId("user-email")).toHaveText(email, {
      ignoreCase: true,
    });
    await page.getByTestId("edit-profile").click();
    await page.waitForURL(/\/profile\/edit/);
    await expect(page.getByTestId("first-name").locator("input")).toHaveValue(
      firstName
    );
    await expect(page.getByTestId("last-name").locator("input")).toHaveValue(
      lastName
    );
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is Cypress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it("should be successful open page and check data is displayed", () =&amp;gt; {
    cy.getBySel("PersonIcon").click();
    cy.getBySel("user-profile").click();
    cy.location("pathname").should("include", "/profile");
    cy.getBySel("user-name").should("contain.text", firstName + " " + lastName);
    cy.getBySel("user-email").should("contain.text", email);
    cy.getBySel("edit-profile").click();
    cy.location("pathname").should("include", "/profile/edit");
    cy.get('[data-testid="first-name"] input').should("contain.value", firstName);
    cy.get('[data-testid="last-name"] input').should("contain.value", lastName);
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When it comes to running the tests, we notice the architectural differences between the frameworks. Cypress executes commands inside the browser, which gives it easy access to important components such as DOM, local storage, and window objects. On the other hand, Playwright uses a client-server architecture and communicates with browsers via a WebSocket connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After rewriting all the tests, we ran them all and observed that by default, Playwright runs tests in parallel, providing this feature for free. In contrast, Cypress performs parallelization only for different machines, and it is a paid feature. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Running the same tests in both frameworks revealed that Playwright completed the tests more quickly than Cypress. &lt;br&gt;
We conducted the tests and found that Playwright completed them in 2.7 minutes&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbogwx5hs9wuk3l7tput.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbogwx5hs9wuk3l7tput.png" alt="screenshot of Playwright completed tests" width="800" height="313"&gt;&lt;/a&gt;&lt;br&gt;
…while Cypress took 3.82 minutes, showing a significant time difference in favor of Playwright.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjud0rxhqvmyk1ne8dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjud0rxhqvmyk1ne8dp.png" alt="screenshot of Cypress completed tests" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Considering all the above points, one might wonder why we decided to change the framework. Although we did not see significant benefits at that moment, we took into account the future of our project and potential projects that will be built on top of boilerplates from the &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bcboilerplates&lt;/a&gt; ecosystem. From this perspective, Playwright seemed more promising than Cypress due to its parallelization of tests, higher speed, the possibility of testing mobile applications, the ability to use programming languages other than JS and TS, and the backing of a major player like Microsoft.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>development</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>An effective way to start a NextJS project</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Thu, 27 Jun 2024 10:57:04 +0000</pubDate>
      <link>https://dev.to/rodik/an-effective-way-to-start-a-nextjs-project-5kn</link>
      <guid>https://dev.to/rodik/an-effective-way-to-start-a-nextjs-project-5kn</guid>
      <description>&lt;p&gt;Choosing a framework for starting a new project can be quite challenging, considering the many frameworks and tools available today. Developers who want to build high-performance and scalable web applications often choose Next.js over others. No wonder, since Next.js is a React framework created by Vercel, offers a comprehensive solution for building server-side rendered (SSR) and static web applications. Here are some of the key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-Side Rendering (SSR) and Static Site Generation (SSG):&lt;/strong&gt; Next.js supports both SSR and SSG, allowing developers to choose the best rendering method for their needs. SSR improves SEO and page load speed by rendering pages on the server, while SSG can pre-render pages at build time for faster performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Routing:&lt;/strong&gt; Next.js simplifies routing with its file-based routing system. By organizing your files and folders in the pages directory, you can automatically create corresponding routes, eliminating the need for an external router library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Performance:&lt;/strong&gt; Next.js comes with a host of performance optimizations out of the box, including code splitting, automatic static optimization, and image optimization, ensuring your application runs efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Starting from scratch can be time-consuming, especially when configuring essential features like authorization and CRUD operations. A proper approach is to use a ready-made boilerplate that includes these settings, allowing you to focus on building features rather than setting up the basics. By applying a ready-to-use Next.js boilerplate, you would get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time and Effort Savings:&lt;/strong&gt; a boilerplate provides a foundation with pre-configured settings, saving you from the hassle of initial setup and configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Practices:&lt;/strong&gt; experienced developers follow industry best practices when building boilerplates, ensuring your project starts on the right foot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Included Features:&lt;/strong&gt; built-in features such as authentication, routing, and state management, that a lot of boilerplates include, allowing you to hit the ground running.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting Started with a Next.js boilerplate
&lt;/h3&gt;

&lt;p&gt;Let's go step-by-step on how to start your project using a boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose a Boilerplate:&lt;/strong&gt; Select the boilerplate that best fits your requirements. In this review, we’ll use the &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;extensive-react-boilerplate&lt;/a&gt; as an example, because we use it in our company. In our &lt;a href="https://dev.to/rodik/top-12-battle-tested-react-boilerplates-for-2024-f6i"&gt;boilerplate overview article&lt;/a&gt;, we've provided the reasons behind its creation and implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clone the Repository:&lt;/strong&gt; Clone the boilerplate repository to your local machine using Git.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone --depth 1 https://github.com/brocoders/extensive-react-boilerplate.git my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install Dependencies:&lt;/strong&gt; Navigate to the project directory and install the necessary dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd my-app
npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configure Environment Variables:&lt;/strong&gt; Set up your environment variables for authentication and other configurations. To do this, copy the example environment file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp example.env.local .env.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the Development Server: Start the development server to see your project in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Customize Your Project:&lt;/strong&gt; With the boilerplate set up, you can now start building your features. The boilerplate provides a structure and essential configurations, allowing you to focus on the core functionality of your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Starting a project with Next.js offers numerous advantages, from server-side rendering to built-in routing and performance optimizations. Using a ready-made boilerplate can further accelerate your development process by providing pre-configured settings and best practices. By leveraging these tools, you can focus on what matters most: building a high-quality, scalable web application. In the next article, we'll delve into mastering CRUD operations in Next.js, providing you with the tools and knowledge to manage data effectively in your applications.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>How We Went from 46 to 99 Performance Score to Improve Our Website Speed</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Wed, 19 Jun 2024 09:31:22 +0000</pubDate>
      <link>https://dev.to/rodik/how-we-went-from-46-to-99-performance-score-to-improve-our-website-speed-2930</link>
      <guid>https://dev.to/rodik/how-we-went-from-46-to-99-performance-score-to-improve-our-website-speed-2930</guid>
      <description>&lt;p&gt;In 2018, Google announced that page speed from then on would be a ranking factor for both desktop and mobile search results. But it's not only ranking that is influenced by page speed. According to research, a 3-second page loading time means a 13% bounce rate, so the page speed doesn't only impact the search results but the user experience as well. &lt;/p&gt;

&lt;p&gt;So what do you do if your website is slow? Fixing website performance is not easy, which is why many companies do only minor optimizations just to keep their websites somewhere in search results. Our Brocoders's website ended up in the same situation. But unlike other businesses out there, we decided to eliminate every performance issue we had.  &lt;/p&gt;

&lt;p&gt;Keep reading to find out how we went from a 43 performance score to 99 and why (apart from being very proud of ourselves) having one of the fastest websites in the industry is worth the effort. &lt;/p&gt;

&lt;h2&gt;
  
  
  Our starting point: what was the problem and why we had to solve it
&lt;/h2&gt;

&lt;p&gt;The old Brocoders's site, live since 2011, served us well and we loved it very much as it was our first child. However, after several years we felt it was time for a change. That’s why in 2020 we decided to invest in a new website for our company. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzcp4y6aoqcb8vjqpwav.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzcp4y6aoqcb8vjqpwav.gif" alt="the old Brocoders's website view"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is what the old Brocoders's website looked like. Forever in our hearts.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By rebuilding our website, we aimed to achieve three major goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Increase conversion rates through faster page loads, better UX, and higher-quality content.&lt;/li&gt;
&lt;li&gt;Improve SEO and get better visibility in search engines.&lt;/li&gt;
&lt;li&gt;Improve our brand image by introducing new portfolio projects and client reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These goals guided us in deciding what features we had to add. Here is the essential functionality we came up with to achieve our goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New highly flexible web pages with a great look and feel to demonstrate our services, industries, technologies, and case studies.&lt;/li&gt;
&lt;li&gt;Updated blog post pages with a search option, tags, and categories.&lt;/li&gt;
&lt;li&gt;'Get in touch' and other forms to easily contact us. &lt;/li&gt;
&lt;li&gt;Numerous third-party integrations: Google Tag Manager, Cookiebot, and Hubspot to name a few.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, our goal was to build a secure static website. That’s why we chose GatsbyJS, a React framework that perfectly aligns with this goal.&lt;/p&gt;

&lt;p&gt;Why Gatsby, you ask? The secret sauce behind &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;@GatsbyJS&lt;/a&gt; is its ability to transform web development into a seamless and fast experience, weaving together pre-built HTML, CSS, and JavaScript during compilation. Developed with React, GatsbyJS also allowed us to create our own templates and components, using extensions from the React ecosystem. &lt;/p&gt;

&lt;p&gt;Another thing we had to care about for the new website was the API backend. Our choice fell on &lt;a href="https://www.gatsbyjs.com/" rel="noopener noreferrer"&gt;@Strapi&lt;/a&gt;, an open-source content management system. Not only did it allow us to customize our data schemas and extend API functionality to manage different types of content like text, images, and videos, but also save us a great amount of time and money – with its user-friendly admin panel and additional features we didn't have to build it all from scratch.&lt;/p&gt;

&lt;p&gt;Finally, for server requests, we used GraphQL. Perfect for collaboration with GatsbyJS, it lets us define the query structure and specify only the necessary fields and relationships, avoiding excessive data, which in turn leads to faster page loading time.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qeyraes7wajqomfi0qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qeyraes7wajqomfi0qa.png" alt="combination of technologies at our site"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The combination of technologies allowed us to achieve the perfect website stability and security&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Once done, the new website was there, gratifying us with higher rankings and renewed design. At first…But then, things gradually went downhill. After adding new pages, making numerous edits, and integrating third-party software, the codebase had become so heavy that our performance scores on Google's Lighthouse, a tool that analyzes web page speed, dropped drastically. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft41i1svi70m8vy7kb53p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft41i1svi70m8vy7kb53p.png" alt="Low lighthouse scores before we started"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Lighthouse scores that our website had before we started working on improvements&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With such a low score, Google wouldn’t rank us high. And even if it would, users wouldn’t wait until it loads. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ericsson.com/assets/local/mobility-report/documents/2016/ericsson-mobility-report-feb-2016-interim.pdf" rel="noopener noreferrer"&gt;Ericsson Research&lt;/a&gt; found that the level of stress from waiting for pages to load on mobile can be compared to watching a horror movie. When talking about performance, every second counts. We knew the pain, so we started improving our site’s performance right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving website performance step by step
&lt;/h2&gt;

&lt;p&gt;To achieve a significant performance boost, we analyzed the main website issues and started working on solving each of them. Here’s how we overcame the main challenges. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Accelerating network connections establishment by 2X
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gtmetrix.com/" rel="noopener noreferrer"&gt;GMetrics&lt;/a&gt;, a tool that shows website loading behavior, helped us identify resources from external domains that caused the longest connection times.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib2ybxxm0z5knguw3hnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib2ybxxm0z5knguw3hnq.png" alt="a diagram of downloading time"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;In purple, you see the time it took for a Domain Name Server to receive the request for a domain name's IP address, and in light green, you see server connection time. They took too long, so we accelerated them by reducing server connection time.&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;To speed up our website, we made a few technical optimizations. First, we used Resource Hints, a method involving HTML &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; attributes. These hints help the browser proactively establish connections before making requests for resources from the server. For resources from external domains, we incorporated DNS-prefetch and pre-connect hints that instruct the browser in advance to set up connections with specified domains. Consequently, when the browser needs resources from external domains, they load quicker since the initial setup is already done.&lt;/p&gt;

&lt;p&gt;For internal files like scripts and stylesheets, we applied "preloading" and "prefetch" hints. These hints ensure that when users move between pages on our site, the needed resources are loaded from their local cache, avoiding the need to fetch them from a distant server and making the process faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These optimizations helped us shorten *Largest Contentful Paint from 2,9 seconds to 0,8 and *First Contentful Paint from 1 second to 0,6, allowing the browser to load resources as quickly as possible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*LCP, a &lt;a href="https://developers.google.com/search/docs/appearance/core-web-vitals" rel="noopener noreferrer"&gt;Core Web Vitals metric&lt;/a&gt; that measures the time from when the user starts loading the page until the largest image appears in the viewport.&lt;/p&gt;

&lt;p&gt;*FCP, the time from when the user first accesses the page to when the content is rendered on the screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Achieving better font loading with fast text rendering
&lt;/h3&gt;

&lt;p&gt;On our website, we use both custom and open-source fonts. We host the open-source font "Manrope" from Google using the &lt;a href="https://fontsource.org/docs/getting-started/introduction" rel="noopener noreferrer"&gt;Fontsource&lt;/a&gt; collection as this method is the fastest way to access a font.&lt;/p&gt;

&lt;p&gt;However, there was a delay in loading the font package, even though the text was already retrieved from the HTML file. To avoid this disruption between text and font loading, our initial strategy was to show an invisible fallback font until our intended web font fully loads. It worked, but such an approach made the LCP metric longer, which is why we decided to change the strategy and opt for fast text rendering. &lt;/p&gt;

&lt;p&gt;Here is how it works. First, the browser finds an available font within the declared font family and uses it to render the text. Then, after loading the original font, the browser seamlessly swaps the first font with the required one. It’s the fastest way to load fonts but this approach poses risks of unexpected text shifts because of changes in font size. To address layout shifts and improve the Cumulative Layout Shift (CLS, a metric that measures how much the elements on a webpage move around unexpectedly as it loads), we applied &lt;a href="https://www.w3.org/TR/css-fonts-5/" rel="noopener noreferrer"&gt;CSS Fonts Module Level 5&lt;/a&gt;, a set of specifications that includes descriptors like &lt;em&gt;size-adjust&lt;/em&gt;, &lt;em&gt;descent-override&lt;/em&gt;, and &lt;em&gt;line-gap-override&lt;/em&gt;, that helped us avoid shifts in page content. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4sf85oqslg70lkwpjfd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4sf85oqslg70lkwpjfd7.png" alt="a diagram of downloading time with fonts approach"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;CSS Fonts Module Level 5 worked perfectly and now fonts change without any layout shift&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As a result, three metrics – First Contentful Paint, Largest Contentful Paint, and Cumulative Layout Shift improved. In particular, CLS was reduced from 0,143 to 0 seconds.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Improving the loading of images and video
&lt;/h3&gt;

&lt;p&gt;Another performance issue we had was enormous network payloads. Site testing revealed that we had to optimize the loading of images and videos, reducing their size without losing the quality.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwm6p0b4vn1knuo68g57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwm6p0b4vn1knuo68g57.png" alt="a diagram of all types of files"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://yellowlab.tools/" rel="noopener noreferrer"&gt;Yellow Lab Tools&lt;/a&gt; testing showed us that among all types of files videos and images overweighted our website the most&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;On the website, we use two types of images – vector images (in Scalable Vector Graphics (SVG) format) and raster images (in PNG and JPG formats). For each type we applied different optimization approaches.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1 Optimization of vector images
&lt;/h4&gt;

&lt;p&gt;Vector images are usually lightweight but the problem was that each image required a new HTTP request to load, which slowed down the performance. To address this, we decided to adopt inline SVG, a method that allows embedding the image directly into HTML. To achieve this, we adopted the &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-react-svg/" rel="noopener noreferrer"&gt;'gatsby-plugin-react-svg'&lt;/a&gt;, a Gatsby framework plugin that allowed us to abstract the process. &lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Optimization of raster images
&lt;/h4&gt;

&lt;p&gt;To accelerate the loading of PNG and JPG images, we converted them to the modern .webP format that ensures excellent compression with minimal quality loss. Similarly, we converted videos from .mp4 to .webM, ensuring more efficient compression and quality. Both .webP and .webM became our primary formats, but the old browser versions do not support them. To exclude this problem, we also left fallbacks to .png, .jpg, and .mp4 formats for browsers that don’t support .webP and .webM.&lt;/p&gt;

&lt;p&gt;Next up, we had to optimize image display for different devices. Mobile, tablets, laptops, or 4K monitors require images of different dimensions, so uploading the same image for both phones and 4K monitors only made our website longer to load. That’s why we employed adaptive graphics. We included different image options in the code, letting the browser pick the best one based on things like window size, screen resolution, network speed, and other factors. Using packages like &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-background-image/" rel="noopener noreferrer"&gt;'gatsby-background-image'&lt;/a&gt; and &lt;a href="https://www.gatsbyjs.com/plugins/gatsby-image/" rel="noopener noreferrer"&gt;'gatsby-image'&lt;/a&gt;, we created various image variants for different devices. In the &lt;a href="https://developer.chrome.com/docs/devtools/network/" rel="noopener noreferrer"&gt;Network tab&lt;/a&gt; screenshot below, you see the “Toggle device toolbar” where we switched between page display modes.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqgndx0o5ewcz73vdhn1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqgndx0o5ewcz73vdhn1.png" alt="a device toolbar"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is where we toggled a device toolbar, optimizing image loading for different devices&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vay7wymdda5jsalwm3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vay7wymdda5jsalwm3o.png" alt="loading process for the same file with reduced size optimized"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Here you can see the same file with reduced size optimized for different devices&lt;/em&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  3.3 Lazy loading for behind-the-scenes images
&lt;/h4&gt;

&lt;p&gt;Finally, we leveraged lazy loading with a blurred image effect to ensure images load only when the user sees them, optimizing the experience for users with limited data plans or slower internet connections. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboek61ri3wkz9q7xzadf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboek61ri3wkz9q7xzadf.png" alt="how the lazy loading of images works"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is how the lazy loading of images works&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The results were worth all the sweat – we significantly reduced the weight of the images on the website without sacrificing quality. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ikwaoooeepq81fmvnch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ikwaoooeepq81fmvnch.png" alt="a diagram from Yellow Lab Tools"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After improvements, Yellow Lab Tools shows that the video – not surprisingly – still takes a larger portion of the page's weight but the weight of images has significantly decreased (purple section)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The optimizations greatly improved user experience and metrics like CLS and LCP. Thanks to using image optimization plugins exclusively from the Gatsby ecosystem, we made it easier to optimize performance.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Optimizing caching
&lt;/h3&gt;

&lt;p&gt;We have to confess that caching, a technique where the web server retains a copy of the web page, was initially overlooked during the website development. It was truly a missed opportunity because effective caching would help us speed up the website and reduce server load. We decided to catch up with cache optimization but faced several specific challenges. &lt;/p&gt;

&lt;p&gt;Our goal was to fasten the loading of resources by keeping them saved for future visits instead of downloading them every time. To achieve this, we employed the 'cache-control' attribute in the HTTP header and set the time for how long the file should stay in the cache. But then another issue appeared. When we made updates to the website content or design, the changes wouldn't show up immediately because the browser kept using the old saved copy. &lt;/p&gt;

&lt;p&gt;How did we solve it? We added a hash to the file names, which updates with every file edit. This way, we could keep files in the cache for a long time but still make changes easily. As a result, the First Contentful Paint (FCP) metric went from high to medium.&lt;/p&gt;

&lt;p&gt;Now, we're considering another type of caching called browser caching. Unlike server caching, which requires a constant server connection and uses bandwidth to load the response, browser caching allows users to access the webpage without a network connection. But it also has its limitations –  if the user's device is running out of storage space, the browser might delete older stuff to make room for new things. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rvpshlh4pre29g1vria.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rvpshlh4pre29g1vria.png" alt="how caching works"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;For you to get a better idea, here is a comparison of how server-side caching and client-side caching work&lt;/em&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  5. Eliminating large JavaScript bundles
&lt;/h3&gt;

&lt;p&gt;Bundles are simply collections of files, usually JavaScript, CSS, and other assets, grouped in a single file for more efficient delivery. As our website grew in complexity, the size of our bundles continued to expand, overweighting the website. It was high time to identify problem areas and get rid of them. &lt;/p&gt;

&lt;p&gt;There are some handy tools for identifying and addressing problematic bundles. One of them, &lt;a href="https://bundlephobia.com/" rel="noopener noreferrer"&gt;Bundlephobia&lt;/a&gt;, gives insights into how much an NPM package contributes to bundle size, helping avoid too large collections of files. &lt;a href="https://github.com/wix/import-cost" rel="noopener noreferrer"&gt;Import Cost&lt;/a&gt;, a VSCode Extension, calculates the 'cost' of imported packages, helping to make informed decisions. As part of our optimization strategy, we've swapped out hefty JS libraries, such as replacing the widely-used 'classnames' package with the more efficient &lt;a href="https://github.com/lukeed/clsx/tree/4c9a55df075eb96d968e09e56cfa3ba57a5bbdca/bench" rel="noopener noreferrer"&gt;'clsx'&lt;/a&gt;, a faster and smaller drop-in replacement tailored to our website needs.&lt;/p&gt;

&lt;p&gt;Then, with the &lt;a href="https://github.com/webpack-contrib/webpack-bundle-analyzer" rel="noopener noreferrer"&gt;Webpack Bundle Analyzer&lt;/a&gt; plugin, we discovered problematic areas in bundles.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbpode7xpv2ygopufl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbpode7xpv2ygopufl0.png" alt="a Bundle Analyzer diagram before"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Bundle Analyzer highlighted our biggest bundles – client-location and map-with-flashing-dots&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;To break down these piles of files, we split big bundles into smaller parts using code-splitting and lazy-loading. Webpack's built-in code-splitting feature allowed us to transform the import command into a function that points to a file path instead of directly importing files. This creates a promise, which is like a commitment that the file will be loaded. When a similar structure appears in the code, this promise is fulfilled, and the file is uploaded.&lt;/p&gt;

&lt;p&gt;For non-critical view, HTML, and JS, we used dynamic imports which let us reduce the initial size of the webpage. Dynamic here means that the website decides whether to load additional files based on specific conditions, making sure it doesn't disrupt the user experience.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcs4cfrvufem6ntqjes6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcs4cfrvufem6ntqjes6.png" alt="a Bundle Analyzer diagram after"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Here’s the result of our optimizations: a streamlined loading with no isolated large bundles&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After splitting large bundles into smaller parts, we successfully lightened the page and eliminated all large file collections.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Minimizing HTML and CSS with code compression
&lt;/h3&gt;

&lt;p&gt;Code compression was a game-changer for us. By shrinking the code and removing unnecessary white spaces, we achieved smaller file sizes, lightning-fast downloads, and reduced bandwidth consumption. Now our server delivers website files in a gzip format, making it faster and significantly improving important metrics like FCP and FID (First Input Delay, a metric that quantifies the delay between the first click and the browser's response) from low to medium performance levels. &lt;/p&gt;

&lt;h3&gt;
  
  
  7. Code quality and memory leaks
&lt;/h3&gt;

&lt;p&gt;Finally, we reviewed our code and discovered some sneaky memory leaks. The problem was that objects were still in memory even when they were no longer needed, resulting in a cluttered space.&lt;/p&gt;

&lt;p&gt;To fix this, we applied two methods. If the event listener (a mechanism that awaits specific occurrences, such as user interactions or system events, and responds accordingly) is only needed once, we use the {only: true} parameter. It ensures that after the listener is triggered, it's automatically removed, preventing any memory issues. As for the second method, we made sure to explicitly remove event listeners using the removeEventListener() function. We did this before removing the element or when the listeners were no longer needed. This ensured a clean disconnection between elements and functions, avoiding memory leaks.&lt;/p&gt;

&lt;p&gt;Another thing related to addEventListener is using the { passive: true } parameter. We adopted it for scrolling, avoiding interface hiccups, and ensuring a smoother user experience. &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;useEffect(() =&amp;gt; {&lt;br&gt;
setScrolled(document.documentElement.scrollTop &amp;gt; 50);&lt;br&gt;
window.addEventListener('scroll', handleScroll, { passive: true });&lt;br&gt;
return () =&amp;gt; {&lt;br&gt;
document.body.style.overflowY = 'scroll';&lt;br&gt;
window.removeEventListener('scroll', handleScroll);&lt;br&gt;
};&lt;br&gt;
}, []);&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Before and after: how performance boost impacted our website&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Truth be told, our starting point was far from good – a website wrestling with sluggish load times and an overloaded server that drastically impacted the user experience. But armed with insights and optimization techniques, we did our best and received impressive results. Were we happy with them? Absolutely. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iu7vw86tbc88yw8y08d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5iu7vw86tbc88yw8y08d.png" alt="a screenshot on Lighthouse metrics"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insight&lt;/a&gt; handed us a shiny report card, showcasing significant improvements&lt;/em&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy44umhfi677w90hq2ado.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy44umhfi677w90hq2ado.png" alt="a screenshot on Core Web Vitals metrics"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The testing of the 28-day performance showed impressive results – all metrics were improved several times compared with the starting point&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All these improvements wouldn’t be possible without hours and days of dedicated work, but there was something else that greatly contributed to our success – the right tools. Some instruments saved us a lot of time during the work and we can’t help but mention them. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tools for performance optimization: PageSpeed Insights and Lighthouse
&lt;/h2&gt;

&lt;p&gt;In performance analysis and improvement, two Google instruments stand out: &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt; and &lt;a href="https://developer.chrome.com/docs/lighthouse/overview" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt;. These tools analyze various aspects of a web page to provide insights into its speed, user experience, and overall performance. Here are the metrics that they both consider: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt; - measures the time it takes for the largest content element (such as an image or text block) in the viewport to become visible to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Contentful Paint (FCP)&lt;/strong&gt; - measures the time it takes for the browser to render the first piece of content, such as text or an image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Input Delay (FID)&lt;/strong&gt; - measures the delay between a user's first interaction (such as clicking a button) and the browser's response to that interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cumulative Layout Shift (CLS)&lt;/strong&gt; - measures the sum of all individual layout shift scores that occur during the page's lifespan. A layout shift occurs when visible elements on a page move unexpectedly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interaction to Next Paint (INP)&lt;/strong&gt; - measures the time it takes for the browser to respond to user interactions (such as clicks or taps) by updating the visual content on the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to First Byte (TTFB)&lt;/strong&gt; - measures the time it takes for the browser to receive the first byte of data from the server after a request is made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Blocking Time (TBT)&lt;/strong&gt; - measures the total amount of time during which the main thread of the browser is blocked and unable to respond to user input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTI (Time To Interactive)&lt;/strong&gt; - measures the time it takes for a page to become fully interactive, meaning all necessary resources are loaded, and the page responds promptly to user input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FID (First Input Delay)&lt;/strong&gt; - measures the delay between a user's first interaction and the browser's response to that interaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use either of these tools to get insights into your website's performance. Lighthouse is more flexible and provides more detailed information. PageSpeed Insights, on the other hand, focuses on monitoring the performance of individual pages. &lt;/p&gt;

&lt;p&gt;The most commonly used, these two aren’t the only tools we recommend. Here are a couple of resources you’ll find useful in your performance optimization journey. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus tools for a deeper dive
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gtmetrix.com/" rel="noopener noreferrer"&gt;GTMetrix&lt;/a&gt;: a powerhouse that not only audits pages but also visualizes loading in a digestible format, making performance improvements tangible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yellowlab.tools/" rel="noopener noreferrer"&gt;Yellow Lab Tools&lt;/a&gt;: an insightful dashboard that classifies and evaluates dozens of metrics. It not only pinpoints issues but also provides detailed recommendations for optimization.&lt;/p&gt;

&lt;p&gt;These tools have been our guiding lights, helping us enhance performance and offer a superior user experience. Keep them in mind when you start to improve your website performance. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;If your business depends on the website, you can’t sleep on its performance. This factor can move you to the top of the Google results or, on the contrary, make your users leave for faster websites that give them a better experience. To help you avoid this scenario, we shared the story of our website transformation.&lt;/p&gt;

&lt;p&gt;Our performance odyssey isn't over as there will always be room for improvement. Lately, we've been noticing that GatsbyJS is experiencing not the best times and we are deliberating about switching to NextJS. But this is the subject of the other article.&lt;/p&gt;

&lt;p&gt;But like there are no identical businesses, there is no single instruction on how to improve your site performance. Every case is unique and you need to address the challenges that pose the issue in your case. Not an easy task to do, but we can help. If you want to give your website a performance boost, &lt;a href="https://brocoders.com/get-in-touch/" rel="noopener noreferrer"&gt;drop us a note&lt;/a&gt;, and we’ll see what we can do for you. &lt;br&gt;
`&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>performance</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>BC Boilerplates: Recent Updates</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Wed, 12 Jun 2024 10:25:39 +0000</pubDate>
      <link>https://dev.to/rodik/bc-boilerplates-recent-updates-37eh</link>
      <guid>https://dev.to/rodik/bc-boilerplates-recent-updates-37eh</guid>
      <description>&lt;p&gt;Over the past few months, BC Boilerplates have undergone several changes, covering important aspects from testing to automating installation processes.&lt;br&gt;
Below, we are pleased to present updates that will facilitate the development process of applications built on boilerplates from the &lt;a href="https://bcboilerplates.com/"&gt;BC Boilerplates ecosystem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;Extensive-react-boilerplate&lt;/a&gt; received the following updates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From now on,  e2e test development will ensure their stability through the Playwright tool (previously Cypress was used), which has excellent support from Microsoft. The ability to run parallel tests is provided, reducing execution time (in Cypress, this was available for an additional fee).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sign-Up Disablement: For convenient management of the registration mode offered by the Boilerplate,  it is now sufficient to specify the exact value of the env variable in the config file (true/false).&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{IS_SIGN_UP_ENABLED &amp;amp;&amp;amp; (
&amp;lt;Button
onClick={handleCloseNavMenu}
sx={{ my: 2, color: "white", display: "block" }}
component={Link}
href="/sign-up"
&amp;gt;
{t("common:navigation.signUp")}
&amp;lt;/Button&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Availability of a privacy policy page with contact information for any questions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;New features of &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;nestjs-boilerplate&lt;/a&gt; are related to the running tests in &lt;em&gt;watchAll&lt;/em&gt; mode and CLI usability:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tests.  The separate 'container' which by default runs in &lt;em&gt;watchAll&lt;/em&gt; mode was improved. Compared to the previous approach, it used to be necessary to run each time a container with e2e tests from the very beginning, but now it is running continuously in standby mode. If you change any test, you can run exactly the ones you need at that moment.&lt;/li&gt;
&lt;li&gt;CLI has been added to assist the installation process of your project. Now you have the option to choose a database for the backend (Postgres or Mongo), whereas previously, more than 60 files had to be manually changed. You can remove unnecessary features for your development with just a few clicks.&lt;/li&gt;
&lt;li&gt;CLI for resource generation. Automatic generation of domains, endpoints, repository, Swagger documentation, swagger documentation, and the necessary entity, providing resources and interfaces for the Extensive-react-boilerplate from the backend side.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some changes apply simultaneously to &lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;extensive-react-boilerplate&lt;/a&gt; and  &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;nestjs-boilerplate&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A section for editing the email address has been added to the user profile.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft300lwx7c7e14f9b8tuy.png" alt="email editing section" width="800" height="565"&gt;
It is important to note that the user must confirm their request to change their email address by responding to the confirmation sent to the new email address provided.&lt;/li&gt;
&lt;li&gt;A common CLI tool for automating tasks related to release-it package versions and publication has been added. It handles versioning of the existing code and generates an automatic changelog based on commits, using the specification rules for recording changes in conventional commits (refer to CHANGELOG.md).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fki0iuc024xpozye7pgth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fki0iuc024xpozye7pgth.png" alt="git commit histiry" width="694" height="378"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7v4wv6n6cksg1qqlt0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7v4wv6n6cksg1qqlt0l.png" alt="backend commits" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The proposed updates will significantly increase the convenience and efficiency of development and will become an even more powerful tool for creating and supporting modern SaaS solutions. We're always looking for ways to improve your development experience. Please,  let us know what you think and share your feedback via &lt;a href="https://bcboilerplates.com/#contacts"&gt;https://bcboilerplates.com/#contacts&lt;/a&gt; in the way you prefer.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nestjs</category>
      <category>react</category>
    </item>
    <item>
      <title>Top 12+ Battle-Tested React Boilerplates for 2024</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Mon, 29 Apr 2024 18:52:13 +0000</pubDate>
      <link>https://dev.to/rodik/top-12-battle-tested-react-boilerplates-for-2024-f6i</link>
      <guid>https://dev.to/rodik/top-12-battle-tested-react-boilerplates-for-2024-f6i</guid>
      <description>&lt;h2&gt;
  
  
  The matter of choosing the most popular web framework to start
&lt;/h2&gt;

&lt;p&gt;Recently, React has become one of the most beloved interface frameworks of all time. According to a 2023 survey, React.js is used by 40.58% of developers worldwide, making it &lt;a href="https://www.statista.com/statistics/1124699/worldwide-developer-survey-most-used-frameworks-web/" rel="noopener noreferrer"&gt;one of the most popular web frameworks&lt;/a&gt;. Developed by Facebook, React.js is also relied upon by other tech giants such as PayPal, Uber, Instagram, and Airbnb for their user interfaces. Undoubtedly, its widespread adoption and strong community support have been facilitated by React's combination of productivity, component-based architecture, and declarative syntax. This means developers are building projects on React more than ever before.&lt;/p&gt;

&lt;p&gt;The React library is non-opinionated by design, meaning that "out of the box", it doesn't include practically any additional features beyond the core functionality of defining and managing components. Therefore, it's easy to mess up without knowing best practices for prop passing, decomposing, structuring React application files, scaling the application as a whole, and other nuances. These pitfalls can be avoided by using a boilerplate that contains built-in functions and configurations, providing a comprehensive foundation with core tools and libraries, optimizing the development process, and allowing developers to focus on building their application logic rather than dealing with initial setup and configuration. In other words, it serves as a standardized starting point for initiating application development. Searching for 'react-boilerplate' on GitHub yields 44.8k repositories at the moment. The question arises regarding which template to choose for development, one that fits your application and is good for scalability and future maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of React boilerplates
&lt;/h2&gt;

&lt;p&gt;In the past,  the most commonly used way for starting React projects was create-react-app (CRA) - a popular and officially supported boilerplate by Facebook. However, the new React documentation, published on March 16, 2023, no longer recommends CRA as the best solution for creating React programs. Let's consider the alternatives, compare them, and decide on the best way to start a project. &lt;br&gt;
By delving into the various aspects of React boilerplates, let's consider the criteria by which they can be divided:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Libs and Configs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Minimalistic Boilerplates:&lt;/em&gt; provide basic configurations for a React project, including basic setups (such as Webpack, Babel, and ESLint). They assume that developers will add certain libraries and features as needed. The majority of boilerplates fall into this category.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Feature-Rich Boilerplates:&lt;/em&gt; come with pre-configured additional libraries and tools. These may include state management (e.g., Redux), routing (React Router), and testing, also may include basic UI components and pages, further speeding up development by providing common UI elements and layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authentication and Registration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Boilerplates with Auth and Registration:&lt;/em&gt; include components for login, signup, and user sessions.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Boilerplates without Auth:&lt;/em&gt;  leave authentication implementation to developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Full-Stack vs. Frontend-Only:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Full-Stack Boilerplates:&lt;/em&gt; provide a comprehensive solution for building web applications, covering both the frontend (React) and backend.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Frontend-Only Boilerplates:&lt;/em&gt; focus solely on the React interface. Developers need to integrate them with the desired server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UI Components Libraries:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Boilerplates with UI Components:&lt;/em&gt; include a full set of UI components that adhere to consistent design patterns (e.g., buttons, forms, modals).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Boilerplates without UI Components:&lt;/em&gt; leave the development of components entirely to the developer using the boilerplate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Paid vs. Free:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Free/Open-Source Boilerplates:&lt;/em&gt; freely available, community-supported, and often well-maintained.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Paid Boilerplates:&lt;/em&gt; some commercial templates offer additional features, premium support, or extended functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the above classification, it can be said that the most popular React boilerplates, such as Vite, Create React App (CRA), Create Next App, Razzle, include only the basic libraries and configurations necessary to start developing with React (Minimalistic Boilerplates).&lt;/p&gt;

&lt;h2&gt;
  
  
  React template selection criteria
&lt;/h2&gt;

&lt;p&gt;Deciding which boilerplate to use during development can be quite challenging because it's not just about creating an application but also about scaling and maintaining it afterward. So how to choose the appropriate solution from the given variety of existing boilerplates, and how do you choose them in general? Here are the key points we suggest paying attention to when choosing a boilerplate to start your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Support and Maintenance Options:&lt;/em&gt; Is the project regularly updated?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Performance Scores&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Code Quality&lt;/em&gt; (structure cleanliness, scalability, code organization)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Production Readiness:&lt;/em&gt; Is the project ready for production use right now?&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Availability of features&lt;/em&gt; such as authentication, routing, internationalization, form handling, testing, basic pages, and UI components - the list could go on, you just need to determine which ones are needed for your project implementation and look for them in the boilerplate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  React Project Scaffolding Tools
&lt;/h2&gt;

&lt;p&gt;The initial step in developing React applications typically involves choosing among Vite, Create React App, Create Next App, or Razzle as the foundation. They provide framework-like functionality, particularly regarding setting up the initial project structure, configuring build tools, and providing development servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;&lt;u&gt;&lt;strong&gt;Vite&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt; focuses on providing an extremely fast development server and workflow speed in web development. It uses its own ES module imports during development, speeding up the startup time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;&lt;u&gt;&lt;strong&gt;Create React App (CRA)&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt; abstracts away the complexity of configuring Webpack, Babel, and other build tools, allowing developers to focus on writing React code. It includes features such as hot module reloading for efficient development.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://Next.js" rel="noopener noreferrer"&gt;&lt;u&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt; is a React framework for building server-rendered and statically generated web applications. It configures Next.js projects with sensible defaults, including features like server-side rendering (SSR), file-based routing, and API routes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://razzlejs.org/" rel="noopener noreferrer"&gt;&lt;u&gt;&lt;strong&gt;Razzle&lt;/strong&gt;&lt;/u&gt;&lt;/a&gt; is a build tool created by Airbnb, which also simplifies server-side rendering. It abstracts away the complexity of configuring server-side rendering settings and allows developers to easily create versatile JavaScript applications. Razzle supports features like code splitting, CSS-in-JS, and hot module replacement, making it suitable for building React applications that require server-side rendering.&lt;/p&gt;

&lt;p&gt;The above-mentioned build tools are often referred to as React boilerplates. Since they only abstract the complexities of setup away, provide basic configurations, and optimize build workflows, they are not very functional and do not contain additional features themselves. Therefore, according to the classification provided above, we classify them as &lt;strong&gt;Minimal Boilerplates&lt;/strong&gt;. Essentially, they often serve as boilerplate templates, that is, they are great tools for creating more feature-rich React boilerplates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of selected Boilerplates
&lt;/h2&gt;

&lt;p&gt;Next, we will consider React boilerplates that do not charge a license fee and/or offer their features for money and also take into account the date of the last update (not more than six months ago). Based on this, we have taken into consideration 12 boilerplates*:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Contributors&lt;/th&gt;
&lt;th&gt;Last Commit Date&lt;/th&gt;
&lt;th&gt;Open Issues&lt;/th&gt;
&lt;th&gt;About&lt;/th&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;extensive-react-boilerplate&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;341&lt;/td&gt;
&lt;td&gt;8 contributors, supported and used by &lt;a href="https://brocoders.com/" rel="noopener noreferrer"&gt;Brocoders&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;23-11-2024&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Extensive React Boilerplate: &lt;br&gt;✔️NextJS ✔️Auth ✔️I18N ✔️MUI ✔️Forms&lt;br&gt;
&lt;a href="https://react-boilerplate-coral.vercel.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/kriasoft/react-starter-kit" rel="noopener noreferrer"&gt;React Starter Kit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;22.5k&lt;/td&gt;
&lt;td&gt;5 contributors&lt;/td&gt;
&lt;td&gt;15-02-2024&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The web's popular Jamstack front-end template for building web applications with React&lt;br&gt;
&lt;a href="https://github.com/kriasoft/react-starter-kit" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/gilbarbara/react-redux-saga-boilerplate" rel="noopener noreferrer"&gt;react-redux-saga-boilerplate&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;606&lt;/td&gt;
&lt;td&gt;6 contributors&lt;/td&gt;
&lt;td&gt;06-02-2024&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Starter kit with react-router, react-helmet, redux, redux-saga and styled-components&lt;br&gt;
&lt;a href="https://redux-saga.react-boilerplate.com/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/ixartz/Next-js-Boilerplate" rel="noopener noreferrer"&gt;Next-js-Boilerplate&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7k&lt;/td&gt;
&lt;td&gt;24 contributors&lt;/td&gt;
&lt;td&gt;05-04-2024&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Boilerplate and Starter for Next.js 14+ with App Router/Page Router, Tailwind CSS 3.4, TypeScript&lt;br&gt;
&lt;a href="https://nextjs-boilerplate.com/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Adrinlol/landy-react-template" rel="noopener noreferrer"&gt;landy-react-template&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1.1k&lt;/td&gt;
&lt;td&gt;1 contributor&lt;/td&gt;
&lt;td&gt;06-04-2024&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Landy is an open-source React landing page template designed for developers and startups&lt;br&gt;
&lt;a href="https://landy-web.netlify.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/eruptionjs/core" rel="noopener noreferrer"&gt;core&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;307&lt;/td&gt;
&lt;td&gt;6 contributors&lt;/td&gt;
&lt;td&gt;27-03-2024&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Boilerplate for React/Typescript, built based on Vite&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/hadrysm/nextjs-boilerplate" rel="noopener noreferrer"&gt;nextjs-boilerplate&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;1 contributor&lt;/td&gt;
&lt;td&gt;18-03-2024&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Next.js 14+ boilerplate with typescript, husky, lint-staged, eslint, prettier, jest, react-testing-library, storybook, ghaction and plop&lt;br&gt;
&lt;a href="https://nextjs-boilerplate-hadrysm.vercel.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/suren-atoyan/react-pwa" rel="noopener noreferrer"&gt;react-pwa&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;501&lt;/td&gt;
&lt;td&gt;6 contributors&lt;/td&gt;
&lt;td&gt;09-01-2024&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Starter kit for modern web applications&lt;br&gt;
&lt;a href="https://react-pwa.surenatoyan.com/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/wtchnm/Vitamin" rel="noopener noreferrer"&gt;Vitamin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;485&lt;/td&gt;
&lt;td&gt;5 contributors&lt;/td&gt;
&lt;td&gt;06-04-2024&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Opinionated Vite starter template&lt;br&gt;
&lt;a href="https://vitamin-wtchnm.vercel.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mickasmt/next-saas-stripe-starter" rel="noopener noreferrer"&gt;next-saas-stripe-starter&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;722&lt;/td&gt;
&lt;td&gt;1 contributor&lt;/td&gt;
&lt;td&gt;02-04-2024&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;An open-source SaaS Starter built using Next.js 14, Prisma, Planetscale, Auth.js v5, Resend, React Email, Shadcn/ui, Stripe and Server Actions&lt;br&gt;
&lt;a href="https://next-saas-stripe-starter.vercel.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/sungik-choi/gatsby-starter-apple" rel="noopener noreferrer"&gt;gatsby-starter-apple&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;3 contributors&lt;/td&gt;
&lt;td&gt;01-04-2024&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Gatsby blog starter kit with beautiful responsive design&lt;br&gt;
&lt;a href="https://gatsby-starter-apple.netlify.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;
&lt;/td&gt;

&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/sungik-choi/gatsby-starter-apple" rel="noopener noreferrer"&gt;fullstack-typescript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;359&lt;/td&gt;
&lt;td&gt;7 contributors&lt;/td&gt;
&lt;td&gt;28-03-2024&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt; FullStack React with TypeScript starter kit&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;as of April 2024&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison of boilerplates by features
&lt;/h2&gt;

&lt;p&gt;Now let's take a closer look at the features developers can get from using boilerplates and what else needs to be taken into account:&lt;br&gt;
&lt;strong&gt;API Integration:&lt;/strong&gt; Some templates may contain configurations for integrating with specific APIs or server services.&lt;br&gt;
&lt;strong&gt;State Management Solutions:&lt;/strong&gt; Options like Redux, MobX, Recoil, or built-in React state management. Also, it's hard to ignore asynchronous React Query.&lt;br&gt;
&lt;strong&gt;Testing Configuration:&lt;/strong&gt; Predefined testing setups or none of them at all.&lt;br&gt;
&lt;strong&gt;Authentication and Authorization:&lt;/strong&gt; Whether user authentication and authorization are prescribed and how they are handled, in particular, whether there is integration with certain authentication libraries;&lt;br&gt;
&lt;strong&gt;Internationalization (i18n) and Localization:&lt;/strong&gt; Providing the ability to support  multiple languages using libraries like react-i18next or react-intl.&lt;br&gt;
&lt;strong&gt;ESLint Rules Compliance:&lt;/strong&gt; Allows not only to detect or fix problems during code formatting but also to identify potential bugs.&lt;br&gt;
&lt;strong&gt;Styling Solutions:&lt;/strong&gt; The solution for using CSS modules, styled-components, or UI libs, which will ensure easy and effective reuse of styled components.&lt;br&gt;
&lt;strong&gt;Type Safety in the Project:&lt;/strong&gt; Using TypeScript to provide static typing during development, utilizing classes or modules to create more scalable and reliable applications.&lt;br&gt;
&lt;strong&gt;App Theme Selection:&lt;/strong&gt; Allowing users to switch between light and dark themes based on their preferences or automatic settings.&lt;br&gt;
&lt;strong&gt;Ready-made Form Components:&lt;/strong&gt; Providing components intended for reuse across forms, reducing code duplication, and promoting standardization. They may also include built-in validation and error handling, making development more reliable.&lt;br&gt;
&lt;strong&gt;UI Component Libraries:&lt;/strong&gt; Offering ready-made and customizable components, such as buttons and modal windows, that developers can easily integrate into their applications, saving time and effort on designing and coding these elements from scratch.&lt;br&gt;
We analyzed each boilerplate and obtained the following table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
   &lt;th&gt;&lt;/th&gt;
   &lt;th&gt;&lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;extensive-react-boilerplate&lt;/a&gt;&lt;/th&gt;
   &lt;th&gt;&lt;a href="https://github.com/kriasoft/react-starter-kit" rel="noopener noreferrer"&gt;React Starter Kit&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/gilbarbara/react-redux-saga-boilerplate" rel="noopener noreferrer"&gt;react-redux-saga-boilerplate&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/ixartz/Next-js-Boilerplate" rel="noopener noreferrer"&gt;Next-js-Boilerplate&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/Adrinlol/landy-react-template" rel="noopener noreferrer"&gt;landy-react-template&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/eruptionjs/core" rel="noopener noreferrer"&gt;core&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/hadrysm/nextjs-boilerplate" rel="noopener noreferrer"&gt;nextjs-boilerplate&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/suren-atoyan/react-pwa" rel="noopener noreferrer"&gt;react-pwa&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/wtchnm/Vitamin" rel="noopener noreferrer"&gt;Vitamin&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/mickasmt/next-saas-stripe-starter" rel="noopener noreferrer"&gt;next-saas-stripe-starter&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/sungik-choi/gatsby-starter-apple" rel="noopener noreferrer"&gt;gatsby-starter-apple&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://github.com/sungik-choi/gatsby-starter-apple" rel="noopener noreferrer"&gt;fullstack-typescript&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
   &lt;td&gt;Documentation&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Authentication features&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Social sign in&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Internationalization&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;User profile&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Forms&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Statement management&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Tests&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;UI components&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Eslint&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Paid&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Styled-components&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;TypeScript&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;Themes&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;+&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
   &lt;td&gt;UI Component&lt;/td&gt;
   &lt;td&gt;Material ui&lt;/td&gt;
   &lt;td&gt;Material ui&lt;/td&gt;
   &lt;td&gt;gilbarbara/ components&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;antd&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;Tailwind CSS&lt;/td&gt;
   &lt;td&gt;Material ui&lt;/td&gt;
   &lt;td&gt;Tailwind CSS&lt;/td&gt;
   &lt;td&gt;@radix-ui&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
   &lt;td&gt;Material ui&lt;/td&gt;
&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Description of boilerplates from the table
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;Extensive-react-boilerplate&lt;/a&gt;&lt;/strong&gt;. This React boilerplate is designed for all types of projects. It's not only fully compatible with the backend boilerplate &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;nestjs-boilerplate&lt;/a&gt; but also stands as an independent solution, which is one of its main advantages. This template offers a wide range of functionalities, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User authentication and authorization, including the possibility of using Google or Facebook accounts.&lt;/li&gt;
&lt;li&gt;Page private or public access settings.&lt;/li&gt;
&lt;li&gt;ESLint setup with custom rules to enhance code efficiency and cleanliness.&lt;/li&gt;
&lt;li&gt;Type safety to ensure the reliability of the written code.&lt;/li&gt;
&lt;li&gt;Project localization using a custom useLanguage hook.&lt;/li&gt;
&lt;li&gt;E2E testing support.&lt;/li&gt;
&lt;li&gt;Light or Dark Mode at the discretion of the user.&lt;/li&gt;
&lt;li&gt;A library of controlled components based on MUI, integrated with react-hook-form by default.  So, no longer a need to spend extra time connecting input fields to controllers.&lt;/li&gt;
&lt;li&gt;State management using React Query for handling asynchronous operations.&lt;/li&gt;
&lt;li&gt;User management functionalities (CRUD).&lt;/li&gt;
&lt;li&gt;Avatar selection and upload feature with dropzone capability.&lt;/li&gt;
&lt;li&gt;Support for Next.js framework (SSR) for improved application performance and SEO optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, from the above mentioned features, this boilerplate significantly reduces the startup time for your project (&lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;approximately 193 hours&lt;/a&gt;), making it a worthwhile consideration.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates with Auth and Registration, Frontend-Only (and has a fully compatible &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;backend boilerplate&lt;/a&gt;, thus can be used as  Full-Stack Boilerplates), Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/kriasoft/react-starter-kit" rel="noopener noreferrer"&gt;React-starter-kit&lt;/a&gt;&lt;/strong&gt;. A template for creating web applications based on React. It comes with pre-configured setups such as CSS-in-JS, Vitest, VSCode settings, Cloudflare support, and SSR. A connection to Firestore is used as a database.  It includes implementations of some UI components like a toolbar or sidebar based on Joy UI.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates with Auth and Registration, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/gilbarbara/react-redux-saga-boilerplate" rel="noopener noreferrer"&gt;React-redux-saga-boilerplate&lt;/a&gt;&lt;/strong&gt;. A starter project for creating a React application that uses Redux for state management. It provides support for Unit and End 2 End Testing, react-helmet, and uses the Emotion library for styling, simplifying CSS styling with JavaScript. It includes custom components like a header or footer implemented using styled functionality.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ixartz/Next-js-Boilerplate" rel="noopener noreferrer"&gt;Next-js-Boilerplate&lt;/a&gt;&lt;/strong&gt;. This boilerplate has a flexible code structure where you only need to select and save the necessary functionality. It supports integration with Tailwind CSS, authentication with Clerk, and is compatible with SQLite, PostgreSQL, and MySQL databases. Unit testing is done using Jest, and the Zod library is used for describing validation schemas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates with Auth and Registration, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Adrinlol/landy-react-template" rel="noopener noreferrer"&gt;Landy-react-template&lt;/a&gt;&lt;/strong&gt;. This boilerplate comes with multilingual support, smooth animations, and all content is stored in JSON files, allowing users to manage texts without prior knowledge of React.js. Contains a set of its own components (button, input, textarea, etc.) created based on styling HTML elements using styled-components.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Adrinlol/landy-react-template" rel="noopener noreferrer"&gt;Core&lt;/a&gt;&lt;/strong&gt;. A modern template was developed based on the fast project creation tool — Vite. It supports TypeScript for type safety and includes good configurations for ESLint, Prettier, CommitLint, Husky, and Lint-Staged.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Minimalistic Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/hadrysm/nextjs-boilerplate" rel="noopener noreferrer"&gt;Nextjs-boilerplate&lt;/a&gt;&lt;/strong&gt;. This React boilerplate uses Next.js for static page generation. It supports git message convention, component generation using Plop, and uses Tailwind CSS for styling organization. Has its Storybook for component documentation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Minimalistic Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/suren-atoyan/react-pwa" rel="noopener noreferrer"&gt;React-pwa&lt;/a&gt;&lt;/strong&gt;. A ready-made set to start your project from scratch. It consists of a minimalistic combination of core libraries, components, and utilities typically needed by developers when creating React applications. It contains its own HOC for error handling on the page and is developed based on Vite.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/wtchnm/Vitamin" rel="noopener noreferrer"&gt;Vitamin&lt;/a&gt;&lt;/strong&gt;. A starter project containing Tailwind CSS with a basic style reset and a Prettier plugin that automatically organizes your classes. For testing, tools such as Vitest, Testing Library, and Cypress are used, but it does not include React UI Component Libraries.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Minimalistic Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mickasmt/next-saas-stripe-starter" rel="noopener noreferrer"&gt;Next-saas-stripe-starter&lt;/a&gt;&lt;/strong&gt;. By using this boilerplate, you can extend the capabilities of your project with features like Next.js, Prisma, Planetscale, Auth.js v5, Resend, React Email, Shadcn/ui, and Stripe. It includes a library of components built using Radix UI and Tailwind CSS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Feature-Rich Boilerplates, Boilerplates with Auth and Registration, Full-Stack Boilerplates, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/sungik-choi/gatsby-starter-apple" rel="noopener noreferrer"&gt;Gatsby-starter-apple&lt;/a&gt;&lt;/strong&gt;. A template for creating applications with a nice responsive design and contains animations for a mobile menu. The basis for styling the used components is styled-components. The boilerplaite supports search engine optimization well and has RSS feed capabilities.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Minimalistic Boilerplates, Boilerplates without Auth, Frontend-Only, Free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/gilamran/fullstack-typescript" rel="noopener noreferrer"&gt;Fullstack-typescript&lt;/a&gt;&lt;/strong&gt;. This boilerplate is a full-stack application for quickly launching your project. It has a library of custom components based on Material UI, and axios is used for client-server communication. It does not support certain state management technologies like Redux, MobX, etc.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Categories:&lt;/em&gt; Minimalistic Boilerplates, Boilerplates without Auth, Full-Stack Boilerplates, Free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peculiarities of implementation of some features
&lt;/h2&gt;

&lt;p&gt;In general, React templates offer various implementation features aimed at speeding up and standardizing the development process. They include UI Component Libraries and encompass a general approach to styling, state management, and basic ESLint configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  React UI Component Libraries
&lt;/h3&gt;

&lt;p&gt;The implementation of functionalities in React boilerplates often revolves around modular development, where components are designed to be reusable and composable. Analyzing current libraries and according to this article, the following can be considered the most popular ones:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdt2ors84nvpelp4u1r5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdt2ors84nvpelp4u1r5k.png" alt="blocks with listed UI Libraries" width="624" height="247"&gt;&lt;/a&gt;&lt;br&gt;
We can say for sure, Material UI is currently the most popular library with 91.2k GitHub stars and more than 3 million weekly downloads. Thanks to its responsive web design (RWD) feature, you can be confident that your application will automatically adapt to various screens and devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling Solutions
&lt;/h3&gt;

&lt;p&gt;Styling solutions such as CSS modules, styled-components, or Sass are usually included in React boilerplates. They offer different approaches to styling components, providing flexibility and scalability while maintaining component encapsulation.&lt;/p&gt;

&lt;p&gt;Advantages of using styled-components as a styling solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The library automatically tracks components rendered on the page and applies only their styles.&lt;/li&gt;
&lt;li&gt;Automatically generates a unique class name for styles, ensuring no errors in class names.&lt;/li&gt;
&lt;li&gt;Styles are attached to specific components, simplifying the removal of CSS itself.&lt;/li&gt;
&lt;li&gt;Effortless dynamic styling (code examples below belong to &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bc-boilerplates&lt;/a&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const AvatarInputContainer = styled(Box)(({ theme }) =&amp;gt; ({
  display: "flex",
  position: "relative",
  flexDirection: "column",
  alignItems: "center",
  padding: theme.spacing(2),
  marginTop: theme.spacing(2),
  border: "1px dashed",
  borderColor: theme.palette.divider,
  borderRadius: theme.shape.borderRadius,
  cursor: "pointer",

  "&amp;amp;:hover": {
    borderColor: theme.palette.text.primary,
  },
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Using dynamic props of a component during styling. This ensures that the style is updated based on the value of the variable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const StyledCollapseBtn = styled("button")&amp;lt;ICollapse&amp;gt;(({ isOpen, theme }) =&amp;gt; ({
  justifySelf: "flex-end",
  color: COLOURS.black,
  backgroundColor: "transparent",
  border: "none",
  cursor: "pointer",
  paddingLeft: theme.spacing(2.5),
  position: "absolute",
  bottom:theme.spacing(3),
  left: isOpen ? "150px" : "unset",
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This allows for the reuse of styles from one component to another or for influencing one component over another (parent-child relationship).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Link = styled.a`
display: flex;
align-items: center;
padding: 5px 10px;
background: papayawhip;
color: #BF4F74;
`;

const Icon = styled.svg`
flex: none;
transition: fill 0.25s;
width: 48px;
height: 48px;

${Link}:hover &amp;amp; {
  fill: rebeccapurple;
}
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State Management
&lt;/h3&gt;

&lt;p&gt;State management is another important aspect that simplifies application state handling, providing scalability and maintainability, especially in complex applications. Usually, Redux, MobX, and Zustand come to mind when choosing a state management tool. However, they are client-side libraries, and compared to a tool like React Query, their application for storing asynchronous data may not be as efficient.&lt;/p&gt;

&lt;p&gt;React Query is a server-state library used in some boilerplates like &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bc-boilerplates&lt;/a&gt;. It is responsible not only for managing asynchronous operations between the server and the client but also provides ready-to-use functionality for searching, caching, and updating data in React and Next.js applications. With just a few lines of code, React Query replaces the boilerplate code used to manage cached data in your client state. &lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint Rules in Boilerplates
&lt;/h3&gt;

&lt;p&gt;The efficiency of using ESLint rules during the development of your project is also manifested in writing custom rules. Since ESLint has extensive functionality and flexibility, you can create not only formatting and rules but also consider internal project decisions. For example, working with forms, it is possible to control and warn developers about possible unnecessary renders, incorrect solutions when working with objects, or simply point out unused imports. For example, &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;extensive-react-boilerplate&lt;/a&gt; addresses such issues as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;warn about rules regarding incorrect usage of patterns
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
      {
        "selector": "ConditionalExpression[consequent.type=Literal][consequent.value=true][alternate.type=Literal][alternate.value=false]",
        "message": "Do not use \"condition ? true : false\". Simplify \"someVariable === 42 ? true : false \" to \"someVariable === 42\""
      },
      {
        "selector": "JSXElement[openingElement.name.property.name=Provider] JSXElement[openingElement.name.name]",
        "message": "Do not put your regular components inside Context \".Provider\". Create new component, for example ComponentProvider. Put Provider's logic to ComponentProvider. Render \"{children} instead of regular component. Wrap regular component via new ComponentProvider \". Example: \"src/services/auth/auth-provider\""
      },
      {
        "selector": "Property[key.name=/^(padding|margin|paddingLeft|paddingRight|paddingTop|paddingBottom|paddingVertical|marginLeft|marginRight|marginTop|marginBottom|marginVertical)$/][value.type=/^(Literal|UnaryExpression)$/]:not([value.value=\"0 !important\"]):not([value.value=\"0\"]):not([value.value=\"0 auto\"]):not([value.value=\"auto\"])",
        "message": "Use theme.spacing() instead of literal."
      },
      {
        "selector": "CallExpression[callee.name=/^(useQuery|useInfiniteQuery)$/] Property[key.name=queryKey]:not(:has(Identifier[name=key]))",
        "message": "Use key created via createQueryKeys function instead of your solution"
      },
      {
        "selector": "CallExpression[callee.name=refresh]",
        "message": "Do not use refresh() function for update or change result in react-query. Use \"queryClient.resetQueries\" or pass new filter data to queryKey."
      },
      {
        "selector": "ExpressionStatement[expression.callee.object.name=JSON][expression.callee.property.name=parse][expression.arguments.0.callee.object.name=JSON][expression.arguments.0.callee.property.name=stringify]",
        "message": "Do not use JSON.parse(JSON.stringify(...)) for deep copy. Use structuredClone instead."
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;inform about the possibility of uncontrolled renders
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
      {
        "selector": "VariableDeclaration[declarations.0.init.callee.name=useForm] ~ VariableDeclaration[declarations.0.init.callee.name=useFieldArray]",
        "message": "\"useFieldArray\" in main form component (which use \"useForm\") will re-render the whole form component. Move your useFieldArray's logic to separate component."
      },
      {
        "selector": "VariableDeclaration[declarations.0.init.callee.name=useForm] ~ VariableDeclaration[declarations.0.init.callee.name=useController]",
        "message": "\"useController\" in main form component (which use \"useForm\") will re-render the whole form component. Move your useController's logic to separate component."
      },
      {
        "selector": "VariableDeclaration[declarations.0.init.callee.name=useForm] ~ VariableDeclaration[declarations.0.init.callee.name=useFormContext]",
        "message": "\"useFormContext\" in main form component (which use \"useForm\") will re-render the whole form component. Move your useFormContext's logic to separate component."
      },
      {
        "selector": "VariableDeclaration[declarations.0.init.callee.name=useForm] ~ VariableDeclaration[declarations.0.init.callee.name=useFormState]",
        "message": "\"useFormState\" in main form component (which use \"useForm\") will re-render the whole form component. Move your useFormState's logic to separate component."
      },
      {
        "selector": "CallExpression[callee.name=useForm][arguments.length=0], CallExpression[callee.name=useForm][arguments.length=1]:not(:has(Property[key.name=defaultValues]))",
        "message": "Pass object with \"defaultValues\" for correct \"formState\" behavior. More info here: https://react-hook-form.com/api/useform/formstate#main"
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The choice of an effective React template is crucial for the success of your project. Instead of reinventing the wheel, leveraging the power of a well-chosen boilerplate can significantly speed up your development process and create a solid foundation. When selecting a boilerplate, we recommend familiarizing yourself with its directory structure and configuration files to understand its underlying foundation, ease of integration, modularity, and maximum alignment with technical requirements. Consider whether the available features can provide the functions you need. This can save development time and potentially offer well-maintained and tested code.&lt;/p&gt;

&lt;p&gt;Since there was often a question of how to apply multiple boilerplates simultaneously due to the lack of comprehensive functionality in such templates, the &lt;a href="https://bcboilerplates.com/" rel="noopener noreferrer"&gt;bc-boilerplates&lt;/a&gt; team proposed a solution in the form of the &lt;a href="https://github.com/brocoders/extensive-react-boilerplate" rel="noopener noreferrer"&gt;extensive-react-boilerplate&lt;/a&gt;. In our opinion, it can carve out its niche among well-known counterparts and become a worthy competitor deserving of your attention. We invite you to try it out and look forward to your feedback in the form of a new star.&lt;/p&gt;

&lt;p&gt;Full credits for this article to &lt;a href="https://github.com/ElenVlass" rel="noopener noreferrer"&gt;Olena Vlasenko&lt;/a&gt; and &lt;a href="https://github.com/LiudmylaKostenko" rel="noopener noreferrer"&gt;Liudmyla Kostenko&lt;/a&gt;🇺🇦&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Extensive React Boilerplate to kickstart a new frontend project</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Wed, 14 Feb 2024 13:15:50 +0000</pubDate>
      <link>https://dev.to/rodik/extensive-react-boilerplate-to-kickstart-a-new-frontend-project-1lh</link>
      <guid>https://dev.to/rodik/extensive-react-boilerplate-to-kickstart-a-new-frontend-project-1lh</guid>
      <description>&lt;p&gt;How much time do we typically spend on project setup? We're talking about configuring installed libraries and writing boilerplate code to structure and implement best practices for achieving optimal website performance. At &lt;a href="https://brocoders.com/"&gt;Brocoders&lt;/a&gt;, we often start new projects from scratch. That's why over 3 years ago, we created a &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;NestJS  boilerplate&lt;/a&gt; for the backend so that we wouldn't have to spend time developing core functionality that the end user doesn't see but is crucial for developers. Over this time, the boilerplate has received 2.4k stars on GitHub and has gained significant popularity beyond our company. Now, we've decided to take it a step further and created the  &lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;Extensive React Boilerplate&lt;/a&gt; for the frontend. So, in purpose to keep our best practices in project development together, avoiding familiar pitfalls and reducing development time, we've created &lt;a href="https://bcboilerplates.com/"&gt;bc boilerplates&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modules and libraries included in the boilerplate
&lt;/h2&gt;

&lt;p&gt;To have server-side rendering out of the box, automatic page caching, preloading, and other speed optimizations, we use the &lt;strong&gt;Next.js&lt;/strong&gt; framework. It extends React by adding static site generation and is a powerful tool for creating productive and SEO-friendly web applications. To ensure code reliability, performance, and readability in the boilerplate, &lt;strong&gt;TypeScript&lt;/strong&gt; is utilized.&lt;/p&gt;

&lt;p&gt;To prepare the website for supporting local languages and settings, we use the experienced language expert for web applications - the internationalization-framework &lt;strong&gt;i18next&lt;/strong&gt;. It helps organize all added language versions and adapt the content of the site, menu, and messages for different languages and regional settings. We expanded it with packages to detect the user's browser language and transform resources into the server-side of i18next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Material-UI&lt;/strong&gt; is used for quickly creating interfaces without spending time writing components from scratch, such as buttons, input fields, tables, modal windows, and more. With dark mode support, the application based on the boilerplate is automatically configured to use the user's system theme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React Hook Form&lt;/strong&gt; library is integrated for form management, providing a simple and intuitive API optimized for high performance as it works with data without unnecessary re-renders of the entire form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"React Query"&lt;/strong&gt; is used for state management and data caching. It automatically optimizes queries, reducing their duplication, and supports data caching on both the client and server sides, allowing easy cache management across environments.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Cypress&lt;/strong&gt; library provides an interface for tracking and debugging tests, supporting various types of tests including unit tests, integration tests, user interface tests, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ESLint&lt;/strong&gt; helps to ensure that the style of the code in the project is consistent with the rules already established in the .eslintrc.json file to avoid potential problems and warn about possible errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture of the react boilerplate project and folder structure
&lt;/h2&gt;

&lt;p&gt;The project structure allows for easy navigation and editing of various parts of the application. Automated tests are located in the &lt;em&gt;/cypress&lt;/em&gt; folder, divided into different specifications for testing various aspects of the application. All source code of the project, following the logical structure of the application, is concentrated in the &lt;em&gt;/src&lt;/em&gt; folder. Nested within it, the &lt;em&gt;/app&lt;/em&gt; folder displays various application pages, such as the administrative panel with pages for creating and editing users, email confirmation pages, password reset, password change, user profile, login, and registration.The &lt;em&gt;/components&lt;/em&gt; folder contains common components that can be used on different pages of the application. The services section is responsible for interacting with the API server, its files contain modules that are important for proper functionality and interaction with the backend and external services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6wwx6a1t4o6daazfryj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6wwx6a1t4o6daazfryj.png" alt="File structure of the boilerplate" width="800" height="1179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As far as this boilerplate uses Next.js framework for building React applications, the folders are used as routes. This means the more folders you add to your app folder, the more routes you will get. Additionally, if you create a new folder inside of another folder, you will get nested routes. To better understand these concepts, we suggest looking at the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6x2u80ngxyuwenvigvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6x2u80ngxyuwenvigvv.png" alt="Visual explanation of route formation for the project" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use dynamic segments in routing when flexible routes are needed. Within the file structure in the &lt;em&gt;/app&lt;/em&gt; folder, such routes wrap the folder name in square brackets. Thus, it is easy to guess that the variable segments in the route &lt;em&gt;src/app/[language]/admin-panel/users/edit/[id]/&lt;/em&gt; will be &lt;em&gt;language&lt;/em&gt; and &lt;em&gt;id&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mechanisms of authentication and user interaction
&lt;/h2&gt;

&lt;p&gt;Since the web application supports internationalization, additional middleware is added to each page to determine the language, so the language of the authentication form will be displayed depending on the basic system settings of the user's device. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg93hjk1tc327kqwdn84m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg93hjk1tc327kqwdn84m.png" alt="The Sign In page of React Boilerplate" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign Up page
&lt;/h3&gt;

&lt;p&gt;The Sign Up page contains a registration form with fields for user registration, as well as the option to register via Google and Facebook. The necessary API for requests to the server to create a new account is specified, and saving user data is implemented using a context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useAuthGoogleLoginService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchBase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetchBase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthGoogleLoginRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetchBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/auth/google/login`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapperFetchJsonResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthGoogleLoginResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fetchBase&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useAuthFacebookLoginService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchBase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetchBase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthFacebookLoginRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestConfig&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;RequestConfigType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetchBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/auth/facebook/login`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;requestConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapperFetchJsonResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthFacebookLoginResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fetchBase&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access and refresh tokens are acquired and stored for future requests if the backend status is ok, otherwise, error handling procedures are executed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign In page
&lt;/h3&gt;

&lt;p&gt;The Sign In page contains an authentication form with fields for logging in an already registered user, and again, the option to log in via Google or Facebook. After successful authentication, the user receives an access token and a refresh token, which are stored for future requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;HTTP_CODES_ENUM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setTokensInfo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;tokenExpires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokenExpires&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setTokensInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokensInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TokensInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setTokensInfoRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokensInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokensInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokensInfo&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;setTokensInfoRef&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restore and update password
&lt;/h3&gt;

&lt;p&gt;A user may forget their password, so functionality for resetting the old password by sending a link to the email is created. Of course, for such cases there should be a corresponding API on the server, like in our nestjs-boilerplate , which is perfect for for two-way interaction.&lt;/p&gt;

&lt;p&gt;Also, there is an ability to update the password. The logic of sending an api request to the server for updating the user's password and further processing of its results is specified.&lt;/p&gt;

&lt;p&gt;After registering a new account on the server, a link for email confirmation must be generated. Therefore, the boilerplate has logic for the confirm-email route as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Public and private routes
&lt;/h3&gt;

&lt;p&gt;Both public and private routes are implemented - the user's authorization is checked before displaying certain pages, and if the user is not authorized or the authorization data has not yet been loaded, the user is redirected to the sign-in page. Below is the HOC function that implements this logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withPageRequiredAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FunctionComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;OptionsType&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// …&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;WithPageRequiredAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropsType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// …&lt;/span&gt;
   &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;optionRoles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
         &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoaded&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returnToPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
         &lt;span class="nx"&gt;currentLocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLocation&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
         &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;returnTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;returnToPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;

       &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;redirectTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/sign-in?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;redirectTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirectTo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;

     &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;optionRoles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cypress tests have been added for sign-in, sign-up and forgot-password to detect errors and check that all the functionalities of the authentication forms work on different browsers and devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  User’s profile management
&lt;/h2&gt;

&lt;p&gt;The boilerplate includes user data pages and pages for editing their data. Functionality has been added to implement an avatar component that allows users to upload or change their profile photo. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo3xvjl9ueyui0d7xknw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo3xvjl9ueyui0d7xknw.png" alt="The create user page" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;/profile/edit&lt;/em&gt; page has been created to implement the ability to edit the profile, which includes a form with personal data that the user entered during registration, such as name, surname, password, as well as adding/changing an avatar. Additionally, to ensure code quality, detect potential security issues, and verify that the profile editing functionality works properly, this part of the code is also covered by Cypress tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Validation and error messages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sign-in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
 &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error messages should be displayed if required fields are empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sign-in-submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useremail@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sign-in-submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not.exist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error message should be displayed if email isn't registered in the system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/v1/auth/email/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notexistedemail@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sign-in-submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBySel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;be.visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To automate the process of detecting and updating dependencies, we use the &lt;a href="https://docs.renovatebot.com/"&gt;Renovate bot&lt;/a&gt;. It helps avoid issues related to using outdated dependencies and allows controlling the dependency update process according to the project's needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We refer to the &lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;Extensive React Boilerplate&lt;/a&gt; as a structured starting point for frontend development. It pairs beautifully with our &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;NestJS boilerplate&lt;/a&gt; for the backend, and with them, the development team can get started, minimizing setup time and focusing on developing unique aspects of the project, knowing that fundamentals are already correctly implemented. We also keep track of regular library updates and maintain the project in an up-to-date state. So, welcome to try our &lt;a href="https://bcboilerplates.com/"&gt;bc boilerplates&lt;/a&gt; out :)&lt;/p&gt;

&lt;p&gt;Full credits for this article to &lt;a href="https://github.com/ElenVlass"&gt;Elena Vlasenko&lt;/a&gt; and the main developer of the project &lt;a href="https://github.com/Shchepotin"&gt;Vlad Shchepotin&lt;/a&gt; 🇺🇦&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>frontend</category>
    </item>
    <item>
      <title>MongoDB support for NestJS Boilerplate with hexagonal architecture</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Tue, 16 Jan 2024 10:49:07 +0000</pubDate>
      <link>https://dev.to/rodik/mongodb-support-for-nestjs-boilerplate-with-hexagonal-architecture-72p</link>
      <guid>https://dev.to/rodik/mongodb-support-for-nestjs-boilerplate-with-hexagonal-architecture-72p</guid>
      <description>&lt;p&gt;We created &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;NestJS boilerplate&lt;/a&gt; in August 2020 and since then we have worked on its optimisation and improvements.  NestJS boilerplate is a project that contains all necessary libraries and solutions like auth, mailing, etc. for fast-starting your project using a classic REST API approach. Right now this boilerplate has 1.8K stars on Github and got recognition and support from the developers community. Recently we also published our new &lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;frontend boilerplate on React&lt;/a&gt; that is excellently compatible with the backend implementation, so for now, we have a whole &lt;a href="https://bcboilerplates.com/"&gt;bc boilerplate ecosystem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez2c2g5yjm0ry6tb2a9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez2c2g5yjm0ry6tb2a9w.png" alt="NestJS Boilerplate with MongoDB support" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation to include Mongo support
&lt;/h2&gt;

&lt;p&gt;PostgreSQL support was originally included in the boilerplate because of its reliability, data integrity and an active community. But for projects that require high speed of working with large data sets and high scalability, MongoDB is usually a better choice. So, we wanted to integrate MongoDB support into our project. Also we’ve got a number of requests to include NoSQL DB support from the community members and coworkers that use this boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u3l686op4piyi4fhyug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0u3l686op4piyi4fhyug.png" alt="Community request to support MongoDB" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, now it's done and developers can choose between document-oriented database MongoDB and relational database PostgreSQL.&lt;/p&gt;

&lt;p&gt;Now let’s figure out what would be better to use when setting up a new project. Of course, the question is not which database is better, because both databases are excellent, it all depends on the scope and goals of the application. Let’s dive into details.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need a relational database that uses complex SQL requests and works with most apps that support relational table structure, it’s better to choose PostgreSQL.&lt;/li&gt;
&lt;li&gt;For a scenario where a high level of security and high ACID compliance is required, then PostgreSQL is the best solution.&lt;/li&gt;
&lt;li&gt;If you need a reliable tool to handle complex transactions and analytics in applications that work with multi-structured, fast-changing data, then MongoDB is a good choice for your project.&lt;/li&gt;
&lt;li&gt;If you're running an application that you'll need to scale and need to be distributed across regions for data locality or data sovereignty, MongoDB's scale-out architecture will automatically meet those needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need to know more about MongoDB vs PostgreSQL comparison, I recommend reviewing &lt;a href="https://aws.amazon.com/compare/the-difference-between-mongodb-and-postgresql/"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to provide a good level of abstraction and to simplify work with MongoDB we use &lt;a href="https://mongoosejs.com/"&gt;Mongoose&lt;/a&gt; - an object data modeling (ODM) library. It allows developers to define their data models using a schema-based approach and provides a rich set of features that simplify the process of working with MongoDB. In addition to supporting basic CRUD operations and query functions out of the box, Mongoose provides a richer set of features for working with MongoDB, such as middleware functions, virtual properties, query builders, and schema validation. It allows developers to define the structure of their data, including the types of each field, and specify validation rules to ensure data consistency and integrity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52b6zyu091skvk7m8gds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52b6zyu091skvk7m8gds.png" alt="Object Mapping between Node and MongoDB managed via Mongoose" width="800" height="390"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/"&gt;Object Mapping between Node and MongoDB managed via Mongoose&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation with hexagonal architecture
&lt;/h2&gt;

&lt;p&gt;To allow an application to uniformly manage batch execution scenarios separately from its end devices and databases, the hexagonal software architecture (aka ports and adapters architecture) introduced by Alistair Cockburn was used. In &lt;a href="https://alistair.cockburn.us/hexagonal-architecture/"&gt;his article&lt;/a&gt;, he emphasizes that there is not much difference between how a user interface and a database interact with an application, since they are both external connections that are interchangeable with similar components and interact with the application in equivalent ways. Therefore, we used this architectural approach in the project, and it allowed us to encapsulate the implementation details of the data source, thus implementing the support of 2 types of databases in the boilerplate.&lt;br&gt;
Let's take a closer look at the implementation. First of all, we create the User entity in the &lt;code&gt;users/domain&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we create a port called &lt;code&gt;UserRepository&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EntityCondition&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NullableType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;users/infrastructure/persistence/relational/repositories&lt;/code&gt; we implement UserRepository for working with TypeORM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersRelationalRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;InjectRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserEntity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;usersRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Repository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;persistenceModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toPersistence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;persistenceModel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newEntity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EntityCondition&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NullableType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FindOptionsWhere&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we create a module for working with TypeORM in &lt;code&gt;users/infrastructure/persistence/relational&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TypeOrmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forFeature&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;UserEntity&lt;/span&gt;&lt;span class="p"&gt;])],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UsersRelationalRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RelationalUserPersistenceModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we do the same for Mongoose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersDocumentRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;InjectModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserSchemaClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;usersModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserSchemaClass&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;persistenceModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toPersistence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;usersModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;persistenceModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createdUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EntityCondition&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NullableType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userObject&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userObject&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And module for working with MongoDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;MongooseModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forFeature&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserSchemaClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UsersDocumentRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentUserPersistenceModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we connect either the module for working with Mongoose (DocumentUserPersistenceModule) or TypeORM (RelationalUserPersistenceModule)  in &lt;code&gt;users/users.module.ts&lt;/code&gt; based on the ENV configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;infrastructurePersistenceModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;databaseConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;DatabaseConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDocumentDatabase&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;DocumentUserPersistenceModule&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RelationalUserPersistenceModule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;infrastructurePersistenceModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FilesModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UsersController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UsersService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UsersService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;infrastructurePersistenceModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then in the UserService we can access the UserRepository, and nestjs will understand which database to use based on the ENV configuration settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;usersRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full implementation can be found &lt;a href="https://github.com/brocoders/nestjs-boilerplate/tree/main/src/users"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MongoDB schema
&lt;/h2&gt;

&lt;p&gt;Schema was built with best practices for MongoDB for best performance and scalability. Schema design for NoSQL databases is not the same as for relational databases. One of the differences is that in relational we have the option to reduce your schema to normal forms to avoid duplicates, etc. While for NoSQL, we can duplicate data to avoid "joins", due to which the best performance indicator will be achieved during data sampling. Let's take a look at the boilerplate as an example to see what the difference is. The database schema for PostgreSQL looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdywidk3dt1q1y4v0onh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdywidk3dt1q1y4v0onh9.png" alt="DB Schema for PostgreSQL" width="800" height="737"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And an example of data table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g2vvr4eu3oieesyj256.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g2vvr4eu3oieesyj256.png" alt="Example of data table for PostgreSQL" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talking about MongoDB, then we can NOT transfer the design experience from PostgreSQL here, that is, create 4 collections users, files (for photos), roles, statuses and store in the user collection links to other collections and during data sampling using aggregation ($lookup) append additional data, as this will affect performance ​(read more about joins comparison &lt;a href="https://www.enterprisedb.com/blog/comparison-joins-mongodb-vs-postgresql"&gt;in this article&lt;/a&gt;, though 2020 year sounds old, but it is still actual). What should the scheme look like? Everything is very simple: all data must be stored in one collection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm158aafxqwe53ahxez7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm158aafxqwe53ahxez7.png" alt="MongoDB Schema and example of datasets" width="800" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now when we will fetch users, we will not need to make additional requests for obtaining data about the user's photo, role and status, because all the data is already stored in the user's collection, in fact, due to which productivity will increase.&lt;/p&gt;

&lt;p&gt;To learn more about designing MongoDB schemas I recommend reviewing &lt;a href="https://www.mongodb.com/developer/products/mongodb/schema-design-anti-pattern-massive-arrays/"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use NestJS Boilerplate with Mongoose
&lt;/h2&gt;

&lt;p&gt;For comfortable development (MongoDB + Mongoose) you have to clone the repository, go to folder &lt;code&gt;my-app/&lt;/code&gt; and  copy &lt;code&gt;env-example-document&lt;/code&gt; as &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd my-app/&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cp env-example-document .env&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Change &lt;code&gt;DATABASE_URL=mongodb://mongo:27017&lt;/code&gt; to &lt;code&gt;DATABASE_URL=mongodb://localhost:27017&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run additional container:&lt;br&gt;
&lt;code&gt;docker compose -f docker-compose.document.yaml up -d mongo mongo-express maildev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install dependency&lt;br&gt;
&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run migrations&lt;br&gt;
&lt;code&gt;npm run migration:run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run seeds&lt;br&gt;
&lt;code&gt;npm run seed:run:document&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run app in dev mode&lt;br&gt;
&lt;code&gt;npm run start:dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;If we talk about the impact of the selected database on the frontend application, in particular the &lt;a href="https://github.com/brocoders/extensive-react-boilerplate"&gt;Extensive React boilerplate&lt;/a&gt;, which we also maintain up-to-date and it plays well with the currently discussed &lt;a href="https://github.com/brocoders/nestjs-boilerplate"&gt;NestJS boilerplate&lt;/a&gt;, so it won't have affect on their interaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjiy0l1fnuz1cvjdkvrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjiy0l1fnuz1cvjdkvrj.png" alt="Frontend React Boilerplate" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whatever database you choose to use - PostgreSQL or MongoDB (they are both great), the choice should depend on the whole project, and in our boilerplate you have that choice) So, welcome to try it if you find it useful, check out our &lt;a href="https://bcboilerplates.com/"&gt;bc boilerplate ecosystem&lt;/a&gt;,  and don't forget to click the star at the library ⭐.&lt;/p&gt;

&lt;p&gt;Full credits for this article to &lt;a href="https://github.com/Shchepotin"&gt;Vlad Shchepotin&lt;/a&gt; and &lt;a href="https://github.com/ElenVlass"&gt;Elena Vlasenko&lt;/a&gt; 🇺🇦&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>mongodb</category>
      <category>node</category>
      <category>mongoose</category>
    </item>
    <item>
      <title>Will Bun replace Node.js? First try with NestJS</title>
      <dc:creator>Rodion</dc:creator>
      <pubDate>Fri, 15 Sep 2023 12:43:50 +0000</pubDate>
      <link>https://dev.to/rodik/will-bun-replace-nodejs-first-try-with-nestjs-50a3</link>
      <guid>https://dev.to/rodik/will-bun-replace-nodejs-first-try-with-nestjs-50a3</guid>
      <description>&lt;p&gt;Many people recently talked about Bun, so we decided to try it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; is a fast JavaScript all-in-one toolkit that helps to develop, test, run, and bundle JavaScript &amp;amp; TypeScript projects. It claims to be really fast in terms of runtime and development. So it should not only make the app faster but speed up the development process too. Take a look at their &lt;a href="https://www.youtube.com/watch?v=BsnCpESUEqM&amp;amp;pp=ygUGYnVuIGFp" rel="noopener noreferrer"&gt;video presentation on YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our idea was to run our &lt;a href="https://github.com/brocoders/nestjs-boilerplate" rel="noopener noreferrer"&gt;Brocoders Nest.js Boilerplate&lt;/a&gt; on Bun and see how it performs. The test was made on MacBook Pro 2GHz, 16Gb memory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2addgoh8v4zyeewjsn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2addgoh8v4zyeewjsn5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we tried to install dependencies with npm install and bun. The difference is pretty decent: 32s npm and 5s bun.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvmn1whpwkdt7tkiaxgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvmn1whpwkdt7tkiaxgr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frso5toi4b0g9s2609nvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frso5toi4b0g9s2609nvb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But then we tried to run it, there was an error appeared on the screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzribif04mkujpdh71lca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzribif04mkujpdh71lca.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is connected with emitDecoratorMetadata feature that is not supported by Bun yet. There is an issue with it on Github: &lt;a href="https://github.com/nestjs/nest/issues/12359#issuecomment-1712712949" rel="noopener noreferrer"&gt;https://github.com/nestjs/nest/issues/12359#issuecomment-1712712949&lt;/a&gt;&lt;br&gt;
So, finally, I would say that Bun is really a perspective thing, but so far, it seems it's not ready for production yet. But we will definitely watch this repository and come back to it later.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
