1

I want to extend Response in Express to include custom functions. I want to add a function:

sendError(statusCode: number, errorMessage: string)

such that I can invoke it anywhere like

response.sendError(500, "Unable to process this request at the moment.")

Can you tell me how to achieve this? I looked at a few other questions like extension method on number in typescript but I still have some doubts:

  1. How do I extend a function on Response when it doesn't have a prototype?
  2. Where do I put the definition of the extended function? Can I make a separate file containing all the definitions?
  3. Do I have to assign this function to every object of Response to use it or can I just define it once and use it on all response objects everywhere in my project?

Please help me with this. I am new to TypeScript so please excuse any mistakes I might have made in my question :)

Ashu
  • 2,970
  • 1
  • 19
  • 31
  • There is an Express prototype for the response object that you can add methods to once and they will be available everywhere. That's how Express adds its own methods to the generic http response object. I'm not at the right computer right now to be able to look it up and file an answer, but that's the direction you should go. You can find the info in the Express source code. – jfriend00 Jul 05 '21 at 16:48
  • @jfriend00 I could, but as I said, I am new to typescript. But I took a look as you suggested and I found a response variable declaration in the source code and it's doc comment mentions that it's a prototype, but I seem to not be able to get it working. I get the error "... is not a function" during runtime. – Ashu Jul 05 '21 at 17:29

2 Answers2

2

Express uses its own prototype for both the request and the response objects that inherits from the http version.

You can find the Express response prototype on the express object as express.response.

In plain vanilla Javascript, this will work:

const express = require('express');
const app = express();

// add my own method to the response prototype so it can be used
// by any route
express.response.mySend = function(data) {
    // this here is the live res object for this particular request
    console.log("about to send data with my custom response method");
    this.send(data);
}

app.get("/", (req, res) => {
    res.mySend("hello");
});

app.listen(80);

To make this work in TypeScript, you will need appropriate type bindings for the express.response object that allows you to do this. I'm not a TypeScript person so you'll have to take on that part yourself.

FYI, the request object is express.request and it can be used similarly.


You can also add your own methods in middleware like this, but I would assume this is slightly less efficient (if that even matters):

const express = require('express');
const app = express();

// add my own method to the response prototype so it can be used
// by any route
app.use((req, res, next) => {
    res.mySend = function(data) {
        // this here is the live res object for this particular request
        console.log("about to send data with my custom response method");
        this.send(data);
    });
    next();
});

app.get("/", (req, res) => {
    res.mySend("hello");
});

app.listen(80);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @ashu - Did either of these help you? – jfriend00 Jul 06 '21 at 21:00
  • These somewhat helped me in pointing in the right direction, and I have a working solution. Thanks a lot for your help :) Should I post the solution I came up with since your answer is actually in Javascript and technically not the right solution to the question? @jfriend00 – Ashu Jul 07 '21 at 08:00
0

You want to create a custom definition, and use a feature in Typescript called Declaration Merging.

Method 1: Middleware

You can use a middleware and add the declarations for the new methods

  1. Create folder middlewares and middlewares/typings

  2. Create file middlewares/typings/express-extended-response.d.ts

    declare namespace Express {
      export interface Response {
        sendError(statusCode: number, errorMessage: string): void
      }
    }
    
  3. Create file middlewares/express-extended-response.ts

    import { Request, Response, NextFunction } from 'express'
    
    export function extendedResponse(req: Request, res: Response, next: NextFunction) {
      res.sendError = function (statusCode, errorMessage) {
        this.status(statusCode).send(errorMessage)
      }
      next()
    }
    
  4. In you app.ts or index.ts

    import express from 'express'
    import { extendedResponse } from './middlewares/express-extended-response'
    
    const app = express()
    app.use(extendedResponse)
    // From now on you can use the 'sendError' method on every object of type Response
    
    app.get('/', (req, res) => {
      res.sendError(404, 'Not found')
    })
    

Method 2: Add function to express.response

You can add the methods to the express.response object

  1. Create folder typings

  2. Create file typings/express-extended-response.d.ts

    declare namespace Express {
      export interface Response {
        sendError(statusCode: number, errorMessage: string): void
      }
    }
    
  3. In you app.ts or index.ts

    import express from 'express'
    
    const app = express()
    
    app.response.sendError = function (statusCode, errorMessage) {
      this.status(statusCode).send(errorMessage)
    }
    
    app.get('/', (req, res) => {
      res.sendError(404, 'Not found')
    })
    

ts-node

if you want to use ts-node, you must specify the --files flag or include it in your tsconfig.json

ts-node --files app.ts
ts-node --files index.ts

tsconfig.json

{
  "ts-node": {
    "files": true
  },
  "compilerOptions": {
    "esModuleInterop": true,
     ...
  }
}
ts-node app.ts
ts-node index.ts

Demonstration

autocomplete