Ways to streamline redundant code by creating a higher order function that accepts different parameter types in TypeScript

Recently, I've been exploring the idea of refactoring this code into a higher order function using TypeScript to enhance its cleanliness and reusability. However, I'm facing quite a challenge in getting it to work seamlessly.

import { DocumentDefinition, FilterQuery, QueryOptions, UpdateQuery } from 'mongoose';
import TaskModel, { TaskDocument } from '../models/Task.model';
import { databaseResponseTimeHistogram } from '../utils/appmetrics';

export async function createTask(
    input: DocumentDefinition<
        Omit<TaskDocument, 'createdAt' | 'updatedAt' | 'taskId' | 'isCompleted'>
    >
) {
    const metricsLabels = { operation: 'createTask' };
    const timer = databaseResponseTimeHistogram.startTimer();

    try {
        const result = TaskModel.create(input);
        timer({ ...metricsLabels, success: 'true' });

        return result;
    } catch (err: any) {
        timer({ ...metricsLabels, success: 'false' });
        throw new Error(err.message);
    }
}

export async function findTask(
    query: FilterQuery<TaskDocument>,
    options: QueryOptions = { lean: true }
) {
    const metricsLabels = { operation: 'findTask' };
    const timer = databaseResponseTimeHistogram.startTimer();

    try {
        const result = TaskModel.findOne(query, {}, options);
        timer({ ...metricsLabels, success: 'true' });

        return result;
    } catch (err: any) {
        timer({ ...metricsLabels, success: 'false' });
        throw new Error(err.message);
    }
}

export async function findAndUpdateTask(
    query: FilterQuery<TaskDocument>,
    update: UpdateQuery<TaskDocument>,
    options: QueryOptions
) {
    const metricsLabels = { operation: 'findTask' };
    const timer = databaseResponseTimeHistogram.startTimer();

    try {
        const result = TaskModel.findOneAndUpdate(query, update, options);
        timer({ ...metricsLabels, success: 'true' });

        return result;
    } catch (err: any) {
        timer({ ...metricsLabels, success: 'false' });
        throw new Error(err.message);
    }
}

Essentially, my goal is to streamline the metrics functionality by encapsulating the try-catch block into a utility function. This would allow for easy invocation with specific parameters such as the operation, TaskModel.method, and respective arguments like (input) for create, (query, {}, options) for findOne, and (query, update, options) for findManyAndUpdate...

However, I'm encountering difficulties in correctly typing all the various parameters.

Answer №1

Essentially, the goal is to refactor this specific section:

try {
    /* SOME OPERATION */
    timer({ ...metricsLabels, success: 'true' });

    return result;
} catch (err: any) {
    timer({ ...metricsLabels, success: 'false' });
    throw new Error(err.message);
}

An approach could be wrapping it in a function and providing the SOME OPERATION as a parameter:

function withTimer(label:string, operation:Function) {
    const metricsLabels = { operation: label };
    const timer = databaseResponseTimeHistogram.startTimer();

    try {
        const result = operation();
        timer({ ...metricsLabels, success: 'true' });

        return result;
    } catch (err: any) {
        timer({ ...metricsLabels, success: 'false' });
        throw new Error(err.message);
    }
}

Following this structure, functions can now be updated as follows:

export async function createTask(
    input: DocumentDefinition<
        Omit<TaskDocument, 'createdAt' | 'updatedAt' | 'taskId' | 'isCompleted'>
    >
) {
    return withTimer('createTask',
        () => TaskModel.create(input)
    );
}

export async function findTask(
    query: FilterQuery<TaskDocument>,
    options: QueryOptions = { lean: true }
) {
    return withTimer('findTask',
        () => TaskModel.findOne(query, {}, options)
    );
}

export async function findAndUpdateTask(
    query: FilterQuery<TaskDocument>,
    update: UpdateQuery<TaskDocument>,
    options: QueryOptions
) {
    return withTimer('findTask',
        () => TaskModel.findOneAndUpdate(query, update, options)
    );
}

The concept of using higher-order functions lies in encapsulating the less common code parts within functions that can be passed to more general code sections.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Selection box and interactive buttons similar to those found in Gmail

Looking to achieve these effects for <option> and <button> using CSS and JavaScript. Any suggestions on how to do this? ...

I am facing an issue with Angular 14 and Webpack 5, where certain unnecessary nodejs modules seem to be hindering me from successfully serving

I have recently started working on a cutting-edge Angular 14 application. My current node version is v14.20.0, and npm version is 8.17.0. Despite my best efforts, I seem to be facing an issue with multiple nodejs dependencies being included in my project ...

Unable to retrieve data when utilizing SWR and Context API in Next.js

