What is the proper type for a request handler of a firebase HttpsFunction? - node.js

I need to split my functions into multiple files.
This is my index.ts
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
I need it to be something like:
import helloWorldHandler from "./handlers/helloWorldHandler"
export const helloWorld = functions.https.onRequest(helloWorldHandler);
So what should I type the helloWorldHandler request handler?
const helloWorldHandler : ??? = async (req,res) => {
const result = await someApi();
functions.logger.info("Hello logs!", {structuredData: true});
res.send("Hello from Firebase!");
};
export default helloWorldHandler;
I tried:
import * as functions from "firebase-functions";
const helloWorldHandler : functions.HttpsFunction = async (req,res) => { ... };
But I'm getting this error:
Type '(req: Request, res: Response) => Promise<Response>' is not assignable to type 'HttpsFunction'.
Property '__trigger' is missing in type '(req: Request, res: Response) => Promise<Response>' but required in type 'TriggerAnnotated'.
The onRequest() method, the one that should take the handler as a parameter, does not seem to give it a proper type name, rather than a function signature. Do I need to create an alias for that?

The type functions.HttpsFunction is the return type of functions.https.onRequest(), and not the argument to it. A function of this type is exported by your code and defines what needs to be deployed by the Firebase CLI (the region, memory size and so on are stored in the __trigger property).
As you want the type of the first argument to functions.https.onRequest(), you are instead looking for the type:
type HttpsOnRequestHandler = (req: functions.https.Request, resp: functions.Response<any>) => void | Promise<void>
But rather than hard-code this, you can extract it from the Firebase Functions library using either:
import * as functions from "firebase-functions";
type HttpsOnRequestHandler = Parameters<typeof functions.https.onRequest>[0];
or
import { https } from "firebase-functions";
type HttpsOnRequestHandler = Parameters<typeof https.onRequest>[0]
Note: If your code doesn't use the firebase-functions library itself, you can tell TypeScript that you only want its types using import type * as functions from "firebase-functions"; and import type { https } from "firebase-functions"; as appropriate; this removes the import from the compiled JavaScript as its not needed for running the code.

Here is what I've come up with. Still interested in knowing if is there an out-of-the-box type alias for this.
index.ts
import * as functions from "firebase-functions";
import helloWorldHandler from "./handlers/helloWorldHandler"
type Req = functions.https.Request
type Res = functions.Response
// WILL USE THIS EXPORTED TYPE
export type RequestHandler = (req: Req, res: Res) => void | Promise<void>
export const helloWorld = functions.https.onRequest(helloWorldHandler);
helloWorldHandler .ts
import { RequestHandler } from "../index";
const helloWorldHandler : RequestHandler = async (req,res) => { ... };

I'd suggest simply using the provided HttpsFunction type:
import * as functions from "firebase-functions";
import type { HttpsFunction } from "firebase-functions";
export const foo: HttpsFunction = functions.https.onRequest((req, res) => {
// the types of req and res are known here...
res.send("HELO");
});

I managed to run the new Firebase Functions v2 in TypeScript with
// import from a specific subpackage
import {onRequest} from "firebase-functions/v2/https";
export default onRequest((request, response) => {
response.send("Hello from Firebase!");
});

Related

How to mock in express with Typescript and Mocha library

How to mock 'Request' in Mocha using express with Typescript?
Current solution is following:
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = {
get: () => {
return null;
},
};
expect(isAuth(req as Request, {}, () => {}));
});
});
I have got an error Conversion of type '{ get: () => null; }' to type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Is forcing 'unknown' type the only solution to this problem?
You can use node-mocks-http package to create the mockups of the request and response objects for express routing functions.
E.g.
import { expect } from "chai";
import {Request, Response} from 'express';
import httpMocks from 'node-mocks-http';
const isAuth = (req: Request, res: Response) => {
// your code under test
}
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = httpMocks.createRequest();
const res = httpMocks.createResponse()
expect(isAuth(req, res));
});
});
The return value of httpMocks.createRequest() API is MockRequest type, its generic parameter is constrained by express Request type. The Request type is a subset of the MockRequest type, so it matches the Request type.

correlationId dosn't exist in node.js

