Stripe connect http function firebase - node.js

I have created a cloud function with the following:
I'm trying to make an app/platform where a user can sign up for an account using Stripe Connect. I have a database with Firestore and the backend is using firebase cloud functions and nodejs.
const functions = require('firebase-functions');
const express = require('express');
const stripe = require('stripe')('sk_test_51XXX');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createStripeUser = functions.https.onRequest((req, res) => {
var auth_code = stripe.oauth.token({
grant_type: 'authorization_code',
code: req.query.code,
});return res.send("Please close this page")
}
)
My problem is that I have an error where the req, res is. I don't know why there is an error, is the request wrong? What can I do so that I get no error and fix the problem?
If I hover on req, I get this message
(parameter) req: any
Parameter 'req' implicitly has an 'any' type.ts(7006)
I am using typescript

It seems you are using Typescript and req, res are implicitly of type any. You can import Request and Response from Express as shown below:
import { Request, Response } from "express"
export const createStripeUser = functions.https.onRequest((req: Request, res: Response) => {
// ...
})
And yes, explicitly adding any like (req: any, res: any) will remove that error too but that isn't the best thing to do.

Related

cors error when using onRequest cloud functions

This is my function
export const testFunction = functions.https.onRequest((req, res) => {
const text = req.body.text;
res.set("Access-Control-Allow-Origin", "*");
res.send({text: text});
});
i've tried using const cors = require('cors')({origin: true});
I keep getting this as a response. Does anyone know why?
Consider importing like this,
const cors = require('cors')({origin: true});
And try running the below function using firebase deploy –only functions :
const functions= require("firebase-functions");
const cors = require('cors')({origin: true});
exports.testFunction = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const text = req.body.name;
res.send({text:text});
});
});
And then send request using :
curl -X POST "https:/region-projectID.cloudfunctions.net/function-name" -H "Content-Type:application/json" --data '{"name":"Keyboard Cat"}'
The output I am getting in the console is :
And when I click on the Cloud Function URL endpoint, my output is an empty {}.
If you try res.send(“Hello World”) in place of res.send({text:text}), you will get the output in the browser as Hello World but since our Cloud Function performs some contrived operation using data passed in the request body.This could result in an error at run time if the property name is null or undefined.
And indeed, if we deploy this new function and then attempt to call it from our web app without updating our request we do get an error. However, it might not be the error you’d expect.
It’d be easy to assume that we somehow misconfigured our CORS policy. Infact swapping cors() to cors({ origin: '*'}) to cors({ origin: true}) all to no avail. Only when we view the logs for our Cloud Function do we get a useful error message.
So try sending a POST request with the –data flag, or if you are using Postman, send the data and the parameters. Then only you would be able to have an output, if you still see the CORS error, check if your function is handled well or your nested request body attribute is not undefined/null. Sometimes CORS errors are not always CORS!

404 when trying to get response from backend using axios

I am trying to get function result from backend to frontend via axios but it returns 404 every time.
I managed to send request to backend and activate function but on geting result it started returning 404
route in app.ts
import cardRoute from './routes/test';
const app = express();
app.use('/test', cardRoute);
./routes/test.ts (backend)
function test_load() returns string
import express from 'express';
import { test_load } from '../cardpull';
const router = express.Router();
router.post('./test-bed',
async (req, res) => {
let cards = test_load()
res.send(cards);
},
);
export default router;
Frontend call
async function GetCard() {
var cards = await axios.post<string>('/test/test-bed');
return cards;
};
your route is not valid
router.post('./test-bed',
async (req, res) => {
let cards = test_load()
res.send(cards);
},
);
should be:
router.post('/test-bed',
async (req, res) => {
let cards = test_load()
res.send(cards);
},
);
and on your axios URL, maybe you need to include the host and port because if you define only the endpoint, it will hit your frontend host and port,
example if you open express on localhost:3000
then the axios will be
axios.post('http://localhost:3000/test/test-bed')
note: I didn't write the answer with typescript, but with javascript style should be clear enough.

Sending data to Nodejs backend in Reactjs for API call