I'm currently working on fetching data from an API using SWR and dynamically setting the currency based on user preferences through context API. However, I'm facing issues as I am unable to view any data. Can someone please provide assistance or ...

Preventing form submission with JavaScript by implementing multiple validation checks

Preventing a form from submitting when validation returns false is possible. Here's an example: <form name="registration" action="registration.php" onsubmit="return validate()"> <!-- some inputs like: --> <input type="text" id="us ...

Pause file uploads, display modal popup, then resume uploading

Is there a way to pause file uploading in order to display file requirements before proceeding? For example, when a user clicks on "upload file", I would like to show them a modal window with details about the file they need to upload. Once they click ok, ...

Experience the power of TypeScript in a serverless environment as you transform your code from

I have some JavaScript code that needs to be converted to TypeScript. Currently, I have two files: API_Responses.js const Responses = { _200(data = {}) { return { statusCode: 200, body: JSON.stringify(data), }; } }; module.export ...

Double the Power of jQuery Live Search with Two Implementations on a Single Website

I recently implemented a JQuery Live Search feature that is working perfectly. Here is the JQuery script: <script type="text/javascript"> $(document).ready(function(){ $('.search-box input[type="text"]').on("keyup input", function(){ ...

Is there a way to incorporate an array into an array of arrays with jQuery?

I am working with an array shown below: var cString = [ ['1','Techdirt','www.techdirt.com'], ['2','Slashdot','slashdot.org'], ['3','Wired&apos ...

Obtaining objects from a Meteor collection on the server triggers a "Must have Fiber to proceed" error

As a beginner in creating meteor apps, I am working on a project that involves querying git issues from a specific repository. The goal is to generate tasks from these issues after retrieving them using the Github API. However, I keep encountering an error ...

The Bootstrap form validation is preventing the Ajax request from functioning properly

Having successfully implemented a form with Bootstrap validation, I encountered an issue where the AJAX Post method fails to execute after validation. The catch clause does not capture any errors and only the ajax portion of the code doesn't run. Belo ...

Tips for transferring a function from a Node.js server to a client

Hey everyone, I'm trying to achieve the following: On the Node server side: var fn = function(){ alert("hello"); } I am looking for a way to send this function to the client side. I am currently using AngularJS, but I am open to other solution ...

Having issues with Thymeleaf template not functioning properly when using inline JavaScript

I've encountered an issue when attempting to extract my function into a script within HTML. When written as shown below, it works perfectly: <input type="text" id="myInput" onkeypress="return confirm('Are you sure you want to delete ...

What is the best way to reload a React/TypeScript page after submitting a POST request?

I am working on a custom plugin for Backstage that interacts with Argo CD via API calls. To retrieve application information, I make a GET request to the following endpoint: https://argocd.acme.com/api/v1/applications/${app-name} If the synchronizati ...

Utilize Webpack to integrate redux-form as an external library

I currently have a range of imports in my project, such as: import {Field, reduxForm, FormSection, formValueSelector} from 'redux-form'; My goal is to treat the redux-form imports as an external library so that they do not get included in the b ...

Exploring the integration of JSONP with the unmatched features of Google

I am attempting to utilize the Google maps directions API by using jquery $.ajax. After checking my developer tools, it seems that the XHR request has been completed. However, I believe there may be an issue with the way jquery Ajax handles JSON.parse. Y ...

Encountered a TypeError in Angular printjs: Object(...) function not recognized

I'm currently working on integrating the printJS library into an Angular project to print an image in PNG format. To begin, I added the following import statement: import { printJS } from "print-js/dist/print.min.js"; Next, I implemented the pri ...

Synchronize data with Microsoft AJAX using force sync

When working with the Microsoft AJAX framework, I encounter a problem where my AJAX calls are asynchronous when I actually need them to be synchronous. I'm struggling to find a solution for this issue. In addition, I have been having difficulty findi ...

Safari has no issues running Javascript, but other browsers are encountering failures

I am facing an issue where the code is working on Safari but failing on other browsers, and I can't figure out why. You can find the html part and the main javascript part. The main issue at hand is: When executing the function downloadurl(url, fun ...

How can one effectively manage asynchronous behavior on the client side of a Meteor application?

My focus is on Meteor. I want to connect with the Facebook API through Meteor's HTTP in order to show images on Meteor's client side. I've come across suggestions like Fiber Futures, storing data in Sessions, and triggering a synchronous se ...

Inserting a variable into a JSON string

Within my code, I have a basic variable containing strings that are converted into a JSON object. My goal is to create an input field where a person's name can be entered and added to the text. The current setup looks like this var text = '{"st ...