What are the steps to correctly configure a webhook endpoint API for managing RevenueCat events using Firebase Functions?

I've encountered an issue while trying to set up a webhook endpoint API directly from RevenueCat's documentation.

Even though my code closely resembles the example in the documentation, I am puzzled by the error that keeps popping up. Unfortunately, I lack sufficient experience in this area to troubleshoot it effectively. Below is the specific error message:

(parameter) res: functions.Response<any>

Argument of type '(req: Request, res: Response<any>) =>... specified properties from Promise<void>': then, catch, finally, [Symbol.toStringTag]ts(2345)

To be honest, I'm not entirely sure what changes are being requested. Do you have any suggestions? Here is the snippet of my code:

import * as functions from 'firebase-functions'
import { PubSub } from '@google-cloud/pubsub'

const pubsubClient = new PubSub({projectId: '<PROJ_ID>'})

function isAuthorized(req: functions.https.Request) { 
    // Check authorization header
    if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) { 
        return false
    }

    const authToken = req.headers.authorization.split('Bearer ')[1]
    if (authToken !== '<MY_AUTH_TOKEN>') { 
        return false
    }

    return true
}

// Respond to incoming message
export const revenueCatApi = functions.https.onRequest((req, res) => { // *** ERROR DETECTED HERE

    // Only allow POST request
    if (req.method !== 'POST') { 
        return res.status(403).send('Forbidden')
    }

    // Make sure the auth key matches what we set in the Revenue Cat dashboard
    if (!isAuthorized(req)) { 
        return res.status(401).send('Unauthorized')
    }

    const rc = req.body as RCEvent
    var topic: RCTopic = ''

    switch (rc.event.type) { 
        case 'INITIAL_PURCHASE':
            topic = 'rc-initial-purchase'
            break
        case 'NON_RENEWING_PURCHASE':
            topic = 'rc-non-renewing-purchase'
            break
        case 'RENEWAL':
            topic = 'rc-renewal'
            break
        case 'PRODUCT_CHANGE':
            topic = 'rc-product-change'
            break
        case 'CANCELLATION':
            topic = 'rc-cancellation'
            break
        case 'BILLING_ISSUE':
            topic = 'rc-billing-issue'
            break
        case 'SUBSCRIBER_ALIAS':
            topic = 'rc-subscriber-alias'
            break
        default:
            console.log('Unhandled event type: ', rc.event.type)
            return res.sendStatus(200)
    }

    // Set the pub/sub data to the event body
    const dataBuffer = Buffer.from(JSON.stringify(rc))

    // Publishes a message
    return pubsubClient.topic(topic)
        .publish(dataBuffer)
        .then(() => res.sendStatus(200))
        .catch(err => { 
            console.error(err)
            res.sendStatus(500)
            return Promise.reject(err)
        })
})

exports.handleInitialPurchase = functions.pubsub
    .topic('rc-initial-purchase')
    .onPublish(async (message, context) => {
        ...
    })

/* Other pubsub functions below */

RCEvent:

interface RCEvent { 
    api_version: string
    event: { 
        aliases: string[]
        app_id: string
        app_user_id: string
        country_code: string
        currency: string
        entitlement_id: string
        ... // Other interface properties here
    }
}

Answer №1

The error message indicates that TypeScript has identified the signature of your function as follows:

(req: Request, res: Response<any>) => Response<any> | Promise<void | Response<any>>

This means the function takes a Request and Response as arguments, and can potentially return either Response<any> or

Promise<void | Response<any>>
. However, this does not align with the requirement for the function to only return void or Promise<void>.

Your function currently returns three possible outcomes:

return res.status(403).send('Forbidden')

return res.status(401).send('Unauthorized')

return pubsubClient.topic(topic)

The first two outcomes are likely not intended as return values; instead, you are probably aiming to send a response and halt execution early. The third outcome represents a promise.

Avoid returning the result of

res.status(403).send('Forbidden')
; simply return null if you wish to terminate the function prematurely without any further processing.

res.status(403).send('Forbidden')
return null

Apply this approach to both instances where you do not have a promise to handle. By doing so, your function will accurately reflect that it only returns a promise or null, meeting the TypeScript criteria.

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

What is the reasoning behind exporting it in this manner in the index file?

As I was going through a tutorial about nests, there was this step where the instructor made a folder named "dtos" and inside it, they created two dto files (create-user.dto and edit-user.dto). Following that, they also added an index file in the same fold ...

Deciphering TS2345: "The argument supplied, known as 'typeof MyComponent', cannot be assigned to the specified parameter type"

I am facing an issue while attempting to integrate a Typescript React component with react-onclickoutside. The error message that I encounter is as follows: TS2345: Argument of type 'typeof MyComponent' is not assignable to parameter of type &apo ...

Steps for utilizing response data as parameters for useInfiniteQueryHere is how you can make