I'm trying to send data to my Nodejs server (hosted on Firebase), from Reactjs to retrieve data from an API call.
It worked when the API url was hard coded, so the issue should be in sending the data from React to Node. I'm currently trying to have it only return one request, but once I'm able to do that, I will be trying to fetch multiple requests.
The result is to populate the stocks state with the response.
My React code looks like this:
class Table extends Component {
constructor (props)
{
super(props);
this.state = {
favorites: ['APPL'],
stocks: []
};
}
componentDidMount() {
// http is adjusted for stackoverflow
fetch('http://localhost:5001/', {
// new part:
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
favorites: this.state.favorites})
// old part - worked before i tried to send data to the backend:
})
.then((response) => response.json())
.then(stockList => {
this.setState({ stocks: stockList });
console.log(stockList);
});
}
Node code:
const functions = require("firebase-functions");
const express = require("express");
const fetch = require("node-fetch");
const cors = require("cors");
const app = express();
app.use(cors({origin: true}));
app.get('/', async (request, response) => {
// new line that was ment to catch the data from frontend.
const favorites = request.body.favorites
// web and key is the rest of the API call.
const url = web+favorites+key;
const fetchResponse = await fetch(url);
const symbol = await fetchResponse.json();
response.json(symbol);
});
In order to get the data from the frontend you will need to make a post endpoint instead in your Node server and send a GET request inside that method.
const functions = require("firebase-functions");
const express = require("express");
const fetch = require("node-fetch");
const cors = require("cors");
const app = express();
app.use(cors({origin: true}));
app.post('/', (req, res) => {
// here is how you get the POST data from the body
console.log(req.body.favorites)
// send the data from the POST request to the API through a GET request here
})
Now you need to make a GET request where I put the comment, you can use the simpler https node.js library for that.
In my question, I'm trying to use Firebase functions to handle the link to an external API and use the data from that API in my frontend.
After some though and help from Stackoverflow, I have found that it might not be an ideal way to do it. I was basically trying to add a layer between the frontend and the API, but that is not necessary, as it is possible to reach the API directly in React. This will remove the function from Firebase, meaning less steps, less code and less fees.
So for every instance in the state.favorites, the correlated data is pulled from the API and stored in the state.stocks.
This piece of code did the trick:
class Table extends Component {
constructor (props)
{
super(props);
this.state = {
favorites: ['aapl', 'arvl'],
stocks: []
};
}
componentDidMount() {
this.state.favorites.map((favorites, index) => {
fetch(`API_website${favorites}/(API_token`)
.then((response) => response.json())
.then(stockList => {
this.setState({ stocks: stockList });
console.log(stockList);
});
})
}

Jest with Typescript error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout

I'm studying how to create some tests using the Jest with Nodejs, i'm actually using typescript.
When I try to run a simple test, by checking the status of the response, it shows the following error:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
What could I do?
Here's my following codes:
session.test.ts =>
const request = require('supertest');
import app from '../../src/server';
describe('Authentication',() => {
it('should authenticate with valid credentials',async() =>{
const response = await request(app)
.post('/sessions')
.send({
email: "myemail#gmail.com",
password: "123456"
})
await expect(response.status).toBe(200);
});
});
SessionController.ts =>
import {Request, Response} from 'express';
export default class SessionController{
async store(request: Request, response: Response){
return response.status(200);
}
}
server.ts =>
import express from 'express';
import routes from './routes';
require("dotenv").config({
path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
});
const app = express();
app.use(express.json());
app.use(routes);
app.listen(3333);
export default app;
and routes.ts:
import express from 'express';
import UsersController from './controllers/UsersController';
import SessionController from './controllers/SessionController';
const routes = express.Router();
const usersControllers = new UsersController();
const sessionController = new SessionController();
routes.post('/users',usersControllers.create);
routes.post('/sessions',sessionController.store);
export default routes;
at my SessionController.ts, I had to put the following:
import {Request, Response} from 'express';
export default class SessionController{
async store(request: Request, response: Response){
return response.status(200).send()
}
}
I forgot to send haha
The first thing is to check if there are some error in the request, or (more likely) if it remain in a pending state, because 5 seconds are tons of time.
Anyway you can specify the test timeout like follow
describe('Authentication',() => {
it('foobar', async function () { // no arrow function
this.timeout(10000)
await myFunc()
});
});
I am not sure you are actually 'completing' the request using the supertest API.
The fluent chaining approach of supertest allows you to carry on adding 'steps' to the HTTP request before actually dispatching it. Unfortunately, send() is preparing a send step for when you dispatch. It doesn't actually dispatch as you can see from this superagent example in which many further configuration steps follow send() and it's only end() which runs them all.
In several supertest examples I saw there is a chained 'expect' call which would presumably also trigger the actual HTTP post.
Equally, the 'end()' docs at https://github.com/visionmedia/supertest#endfn say...
Perform the request and invoke fn(err, res)
This indicates to me that until you send a 'finalising' call, there won't be a request.

Is there any way to serve binary file input(stream?) directly to multer middleware(for unit test purpose)?

I'm trying to write test codes on multer and my service.
As multer is an express's middleware, multer is intended to process the request from client and automatically handle the file and put it in req.file.
But I wonder if there is any way without making real HTTP request, to pass binary file or stream to multer middleware so that I can check whether the file is saved in local disk properly or the file's size is put afterward in req.file.
My code as middleware module of multer is below, just for reference:
import { Request, Response, NextFunction } from 'express';
import multer from 'multer';
const multerMiddleware = (dest: string) => (req: Request, res: Response, next: NextFunction) => {
const upload = multer({
storage: multer.diskStorage({
destination: dest,
filename: (req, file, cb) => {
const { videoId } = req.body;
const filename = `${videoId}-${file.originalname}`;
cb(null, filename);
}
})
});
upload.single('file')(req, res, next);
}
export default multerMiddleware;
Or, is it the best way to do just make a test-purpose route in Express app and test with it? Like below:
app.post('/testMulter', multerMiddleware('temp/'), (req, res, next) => {
res.status(200);
res.json(req.file);
// ...
});
// in test codes
import request from 'supertest'
import App from '../app.ts'
it('multer middleware download files well', async (done) => {
const app = new App.app;
const response = await request(app)
.post('/testMulter')
.attach('file', './test.mp4');
expect(response.file.filename).toBe('test.mp4');
})
Thank you in advance.
So you want to do an integrational test but without HTTP stuff.
According to the provided code, only req.body is used. In terms of the unit testing, it is simple to mock such Request.
But we do not "know" how multer uses Request and Response. So if you still want to do an integrational test, you approach is good enough.
Overwise it is better to pass { body: { videoId: fakeId } } instead of Request and mock multer, multer.diskStorage, and and multer.single`.

Resources