Skip to content

Functional Programming in TypeScript Using the fp-ts Library: Option

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Welcome back to our blog series on functional programming with fp-ts! In our previous post, we introduced the concept of functional programming and how it can be used to write more robust and maintainable code. Also we talked about the building block of fp-ts library: Pipe and Flow operators. Today, we're going to dive deeper into one of the most useful tools in the fp-ts toolbox: the Option type.

What is an Option?

In JavaScript, we often encounter situations where a value may or may not exist. For example, when we try to access a property of an object that may be null or undefined. This can lead to runtime errors and unexpected behavior. The Option type in fp-ts provides a way to handle these situations in a safe and predictable manner.

An Option is a container that can hold either a value or nothing. It is represented by the Option type in fp-ts, which has two constructors: Some and None. The Some constructor is used to wrap a value, while the None constructor represents the absence of a value.

  1. some: The some constructor is used to create an instance of Option when a value is present, or when a computation succeeds. It takes the value as its argument, and wraps it inside of the Option type.
import { some } from 'fp-ts/lib/Option';

const value = 42;

const optionValue = some(value);

console.log(optionValue) // {_tag: ‘some’, value: 42}
  1. none: The none constructor is used to create an instance of Option when a value is absent, or when a computation fails. It does not take any arguments; it simply represents the absence of a value.
import { none } from 'fp-ts/lib/Option';

const optionNone = none;

console.log(optionNone) //{_tag: none}  

The some and none constructors help us avoid null and undefined errors by forcing us to handle both cases explicitly using functional programming techniques.

But let's look at some examples:

import { Option, some, none } from 'fp-ts/lib/Option';

interface User {
	name: string;
	email: Option<string>;
	phone: Option<string>;
}

function getUser(id: number): User {
	// some logic to fetch user from database
	if (userExists) {
		return {
			name: userData.name,
			email: some(userData.email),
			phone: some(userData.phone),
			}
	} else {
		return {
			name: '',
			email: none,
			phone: none,
		};
	}
}

const user = getUser(123);

console.log(user.name);

if (user.email._tag === 'Some') {
	console.log(user.email.value);
} else {
	console.log('Email not found');
}

if (user.phone._tag === 'Some') {
	console.log(user.phone.value);
} else {
	console.log('Phone not found');
}

In this example, the getUser function returns a User object that may contain missing data. We use Option to represent the email and phone fields, which may or may not be present. We can then pattern match on the Option to safely handle the case where the field is missing.

Pattern matching is a powerful feature in functional programming that allows developers to match values against a set of patterns and execute corresponding code based on the match.

We are pattern matching Option when we do this in the example above:

if (user.email._tag === 'Some') {
	console.log(user.email.value);
} else {
	console.log('Email not found');
}

Why use Option?

Using Option can help us avoid runtime errors and make our code more robust. By explicitly handling the absence of a value, we can prevent null or undefined errors from occurring. This can also make our code easier to reason about, as we don't have to worry about unexpected behavior caused by missing values.

Option can also help us write more expressive code. By using Some and None instead of null or undefined, we can make our intentions clearer, and reduce the cognitive load on other developers who may be reading our code.

Let's say we have a function findUserById that retrieves a user from a database based on their ID. If the user is found, the function returns the user object. But if the user is not found, it returns null.

function findUserById(id: number): User | null {
	// Code to fetch user from the database
}

const userId = 123;

const user = findUserById(userId);

if (user !== null) {
	// User found, do something with the user object
	console.log(user.name);
} else {
	// User not found
	console.log("User not found");
}

In this example, we explicitly check for null to determine if the user was found or not. This approach can lead to potential runtime errors if we forget to check for null in some parts of our code.

Now, let's rewrite the same example using fp-ts Option:

import { Option, some, none } from 'fp-ts/lib/Option';

function findUserById(id: number): Option<User> {
	// Code to fetch user from the database
} 

const userId = 123;

const userOption = findUserById(userId);

userOption.fold(
	() => {
		// User not found
		console.log("User not found");
	},
	(user) => {
		// User found, do something with the user object
		console.log(user.name);
	}
);  

In this example, the findUserById function returns an Option<User>, which can either be some(user) if the user is found, or none if the user is not found.

Instead of manually checking for null, we use the fold method provided by fp-ts Option. If the user is found (some(user)), the second function inside fold is executed (right function), and if the user is not found (none), the second function is executed (left function). This approach ensures that we handle both cases explicitly, avoiding potential null-related errors.