On the specific page where I am implementing useInfiniteQuery const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery( ['posts', searchState], ({ pageParam = 1 }) => post.search({ . ...

Accessing the form element in the HTML outside of the form tag in Angular 2

I am attempting to achieve the following: <span *ngIf="heroForm?.dirty"> FOO </span> <form *ngIf="active" (ngSubmit)="onSubmit()" #heroForm="ngForm"> <div class="form-group"> <label for="name">Name</label& ...

What is the reasoning behind TypeScript allowing the reading of an undefined variable within a closure?

While exploring, I came across this detail that seems undocumented. Here's some legitimate TypeScript code that results in undefined being output: let x: number; const f= () => { const y= x; console.log(y); } f(); Playground Within the fu ...

The Node.js application successfully operates on a local environment, however encounters issues when attempting to run on docker resulting in an error message stating "sh

Despite successfully building the docker image, I am facing difficulties getting the container to run. Below is the content of the package.json file: { "name": "linked-versions-viewer", "version": "1.0.0", &quo ...

Unable to modify MaxResults for Cloud Vision Labeling

I am currently utilizing Google Vision to analyze images and the results are limited to the top 10. I am interested in increasing this limit to 50. How can I achieve this? Below is the code snippet I am working with: const client = new vision.ImageAnnotat ...

What is the best way to display toastr messages in an Angular application?

Can you guide me on how to include toastr in an angular app? Currently, I am enrolled in the Angular Fundamentals course and trying to use toastr.success within my export class: handleThumbnailClick(eventName){ toastr.success(eventName) } But, I kee ...

What is the best way to mock an internal function within my route using sinon?

Currently, I have my own internal function defined in the greatRoute.ts file: //in greatRoute.ts async function _secretString(param: string): Promise<string> { ... } router .route('/foo/bar/:secret') .get( async (...) => { ...

Assigning function types to functions that accept generics: A guide

type FormValidationHandler<FormValues> = (params: { formValues: FormValues, debugName?: string, }) => { isValid: boolean, fieldErrors: Record<string, unknown>, formError: string, } const validateForm: FormValidationHandler = param ...

Angular 2 partial static routing parameters with customizable features

Can an angular 2 routing configuration include a partial-static parameter? Currently, I am using a classic parameter setup like this: const routes: Routes = [ { path: ':type/fine.html', pathMatch: 'full', redirectTo: &ap ...

Unable to utilize a generic model in mongoose due to the error: The argument 'x' is not compatible with the parameter type MongooseFilterQuery

Attempting to include a generic Mongoose model as a parameter in a function is my current challenge. import mongoose, { Document, Model, Schema } from 'mongoose'; interface User { name: string; age: number; favouriteAnimal: string; ...

What is the reason for `downlevelIteration` not being enabled by default?

When directing towards ES5 and using the spread operator ... to convert an Iterator to an Array, it prompts the need for the -downlevelIteration compiler option. Enabling this option allows the spread operators to function without any errors. I'm cur ...

Is it possible to use Firebase auth.user in order to retrieve the signed-in user directly?

As I develop a webapp with NextJS v13.4 and firebase as my backend using the firebase web modular api, I came across a statement in the documentation: "The recommended way to get the current user is by setting an observer on the Auth object." ...

Is a package I overlooked? The 'findOne' property is not found within the 'Schema<Document<any, {}>, Model<any, any>, undefined>'

I have taken over the responsibility of maintaining the websites at my company, and I am encountering the error message (Property 'findOne' does not exist on type 'Schema<Document<any, {}>, Model<any, any>, undefined>' ...

Converting SQL COUNT query to angularfire2: A guide on translating Firebase / angularfire2

In my firebase database, I have the following structure: "users" : { "USER1_ID" : { "email" : "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="41343224337001393939396f222e2c">[email protected]</a>", ...

Error in Nuxt/TypeScript: Unable to retrieve this - TS2339: Property 'XXX' is not found on type

I encountered an issue while working with Nuxt.js and TypeScript. In my project, I rely on dependencies such as axios or nuxt-i18n. As an example, let's focus on Axios. I followed the configuration outlined in the official documentation for nuxt/axios ...

Error in Nodejs when trying to upload tasks to Cloud Firestore: Authentication error - Error: connection timeout

I have been working on a function that handles API calls and fetches JSON data from a large database in a specific sequence using offsets. After parsing the JSON response, the extracted data is then uploaded to our Cloud Firestore server. Using Nodejs (No ...

The form validation feature in Element UI is having issues when used with the Vue 2 Composition API

I am currently developing a form that utilizes element UI's form validation within the vue 2 composition API environment. This form includes a table nested inside, making its structure somewhat complex. ... <div> <el-form ref=" ...

Implementing cursor-based pagination in Next.js API Route with Prisma and leveraging the power of useSWRInfinite

I am working on implementing cursor-based pagination for a table of posts using Next.js API Route, Prisma, and useSWRInfinite. Currently, I am fetching the ten most recent posts with Prisma in a Next.js API Route and displaying them using useSWR, sorted b ...