I am trying to add correlationId using express-correlation-id. I am exactly following the page: https://www.npmjs.com/package/express-correlation-id. I've imported the express-correlation-id pkg and have found it in the package.json and node modules.
But when i tried to get req.correlationId(), it always said:
Property 'correlationId' dosn't exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.
I'm using TypeScript and Express, this is what the code looks like:
import correlator = require("express-correlation-id");
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId(); // where the error occurs
});
Since this package is written in js language, there are no type definition files for typescript, so you need to extend the Express.Request interface by yourself, add the correlationId method to the interface.
Package versions:
"typescript": "^3.9.7"
"express-correlation-id": "^1.3.1",
"express": "^4.17.1",
E.g.
server.ts:
import express from 'express';
import correlator = require('express-correlation-id');
declare global {
namespace Express {
export interface Request {
correlationId(): string;
}
}
}
const app = express();
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId();
});
This is because the property correlationId doesn't exist in the typing of req.
So, if you see the typing of req is:
export interface Request<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends http.IncomingMessage, Express.Request {
So, you must create a new Request type with the new functions that you're going to use. For your case:
import express from "express";
import correlator = require("express-correlation-id");
import { ParamsDictionary, Request } from "express-serve-static-core";
import { ParsedQs } from "qs";
const app = express();
interface CustomReq<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends Request<P, ResBody, ReqBody, ReqQuery> {
// extended options
correlationId: () => any;
}
app.use(correlator());
app.get('/', (req: CustomReq, res) => {
req.correlationId(); // OK
});
As you see in the code above, my new Request is CustomReq and I use the new type for the req.

How to inject dependencies into Firebase/Google Cloud Functions? (unit & integration testing)

I don't know whether my question is really related to Firebase Cloud Functions, but I came across this problem trying to test my Firebase Cloud Functions.
Let's say I have a Firebase Cloud function written in NodeJS:
function.ts
import * as functions from "firebase-functions"
const admin = require("firebase-admin")
import * as authVerifier from "../../auth/authVerifier"
export default functions.https.onRequest(async (req, res) => {
let authId
try {
authId = await authVerifier.identifyClientRequest(req)
} catch (err) {
console.error(`Unauthorized request error: ${err}`)
return res.status(401).send({
error: "Unauthorized request"
})
}
}
Usually I have an interface and can easily mock any class I want to test it.
And, for example, authVerifier looks like:
authVerifier.ts
import * as express from "express"
export async function identifyClientRequest(req: express.Request) {
return true // whatever, it doesn't really matter, should be real logic here
}
I'm trying to test function.ts and I only can pass res and req into it, e.g:
function.test.ts
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = { }
await myFunctions(req as express.Request, res as express.Response)
// assert that authVerifier.identifyClientRequest(req) called with passed req
})
So the question is: how can I mock authVerifier.identifyClientRequest(req) to use different implementations in function.ts and in function.test.ts?
I don't really know NodeJS/TypeScript, so I wonder if I can import another mock class of authVerifier for test or something like that.
Ok, seems like I found the answer. I'll post it here just in case.
Using sinonjs, chai we can stub our class (authVerifier in that case) to return necessary results:
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
import * as authVerifier from "../../../src/auth/authVerifier"
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = mocks.createMockResponse()
const identifyClientRequestStub = sinon.stub(authVerifier, "identifyClientRequest");
const authVerifierStub = identifyClientRequestStub.resolves("UID")
await updateUser(req as express.Request, res as express.Response)
assert.isTrue(authVerifierStub.calledWith(req))
})
And the result is:

In attempting to minimize cold start time for Firebase Cloud functions, how to import a class for use inside one function? [duplicate]

This question already has an answer here:
What is the correct way to dynamically import a class inside of a Firebase Cloud function using typescript?
(1 answer)
Closed 3 years ago.
If I have a module that is only required for one of my Firebase Cloud functions, this Firebase Tutorial suggests importing that module inside just the function that needs it, in order to minimize cold start time for all other functions in a project.
This makes sense, but is it also possible to import a class which contains its own set of dependencies inside of a function?
I have a need to use Bcrypt but only in two of my functions. So I would rather not have to load it for all of my other cloud functions where it is not needed.
In my application, I have the following import:
import BcryptTool from './classes/bcrypt'; // <--- only needed in 2 functions
Here is the contents of bcrypt.ts:
import * as bcrypt from 'bcryptjs';
export default class BcryptTool {
public static hashValue(value: string, rounds: number, callback: (error: Error, hash: string) => void) : void {
bcrypt.hash(value, rounds, (error:any, hash:any) => {
callback(error, hash);
});
}
public static compare(value: string, dbHash: string, callback: (error: string | null, match: boolean | null) => void) {
bcrypt.compare(value, dbHash, (err: Error, match: boolean) => {
if(match) {
callback(null, true);
} else {
callback('Invalid value match', null);
}
});
}
}
And finally, in my Firebase Cloud functions index.ts:
const express = require('express');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({ origin: true });
admin.initializeApp();
const util = express();
const api = express();
...
import BcryptTool from './classes/bcrypt'; // <-- when i import here, calls to its methods within my functions work as expected
...
util.use(cors);
util.post('/verify', async (request: any, response: any) => {
// according to Doug's answer below i should be able to attempt to import here as a solution using a dynamic import expression like so:
const BcryptTool = await import('./classes/bcrypt');
// but the following subsequent call to .compare() fails
BcryptTool.compare(...)
// VS Code hinting shows an error: Property 'compare' does not exist on type 'typeof import('FULL/PATH/TO/CLASS/classes/bcrypt')'
});
api.use(cors);
api.post('/endpoint/foo', async (request: any, response: any) => {
// I do not need Bcrypt here
});
api.post('/endpoint/bar', async (request: any, response: any) => {
// I do not need Bcrypt here
});
Is this not possible? Am I just doing it all wrong?*
Sure, you can async (dynamic) import anywhere you want in TypeScript code. The imported symbols will be visible in the scope where you imported it, and nowhere else. It doesn't matter what the module contains.
(Posted answer on behalf of the question author, to move it from the question post).
I was not importing the class correctly. The reason along with a solution is outlined here.