The fold method is a fundamental operation provided by the Option type in fp-ts. It allows us to extract values from an Option instance by providing two functions: one for handling the case when the Option has a value (Some), and another for handling the case when the Option is empty (None).

It's important to note that Option in fp-ts is implemented as a discriminated union, meaning the some and none constructors are different variants of the same type. This enables the compiler to enforce exhaustiveness checking, ensuring that we handle both cases when using fold or other methods that require pattern matching.

Conclusion:

In this post, we've introduced the Option type in fp-ts and shown how it can be used to handle null or undefined values in a type-safe manner. We've also seen how Option can be used to represent optional values in a more self-documenting way. In future posts, we will explore the fold method in more detail with examples, along with other useful Option methods combined with other new fp-ts operators. These techniques will further enhance our functional programming skills and allow us to handle Option instances more effectively.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Angular 17: Continuing the Renaissance cover image

Angular 17: Continuing the Renaissance

Angular 17: A New Era November 8th marked a significant milestone in the world of Angular with the release of Angular 17. This wasn't just any ordinary update; it was a leap forward, signifying a new chapter for the popular framework. But what made this release truly stand out was the unveiling of Angular's revamped website, complete with a fresh brand identity and a new logo. This significant transformation represents the evolving nature of Angular, aligning with the modern demands of web development. To commemorate this launch, we also hosted a release afterparty, where we went deep into its new features with Minko Gechev from the Angular core team, and Google Developer Experts (GDEs) Brandon Roberts, Deborah Kurata, and Enea Jahollari. But what exactly are these notable new features in the latest version? Let's dive in and explore. The Angular Renaissance Angular has been undergoing a significant revival, often referred to as Angular's renaissance, a term coined by Sarah Drasner, the Director of Engineering at Google, earlier this year. This revival has been particularly evident in its recent versions. The Angular team has worked hard to introduce many new improvements, focusing on signal-based reactivity, hydration, server-side rendering, standalone components, and migrating to esbuild and Vite for a better and faster developer experience. This latest release, in particular, marks many of these features as production-ready. Standalone Components About a year ago, Angular began a journey toward modernity with the introduction of standalone components. This move significantly enhanced the developer experience, making Angular more contemporary and user-friendly. In Angular's context, a standalone component is a self-sufficient, reusable code unit that combines logic, data, and user interface elements. What sets these components apart is their independence from Angular's NgModule system, meaning they do not rely on it for configuration or dependencies. By setting a standalone: true flag, you no longer need to embed your component in an NgModule and you can bootstrap directly off that component: ` Compared to the NgModules way of adding components, as shown below, you can immediately see how standalone components make things much simpler. ` In this latest release, the Angular CLI now defaults to generating standalone components, directives, and pipes. This default setting underscores the shift towards a standalone-centric development approach in Angular. New Syntax for Enhanced Control Flow Angular 17 introduces a new syntax for control flow, replacing traditional structural directives like ngIf or ngFor, which have been part of Angular since version 2. This new syntax is designed for fine-grained change detection and eventual zone-less operation when Angular completely migrates to signals. It's more streamlined and performance-efficient, making handling conditional or list content in templates easier. The @if block replaces *ngIf for expressing conditional parts of the UI. ` The @switch block replaces ngSwitch, offering benefits such as not requiring a container element to hold the condition expression or each conditional template. It also supports template type-checking, including type narrowing within each branch. ``` The @for block replaces *ngFor for iteration and presents several differences compared to its structural directive predecessor, ngFor. For example, the tracking expression (calculating keys corresponding to object identities) is mandatory but offers better ergonomics. Additionally, it supports @empty blocks. ` Defer Block for Lazy Loading Angular 17 introduces the @defer block, a dramatically improving lazy loading of content within Angular applications. Within the @defer block framework, several sub-blocks are designed to elegantly manage different phases of the deferred loading process. The main content within the @defer block is the segment designated for lazy loading. Initially, this content is not rendered, becoming visible only when specific triggers are activated or conditions are met, and after the required dependencies have been loaded. By default, the trigger for a @defer block is the browser reaching an idle state. For instance, take the following block: it delays the loading of the calendar-imp component until it comes into the viewport. Until that happens, a placeholder is shown. This placeholder displays a loading message when the calendar-imp component begins to load, and an error message if, for some reason, the component fails to load. ` The on keyword supports a wide a variety of other conditions, such as: - idle (when the browser has reached an idle state) - interaction (when the user interacts with a specified element) - hover (when the mouse has hovered over a trigger area) - timer(x) (triggers after a specified duration) - immediate (triggers the deferred load immediately) The second option of configuring when deferring happens is by using the when keyword. For example: ` Server-Side Rendering (SSR) Angular 17 has made server-side rendering (SSR) much more straightforward. Now, a --ssr option is included in the ng new command, removing the need for additional setup or configurations. When creating a new project with the ng new command, the CLI inquires if SSR should be enabled. As of version 17, the default response is set to 'No'. However, for version 18 and beyond, the plan is to enable SSR by default in newly generated applications. If you prefer to start with SSR right away, you can do so by initializing your project with the --ssr flag: ` For adding SSR to an already existing project, utilize the ng add command of the Angular CLI: ` Hydration In Angular 17, the process of hydration, which is essential for reviving a server-side rendered application on the client-side, has reached a stable, production-ready status. Hydration involves reusing the DOM structures rendered on the server, preserving the application's state, and transferring data retrieved from the server, among other crucial tasks. This functionality is automatically activated when server-side rendering (SSR) is used. It offers a more efficient approach than the previous method, where the server-rendered tree was completely replaced, often causing visible UI flickers. Such re-rendering can adversely affect Core Web Vitals, including Largest Contentful Paint (LCP), leading to layout shifts. By enabling hydration, Angular 17 allows for the reuse of the existing DOM, effectively preventing these flickers. Support for View Transitions The new View Transitions API, supported by some browsers, is now integrated into the Angular router. This feature, which must be activated using the withViewTransitions function, allows for CSS-based animations during route transitions, adding a layer of visual appeal to applications. To use it, first you need to import withViewTransitions: ` Then, you need to add it to the provideRouter configuration: ` Other Notable Changes - Angular 17 has stabilized signals, initially introduced in Angular 16, providing a new method for state management in Angular apps. - Angular 17 no longer supports Node 16. The minimal Node version required is now 18.13. - TypeScript version 5.2 is the least supported version starting from this release of Angular. - The @Component decorator now supports a styleUrl attribute. This allows for specifying a single stylesheet path as a string, simplifying the process of linking a component to a specific style sheet. Previously, even for a single stylesheet, an array was required under styleUrls. Conclusion With the launch of Angular 17, the Angular Renaissance is now in full swing. This release has garnered such positive feedback that developers are showing renewed interest in the framework and are looking forward to leveraging it in upcoming projects. However, it's important to note that it might take some time for IDEs to adapt to the new templating syntax fully. While this transition is underway, rest assured that you can still write perfectly valid code using the old templating syntax, as all the changes in Angular 17 are backward compatible. Looking ahead, the future of Angular appears brighter than ever, and we can't wait to see what the next release has in store!...