How to add Typescript definitions to Express req & res

I have a set of controller functions for my REST API and I'm getting lots of the following
error TS7006: Parameter 'req' implicitly has an 'any' type.
Likewise for res. I've been playing around with typeings etc. but with no success. For example the Request type parameter below does NOT work.
Here is an example of the controller files. The reference path is correct.
/// <reference path="../../../typings/tsd.d.ts" />
/* globals require */
"use strict";
exports.test = (req : Request, res) => {
I tried adding import * as express from "express"; into the file - I don't need it normally as these functions are exported and use by index.js which actually implements the routing.
And this is tsd.d.ts
/// <reference path="requirejs/require.d.ts" />
/// <reference path="express/express.d.ts" />
/// <reference path="mime/mime.d.ts" />
/// <reference path="node/node.d.ts" />
/// <reference path="serve-static/serve-static.d.ts" />
/// <reference path="bluebird/bluebird.d.ts" />
/// <reference path="mongoose/mongoose.d.ts" />
You can use ES6 style named imports to import only the interfaces you need, rather than import * as express from 'express' which would include express itself.
First, make sure you have installed the type definitions for express (npm install -D #types/express).
Example:
// middleware/authCheck.ts
import { Request, Response, NextFunction } from 'express';
export const authCheckMiddleware = (req: Request, res: Response, next: NextFunction) => {
...
};
// server.ts
import { authCheckMiddleware } from './middleware/authCheck';
app.use('/api', authCheckMiddleware);
Currently using TypeScript 2.3.4 and #types/express 4.0.36.
It can be daunting to type the arguments every time you need to write middleware functions so you can just type the whole function directly too.
npm i #types/express --save-dev ("#types/express": "^4.17.0")
After installing typings..
// This can be shortened..
import { Request, Response, NextFunction } from 'express';
export const myMiddleware = (req: Request, res: Response, next: NextFunction) => {
...
};
// to this..
import { RequestHandler } from 'express';
export const myMiddleware: RequestHandler = (req, res, next) => {
...
};
// or in case it handles the error object
import { ErrorRequestHandler } from 'express';
export const myMiddleware: ErrorRequestHandler = (err, req, res, next) => {
...
};
What I've found is that you can leverage TypeScript generics very effectively to create a wrapper around the Express Request type.
You can declare something that looks similar to this in an interfaces file/folder:
import { NextFunction, Request, Response } from 'express';
type TypedRequest<
ReqBody = Record<string, unknown>,
QueryString = Record<string, unknown>
> = Request<
Record<string, unknown>,
Record<string, unknown>,
Partial<ReqBody>,
Partial<QueryString>
>;
export type ExpressMiddleware<
ReqBody = Record<string, unknown>,
Res = Record<string, unknown>,
QueryString = Record<string, unknown>
> = (
req: TypedRequest<ReqBody, QueryString>,
res: Response<Res>,
next: NextFunction
) => Promise<void> | void;
TypedRequest is effectively a wrapper around Express' Request interface, and populates it with the generics that you pass it, but are also optional (note Record<string, unknown>. It then also applies a Partial around each of the generics (you probably want to make this a DeepPartial instead)
ExpressMiddleware takes in 3 optional generics ReqBody Res and QueryString. These are used to construct a function signature that resembles what middlewares/controllers should look like.
The above then allows you to strongly type & consume as follows:
import { ExpressMiddleware } from '../interfaces/ExpressMiddleware';
type Req = { email: string; password: string };
type Res = { message: string };
export const signupUser: ExpressMiddleware<Req, Res> = async (req, res) => {
/* strongly typed `req.body`. yay autocomplete 🎉 */
res.json({ message: 'you have signed up' }) // strongly typed response obj
};
I hope this helps someone. It's made a massive difference to my Express experience.
The best way to do this is like so.
// create some shared types in your project
import { Request, Response, NextFunction } from 'express';
export type MiddlewareFn = (req: Request, res: Response, next: NextFunction) => void;
// then use the above types:
import {MiddlewareFn} from './my-types.d.ts'
router.get('/foo', <MiddlewareFn>function (req, res, next) {
// ....
});
Rather than installing types(#types/express) you should also define request parameters. Since every parameter is string, interface should base on dictionary.
Here is an inline route handler:
interface GetParams {
[key: string]: string
paramName: string
}
router.get<GetParams>('/:paramName', (req, res) => {
res.send('Parameter is ' + req.params.paramName)
})
Use:
req: Express.Request
res: Express.Response

Resources