Understanding Vue's Reactive Data cover image

Understanding Vue's Reactive Data

Introduction Web development has always been about creating dynamic experiences. One of the biggest challenges developers face is managing how data changes over time and reflecting these changes in the UI promptly and accurately. This is where Vue.js, one of the most popular JavaScript frameworks, excels with its powerful reactive data system. In this article, we dig into the heart of Vue's reactivity system. We unravel how it perfectly syncs your application UI with the underlying data state, allowing for a seamless user experience. Whether new to Vue or looking to deepen your understanding, this guide will provide a clear and concise overview of Vue's reactivity, empowering you to build more efficient and responsive Vue 3 applications. So, let’s kick off and embark on this journey to decode Vue's reactive data system. What is Vue's Reactive Data? What does it mean for data to be ”'reactive”? In essence, when data is reactive, it means that every time the data changes, all parts of the UI that rely on this data automatically update to reflect these changes. This ensures that the user is always looking at the most current state of the application. At its core, Vue's Reactive Data is like a superpower for your application data. Think of it like a mirror - whatever changes you make in your data, the user interface (UI) reflects these changes instantly, like a mirror reflecting your image. This automatic update feature is what we refer to as “reactivity”. To visualize this concept, let's use an example of a simple Vue application displaying a message on the screen: ` In this application, 'message' is a piece of data that says 'Hello Vue!'. Let's say you change this message to 'Goodbye Vue!' later in your code, like when a button is clicked. ` With Vue's reactivity, when you change your data, the UI automatically updates to 'Goodbye Vue!' instead of 'Hello Vue!'. You don't have to write extra code to make this update happen - Vue's Reactive Data system takes care of it. How does it work? Let's keep the mirror example going. Vue's Reactive Data is the mirror that reflects your data changes in the UI. But how does this mirror know when and what to reflect? That's where Vue's underlying mechanism comes into play. Vue has a behind-the-scenes mechanism that helps it stay alerted to any changes in your data. When you create a reactive data object, Vue doesn't just leave it as it is. Instead, it sends this data object through a transformation process and wraps it up in a Proxy. Proxy objects are powerful and can detect when a property is changed, updated, or deleted. Let's use our previous example: ` Consider our “message” data as a book in a library. Vue places this book (our data) within a special book cover (the Proxy). This book cover is unique - it's embedded with a tracking device that notifies Vue every time someone reads the book (accesses the data) or annotates a page (changes the data). In our example, the reactive function creates a Proxy object that wraps around our state object. When you change the 'message': ` The Proxy notices this (like a built-in alarm going off) and alerts Vue that something has changed. Vue then updates the UI to reflect this change. Let’s look deeper into what Vue is doing for us and how it transforms our object into a Proxy object. You don't have to worry about creating or managing the Proxy; Vue handles everything. ` In the example above, we encapsulate our object, in this case, “state”, converting it into a Proxy object. Note that within the second argument of the Proxy, we have two methods: a getter and a setter. The getter method is straightforward: it merely returns the value, which in this instance is “state.message” equating to 'Hello Vue!' Meanwhile, the setter method comes into play when a new value is assigned, as in the case of “state.message = ‘Hey young padawan!’”. Here, “value” becomes our new 'Hey young padawan!', prompting the property to update. This action, in turn, triggers the reactivity system, which subsequently updates the DOM. Venturing Further into the Depths If you have been paying attention to our examples above, you might have noticed that inside the Proxy method, we call the functions track and trigger to run our reactivity. Let’s try to understand a bit more about them. You see, Vue 3 reactivity data is more about Proxy objects. Let’s create a new example: ` In this example, when you click on the button, the message's value changes. This change triggers the effect function to run, as it's actively listening for any changes in its dependencies. How does the effect property know when to be called? Vue 3 has three main functions to run our reactivity: effect, track, and trigger. The effect function is like our supervisor. It steps in and takes action when our data changes – similar to our effect method, we will dive in more later. Next, we have the track function. It notes down all the important data we need to keep an eye on. In our case, this data would be state.message. Lastly, we've got the trigger function. This one is like our alarm bell. It alerts the effect function whenever our important data (the stuff track is keeping an eye on) changes. In this way, trigger, track, and effect work together to keep our Vue application reacting smoothly to changes in data. Let’s go back to them: ` Tracking (Dependency Collection) Tracking is the process of registering dependencies between reactive objects and the effects that depend on them. When a reactive property is read, it's "tracked" as a dependency of the current running effect. When we execute track(), we essentially store our effects in a Set object. But what exactly is an "effect"? If we revisit our previous example, we see that the effect method must be run whenever any property changes. This action — running the effect method in response to property changes — is what we refer to as an "Effect"! (computed property, watcher, etc.) > Note: We'll outline a basic, high-level overview of what might happen under the hood. Please note that the actual implementation is more complex and optimized, but this should give you an idea of how it works. Let’s see how it works! In our example, we have the following reactive object: ` We need a way to reference the reactive object with its effects. For that, we use a WeakMap. Which type is going to look something like this: ` We are using a WeakMap to set our object state as the target (or key). In the Vue code, they call this object targetMap. Within this targetMap object, our value is an object named depMap of Map type. Here, the keys represent our properties (in our case, that would be message and showSword), and the values correspond to their effects – remember, they are stored in a Set that in Vue 3 we refer to as dep. Huh… It might seem a bit complex, right? Let's make it more straightforward with a visual example: With the above explained, let’s see what this Track method kind of looks like and how it uses this targetMap. This method essentially is doing something like this: ` At this point, you have to be wondering, how does Vue 3 know what activeEffect should run? Vue 3 keeps track of the currently running effect by using a global variable. When an effect is executed, Vue temporarily stores a reference to it in this global variable, allowing the track function to access the currently running effect and associate it with the accessed reactive property. This global variable is called inside Vue as activeEffect. Vue 3 knows which effect is assigned to this global variable by wrapping the effects functions in a method that invokes the effect whenever a dependency changes. And yes, you guessed, that method is our effect method. ` This method behind the scenes is doing something similar to this: ` The handling of activeEffect within Vue's reactivity system is a dance of careful timing, scoping, and context preservation. Let’s go step by step on how this is working all together. When we run our Effect method for the first time, we call the get trap of the Proxy. ` When running the get trap, we have our activeEffect so we can store it as a dependency. ` This coordination ensures that when a reactive property is accessed within an effect, the track function knows which effect is responsible for that access. Trigger Method Our last method makes this Reactive system to be complete. The trigger method looks up the dependencies for the given target and key and re-runs all dependent effects. ` Conclusion Diving into Vue 3's reactivity system has been like unlocking a hidden superpower in my web development toolkit, and honestly, I've had a blast learning about it. From the rudimentary elements of reactive data and instantaneous UI updates to the intricate details involving Proxies, track and trigger functions, and effects, Vue 3's reactivity is an impressively robust framework for building dynamic and responsive applications. In our journey through Vue 3's reactivity, we've uncovered how this framework ensures real-time and precise updates to the UI. We've delved into the use of Proxies to intercept and monitor variable changes and dissected the roles of track and trigger functions, along with the 'effect' method, in facilitating seamless UI updates. Along the way, we've also discovered how Vue ingeniously manages data dependencies through sophisticated data structures like WeakMaps and Sets, offering us a glimpse into its efficient approach to change detection and UI rendering. Whether you're just starting with Vue 3 or an experienced developer looking to level up, understanding this reactivity system is a game-changer. It doesn't just streamline the development process; it enables you to create more interactive, scalable, and maintainable applications. I love Vue 3, and mastering its reactivity system has been enlightening and fun. Thanks for reading, and as always, happy coding!...

Maximizing Routing Flexibility with Next.js Parallel and Intercepting Routes cover image

Maximizing Routing Flexibility with Next.js Parallel and Intercepting Routes

Maximizing Routing Flexibility with Next.js Parallel and Intercepting Routes Introduction: Next.js, a powerful React framework, offers two essential features - Parallel Routes and Intercepting Routes - that enable developers to create highly dynamic and seamless user experiences in their applications. In this blog post, we will explore both Parallel Routes and Intercepting Routes, highlighting their functionalities, use cases, and how they can be combined to enhance application routing. What are Parallel Routes? Parallel Routes in Next.js allow the rendering of multiple pages within a shared layout. Rather than navigating to a completely separate page, Parallel Routes enable the simultaneous rendering of different pages based on certain conditions such as user roles or tab navigation. By leveraging named slots, defined using the @folder convention, multiple pages can be seamlessly integrated into a single layout, enhancing user experience and reducing code duplication. How to Use Parallel Routes: To utilize Parallel Routes in Next.js, you must define named slots for the desired pages within your application's folder structure. These slots are then passed as props to a shared parent layout component. For example, consider a social networking application where two pages, feed and notifications, must be rendered within a common layout. You can define Parallel Routes using the following structure: ` The layout component in app/layout.js will accept the @feed and @notifications slots as props and render them alongside the children prop. Here's an example of the layout component: ` It's important to note that slots do not affect the URL structure and are not considered route segments. This means the URL would be /mypage/@feed/views for a route like /mypage/views since @feed is a slot. Also, to prevent the application from rendering nothing or throwing an error, we could use the default.tsx file: ` In this example, the default.tsx file specifies the default content (or fallback) to render when none of the named slots match the current route. It acts as a catch-all for routes without a corresponding slot. Some Use Cases: *Conditional Routes:* Parallel Routes can conditionally render routes based on certain conditions, such as user roles. For example, you can render a different dashboard page for admins and users. This can be achieved by creating a layout component specific to the dashboard and checking the user role to determine which slot to render. *Tab Groups:* You can utilize Parallel Routes to create tabbed navigation within a section. This is especially useful for analytics or multi-page views. Adding a layout inside a slot allows users to navigate between different pages within that slot independently. *Loading and Error UI:* Parallel Routes can be independently streamed, allowing you to define custom error and loading states for each route. This enables better control over the user experience by providing distinct feedback for different app sections. *Modals:* Parallel Routes can be combined with Intercepting Routes to create modals with shareable URLs, preserved context on refresh, and customized navigation behavior. By intercepting certain routes and rendering them within a modal component, you can create a seamless modal experience. Understanding Intercepting Routes: Intercepting Routes in Next.js allows you to load content from another route within the current layout without changing the URL or refreshing the page. Using the (..) Convention: Intercepting routes are defined using the (..) convention, which is similar to the relative path convention ../ but for route segments. The convention allows for matching segments on the same level (.), one level above (..), two levels above (..)(..), or from the root app directory (...). For example, to intercept the photo segment from within the feed segment, you can create a (..)photo directory. Integrating Intercepting Routes with Parallel Routes - Modals: One powerful use case for Intercepting Routes is creating modals, which can be combined with Parallel Routes to solve common challenges such as making the modal content shareable through a URL, preserving context on page refresh, and handling backward and forward navigation. Let's make an example: ` Our folder structure will be: In this example, when a user clicks on a photo within the /gallery page, the route /photo/:photoId will be intercepted and rendered within the (..)photo directory, overlaying the gallery content. This means that the photo route is only one segment level higher despite being two file-system levels higher. In the example above, the path to the photo segment can use the (..) matcher since @modal is a slot and not a segment. Common Use Cases - Beyond Modals: Intercepting Routes can also be utilized in other scenarios. Some examples include opening a login modal in a top navbar while having a dedicated /login page, or opening a shopping cart in a side modal for better accessibility or creating wizards or multi-step forms within a single page. The possibilities are endless. Conclusion Next.js Parallel and Intercepting Routes offer powerful methods for enhancing the routing capabilities of your applications. With Parallel Routes, multiple pages can be seamlessly integrated within a shared layout, simplifying navigation and enhancing user experience. By incorporating Intercepting Routes, you can create dynamic overlays like modals, enabling content from different routes to be displayed within the current layout. By leveraging these features, you can unlock exciting possibilities and take your Next.js applications to new heights of user engagement....

Next.js + MongoDB Connection Storming cover image

Next.js + MongoDB Connection Storming

Building a Next.js application connected to MongoDB can feel like a match made in heaven. MongoDB stores all of its data as JSON objects, which don’t require transformation into JavaScript objects like relational SQL data does. However, when deploying your application to a serverless production environment such as Vercel, it is crucial to manage your database connections properly. If you encounter errors like these, you may be experiencing Connection Storming: * MongoServerSelectionError: connect ECONNREFUSED &lt;IP_ADDRESS>:&lt;PORT> * MongoNetworkError: failed to connect to server [&lt;hostname>:&lt;port>] on first connect * MongoTimeoutError: Server selection timed out after &lt;x> ms * MongoTopologyClosedError: Topology is closed, please connect * Mongo Atlas: Connections % of configured limit has gone above 80 Connection storming occurs when your application has to mount a connection to Mongo for every serverless function or API endpoint call. Vercel executes your application’s code in a highly concurrent and isolated fashion. So, if you create new database connections on each request, your app might quickly exceed the connection limit of your database. We can leverage Vercel’s fluid compute model to keep our database connection objects warm across function invocations. Traditional serverless architecture was designed for quick, stateless web app transactions. Now, especially with the rise of LLM-oriented applications built with Next.js, interactions with applications are becoming more sequential. We just need to ensure that we assign our MongoDB connection to a global variable. Protip: Use global variables Vercel’s fluid compute model means all memory, including global constants like a MongoDB client, stays initialized between requests as long as the instance remains active. By assigning your MongoDB client to a global constant, you avoid redundant setup work and reduce the overhead of cold starts. This enables a more efficient approach to reusing connections for your application’s MongoDB client. The example below demonstrates how to retrieve an array of users from the users collection in MongoDB and either return them through an API request to /api/users or render them as an HTML list at the /users route. To support this, we initialize a global clientPromise variable that maintains the MongoDB connection across warm serverless executions, avoiding re-initialization on every request. ` Using this database connection in your API route code is easy: ` You can also use this database connection in your server-side rendered React components. ` In serverless environments like Vercel, managing database connections efficiently is key to avoiding connection storming. By reusing global variables and understanding the serverless execution model, you can ensure your Next.js app remains stable and performant....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co