Firebase HTTP Cloud Function error 'Cannot GET /' - node.js

I am trying to get users data from firebase but I keep receive 'CANNOT GET /' error.
//firebase init
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp();
//express and cors init
const express = require('express');
const cors = require('cors');
//middleware init
const app = express();
app.use(cors());
const database = admin.database();
app.get("/users", function (request, response) {
return database.ref('/users').on("value", snapshot => {
return response.status(200).send(snapshot.val());
}, error => {
console.error(database);
return response.status(500).send(err);
})
});
exports.users = functions.https.onRequest(app);
I also try to use another function from another website I refer to get at least the username but it returns error.
exports.users = functions.https.onRequest((req, res) => {
database().ref('/merchants').once('value').then(function(snapshot) {
var username = snapshot.val().username;
res.status(200).send(username);
});
});

Problem 1: Exporting express applications
When you export an express app through a Cloud Function, the paths become relative to the exported function name. You exported your app as exports.users which sets the root path of your function to /users and to call it, you would visit https://us-central1-<project-id>.cloudfunctions.net/users.
However, because you defined a route handler for /users (using app.get("/users", ...)) as well, you added a handler for https://us-central1-<project-id>.cloudfunctions.net/users/users instead.
The error Cannot GET / is thrown because when you call the function at https://us-central1-<project-id>.cloudfunctions.net/users, the relative URL is set as "/", which you haven't configured a handler for (using app.get("/", ...)).
So to fix your code above, change
app.get("/users", function (request, response) {
to
app.get("/", function (request, response) {
Problem 2: Username queries
The issue could be as simple as that you call database() instead of admin.database().
However, the purpose of this query is unclear, so I will assume that you have a merchant's ID and you are trying to get the username of the owner of that particular merchant.
This would mean a data structure similar to:
{
"users": {
"somePerson": { ... },
"otherPerson: { ... }
},
"merchants": {
"reallyGreatGuyInc": {
"username": "somePerson",
...
},
"realShadyPeopleInc": {
"username": "otherPerson",
...
}
}
}
If you exported a function called getMerchantUsername where you pass in the merchant ID via a GET parameter and call it at the URL https://us-central1-<project-id>.cloudfunctions.net/getMerchantUsername?id=reallyGreatGuyInc, you would define it using the following code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getMerchantUsername = functions.https.onRequest((req, res) => {
if (!req.query.id) {
// '?id=' is required
return res.status(400).send("Missing 'id' parameter");
} else if (/[\u0000-\u001F\u007F\.\$#/\[\]\\]/.test(req.query.id)) {
// check id for invalid characters
return res.status(400).send("Invalid 'id' parameter");
}
// run query
admin.database().ref(`/merchants/${req.query.id}/username`).once('value')
.then((snapshot) => {
var username = snapshot.val();
res.status(200).send(username);
})
.catch((error) => {
console.error(error);
res.status(500).send('Request failed.');
});
});
Notes:
The test for invalid characters is based on the list in the RTDB: Structure data documentation. This somewhat prevents looking at other database values.
If you only need the value of username, rather than request the entire merchant object and parse it, request only the username as in the example above where I used `/merchants/${req.query.id}/username`.

Related

How to make a GET Request for a unique register with AXIOS and NodeJS/Express

I'm trying to make GET request to external API (Rick and Morty API). The objective is setting a GET request for unique character, for example "Character with id=3". At the moment my endpoint is:
Routes file:
import CharacterController from '../controllers/character_controller'
const routes = app.Router()
routes.get('/:id', new CharacterController().get)
export default routes
Controller file:
async get (req, res) {
try {
const { id } = req.params
const oneChar = await axios.get(`https://rickandmortyapi.com/api/character/${id}`)
const filteredOneChar = oneChar.data.results.map((item) => {
return {
name: item.name,
status: item.status,
species: item.species,
origin: item.origin.name
}
})
console.log(filteredOneChar)
return super.Success(res, { message: 'Successfully GET Char request response', data: filteredOneChar })
} catch (err) {
console.log(err)
}
}
The purpose of map function is to retrieve only specific Character data fields.
But the code above doesn't work. Please let me know any suggestions, thanks!
First of all I don't know why your controller is a class. Revert that and export your function like so:
const axios = require('axios');
// getCharacter is more descriptive than "get" I would suggest naming
// your functions with more descriptive text
exports.getCharacter = async (req, res) => {
Then in your routes file you can easily import it and attach it to your route handler:
const { getCharacter } = require('../controllers/character_controller');
index.get('/:id', getCharacter);
Your routes imports also seem off, why are you creating a new Router from app? You should be calling:
const express = require('express');
const routes = express.Router();
next go back to your controller. Your logic was all off, if you checked the api you would notice that the character/:id endpoint responds with 1 character so .results doesnt exist. The following will give you what you're looking for.
exports.getCharacter = async (req, res) => {
try {
const { id } = req.params;
const oneChar = await axios.get(
`https://rickandmortyapi.com/api/character/${id}`
);
console.log(oneChar.data);
// return name, status, species, and origin keys from oneChar
const { name, status, species, origin } = oneChar.data;
const filteredData = Object.assign({}, { name, status, species, origin });
res.send(filteredData);
} catch (err) {
return res.status(400).json({ message: err.message });
}
};

Express Rest Api: question about routing?

I have a Rest API where a user can create a list and then put list items in it (for a quiz). My schema structure is this:
const verbListSchema = {
title: String,
verbs: [{verb: String}]
};
Here are the url endpoints I have so far:
/lists/ (gets back all the lists)
/lists/verbs (gets all the verbs from all the lists)
My question is - I want to get, post, patch and delete a specific list using its id, like /lists?list_id=123/verbs or /lists/123/verbs and then one step further to get individual verbs I want to do something like /lists/123/verbs/124 or /lists?list_id=123/verbs?verb_id=124 the last doesn't work because it counts the last endpoint as a query param.
In terms of best practice what's the best way to do this. I could do something like this (I use express.js)?
app.[request-type]("/lists") {...}
app.[request-type]("/lists/:list_id") {...}
app.[request-type]("/lists/:list_id/verbs") {...}
app.[request-type]("/lists/:list_id/verbs/:verb_id") {...}
and then if I want to retrieve all the lists, not just a specific one I can check if the list_id is "all" like, /lists/all/verbs?
And here is my code so far:
const express = require("express");
const verbRouter = require("./verbRoutes");
const router = express.Router();
const VerbList = require("../../verb-list-db");
const isOriginal = async (req,res,next) => {
const listExists = await VerbList.find({title: req.body.listTitle})
if (listExists.length > 0 ) return res.status(400).json({message: "list already exists"});
next();
};
router.route("/")
.get(async (req,res,next) => {
try {
const listId = req.query.list_id;
if (listId) return res.json(await VerbList.find({_id: listId}));
const lists = await VerbList.find({});
res.json(lists);
} catch(err) {next(err)}
})
.post(isOriginal, async (req,res,next) => {
const newList = new VerbList({ // creates a new list
title: req.body.listTitle
})
newList.save()
.then(() => {return res.send("list successfully added!")})
.catch(err => next(err));
})
.patch(isOriginal, async (req,res,next) => {
try {
const listId = req.query.list_id;
if (!listId) throw new Error("you must have a list_id to patch!")
res.json(await VerbList.updateOne({_id: req.query.list_id}, {title: req.body.listTitle}))
} catch(err) {next(err)}
})
.delete(async (req,res,next) => {
try {
const listId = req.query.list_id;
if (!listId) throw new Error("you must have a list_id to delete!");
res.json(await VerbList.deleteOne({_id: req.query.list_id}))
} catch(err) {next(err)}
})
Any suggestions would be appreciated :)
You can try to modularize your express code by separating your /lists routes from your main server.js (or index.js) file.
index.js
const express = require('express');
const app = express();
//Now lets route all the API requests that start with '/list' to a file called lists.js
app.use('/lists', require('/path/to/lists.js')
app.listen(3000, () => console.log(`\nServer started on port 3000`))
lists.js
const express = require('express');
const router = express.Router();
// now you know all the requests in this file would be for /list so lets implement a router for fetching all the lists
router.get('/', async(req, res) => {
*** add all your logic for /lists endpoints here**
res.status(200).json(lists_json_response); //send a response back to your client
})
//you can create as many endpoints as you want in this file for endpoints that start with '/lists'
router.[request-method]("/lists/:list_id") {...} // endpoint for requesting a specific list
router.[request-method]("/:list_id/verbs") {...} //endpoint for requesting all the verbs for a specific list
router.[request-method]("/lists/all/verbs") {...} // all the verbs in all the lists
module.exports = router;
Also you cant put query parameters in the middle of an endpoint. if it is going to be a variable that you need and its not in the end of the URL, you have to pass it as a param, e.g.:
instead of doing /lists?list_id=123/verbs?verb_id=124, you can do something like /lists/123/verbs/124 to look for the verb with id 124 in a list with id 123.
so to listen to a request to this endpoint, you can design another endpoint in your lists.js file like this:
router[request-method].('/:list_id/verb/:verb_id', async(req, res)=> {
var list_id = req.params.list_id
var verb_id = req.params.verb_id
***
now use the list_id and verb_id to query the requested data and send a response back to the client
***
})

fs.writefile is making POST request loop infinitly within my express app

I have this current server code:
const express = require("express")
const fs = require("fs")
const router = express.Router()
const path = require("path")
const todos = JSON.parse(fs.readFileSync(path.join(__dirname, "../db", "todolist.json"), "utf8"))
router.get("/", async (req, res) => {
res.send(todos)
})
router.post("/new", async (req, res) => {
const { title, description } = req.body
const todoItem = {
id: "3",
title,
description
}
todos.todos.push(todoItem)
const data = JSON.stringify(todos, null, 2)
fs.writeFile(path.join(__dirname, "../db", "todolist.json"), data, () => {})
res.status(201).json(todoItem)
})
client:
console.log("Hello world!")
const somedata = {
title: "A new boy",
description: "Recieved from the client"
}
const main = async () => {
const response1 = await fetch("http://localhost:3000/todo", {
method: "GET",
})
const data1 = await response1.json()
const response2 = await fetch("http://localhost:3000/todo/new", {
method: "POST",
body: JSON.stringify(somedata),
headers: {
'Content-Type': 'application/json',
"Accept": "application/json"
}
})
const data2 = await response2.json()
return { data1, data2 }
}
main().then(data => console.log(data))
When I make a /POST request to create a new entity the browser just loops the request over and over until I manually have to quit the server. This does not happen if I use postman for some reason. Does anybody see any obvious error here with how the writeFile-method is used and why it continuously reloads the browser to keep pushing POST requests?
Thanks! :)
i had the same problem! And it took me about 1 hour to understand what my Problem is:
If you use "live server extension", the server will restart everytime, when you write, change or delete a file in the project folder!
So, if your node-app wirte a file, the live-server will restart and the app writes the file again! => loop
In my case, i write a pdf-file. All i had to do, is to tell the live server extension to ignore pdf files:
So i just add to "settings.json":
"liveServer.settings.ignoreFiles":["**/*.pdf"]
fs.writeFile is asynchronous function. So, to send a response after file written you must do it in the callback. And of course, don't forget about error checking. I.e.
router.post("/new", async (req, res) => {
const { title, description } = req.body
const todoItem = {
id: "3",
title,
description
}
todos.todos.push(todoItem)
const data = JSON.stringify(todos, null, 2)
fs.writeFile(path.join(__dirname, "../db", "todolist.json"), data, (err) => {
if(err) {
throw err;
}
res.status(201).json(todoItem)
})
})
Or you can use fs.writeFileSync as Muhammad mentioned earlier.
I think I found the problem. It seemed that the live server extension was messing things up when I had the client and server on separate ports, making the browser refresh for every request made somehow. I switched back to them sharing port, which then makes it work. I have to find a good way of separating them on a later basis without this bug happening, but that is for another time.
Thanks for your help :)
I share my working sample.body-parser dependency is need to get body in post request.Please don't change the order in server.js.Check and let me know.
and also check once whether your client code is in in loop.
My server.js
const express = require("express")
const fs = require("fs")
const router = express.Router()
const path = require("path")
const app = express();
const bodyParser = require("body-parser")
const todos = JSON.parse(fs.readFileSync(path.join(__dirname, "../db", "todolist.json"), "utf8"))
app.use(bodyParser.json());
app.use("/",router)
router.get("/todo", async (req, res) => {
res.send(todos)
})
router.post("/todo/new", async (req, res) => {
const { title, description } = req.body
const todoItem = {
id: "3",
title,
description
}
todos.todos.push(todoItem)
const data = JSON.stringify(todos, null, 2)
fs.writeFile(path.join(__dirname, "../db", "todolist.json"), data, () => {})
res.status(201).json(todoItem)
});
app.listen(3000, () => {
console.log(`Server running in Port`);
});
todolist.json
{
"todos": []
}
I think you should use fs.writeFileSync() or write some code in its callback

LoopbackJS (express) middleware (auth0) fails on production, but works on the local environment

I have a middleware responsible for authorization: loopback-auth-jwt, and I want to apply it to most of the endpoints, but not to all of them. Some of the endpoints should be exposed, but if a request has the access token it should be used for authentication. So I wrote a middleware, that should check the token and if it's present authorize the user, otherwise it should do nothing, so user would be treated as anonymous. It works perfectly on the local environment, though it fails on the remote server. To be honest I don't even know how to debug that. Here is my middleware (it's not 1 to 1 code, but the general concept is here):
// Define routes that should be public and not protected by auth0 by default.
const EXCLUDED_ROUTES = [
'/api/projects/',
'/api/tasks/[a-z0-9]*',
'/api/projects/(task)/[a-z0-9]*'
];
module.exports = server => {
const auth0Jwt = require('loopback-auth0-jwt');
const jwtDecode = require('jwt-decode');
const authConfig = {
// ...
};
const auth = auth0Jwt(server, authConfig);
// Check if given URL should be considered as public or private route.
// This function is working as desired.
const isPublicRoute = url => {
let isPublic = false;
EXCLUDED_ROUTES.map(route => route.replace('/', '/')).forEach(pattern => {
const regex = new RegExp(pattern);
if (regex.test(url)) {
isPublic = true;
}
});
return isPublic;
};
// Middleware that depend on the result of isPublicRoute function applies auth0 middleware or not.
const authMiddleware = async (req, res, next) => {
const path = req._parsedUrl.pathname;
if (isPublicRoute(path)) {
// validate reuqest headers
if (!(req.headers && req.headers.authorization)) return next();
// ... some additional checks
const userWithToken = await server.models.User.findOne({where: { email: userEmail }, include: ['accessTokens']});
// no token was found, so we need to login user.
if (!userWithToken.accessToken) {
accessToken = await server.models.User.login({
// data
});
}
if (accessToken) {
req.accessToken = accessToken;
next();
} else {
next();
}
} else {
// route is not public - apply auth0 middelware.
server.use(path, auth.authenticated);
next();
}
};
// apply authMiddleware
server.use(authMiddleware);
server.enableAuth();
};
So we've identified the issue. According to the loopback docs, server.use(path, auth.authenticated) will add the middleware, but since it's an express middleware it has no control on the order of the middleware execution, that's why random results were returned.
So we've moved that to middleware.json and applied this middleware in the auth stage and it works like a charm.

How to access the GET parameters after "?" in Express?

I know how to get the params for queries like this:
app.get('/sample/:id', routes.sample);
In this case, I can use req.params.id to get the parameter (e.g. 2 in /sample/2).
However, for url like /sample/2?color=red, how can I access the variable color?
I tried req.params.color but it didn't work.
So, after checking out the express reference, I found that req.query.color would return me the value I'm looking for.
req.params refers to items with a ':' in the URL and req.query refers to items associated with the '?'
Example:
GET /something?color1=red&color2=blue
Then in express, the handler:
app.get('/something', (req, res) => {
req.query.color1 === 'red' // true
req.query.color2 === 'blue' // true
})
Use req.query, for getting he value in query string parameter in the route.
Refer req.query.
Say if in a route, http://localhost:3000/?name=satyam you want to get value for name parameter, then your 'Get' route handler will go like this :-
app.get('/', function(req, res){
console.log(req.query.name);
res.send('Response send to client::'+req.query.name);
});
Query string and parameters are different.
You need to use both in single routing url
Please check below example may be useful for you.
app.get('/sample/:id', function(req, res) {
var id = req.params.id; //or use req.param('id')
................
});
Get the link to pass your second segment is your id example: http://localhost:port/sample/123
If you facing problem please use Passing variables as query string using '?' operator
app.get('/sample', function(req, res) {
var id = req.query.id;
................
});
Get link your like this example: http://localhost:port/sample?id=123
Both in a single example
app.get('/sample/:id', function(req, res) {
var id = req.params.id; //or use req.param('id')
var id2 = req.query.id;
................
});
Get link example: http://localhost:port/sample/123?id=123
Update: req.param() is now deprecated, so going forward do not use this answer.
Your answer is the preferred way to do it, however I thought I'd point out that you can also access url, post, and route parameters all with req.param(parameterName, defaultValue).
In your case:
var color = req.param('color');
From the express guide:
lookup is performed in the following order:
req.params
req.body
req.query
Note the guide does state the following:
Direct access to req.body, req.params, and req.query should be
favoured for clarity - unless you truly accept input from each object.
However in practice I've actually found req.param() to be clear enough and makes certain types of refactoring easier.
#Zugwait's answer is correct. req.param() is deprecated. You should use req.params, req.query or req.body.
But just to make it clearer:
req.params will be populated with only the route values. That is, if you have a route like /users/:id, you can access the id either in req.params.id or req.params['id'].
req.query and req.body will be populated with all params, regardless of whether or not they are in the route. Of course, parameters in the query string will be available in req.query and parameters in a post body will be available in req.body.
So, answering your questions, as color is not in the route, you should be able to get it using req.query.color or req.query['color'].
The express manual says that you should use req.query to access the QueryString.
// Requesting /display/post?size=small
app.get('/display/post', function(req, res, next) {
var isSmall = req.query.size === 'small'; // > true
// ...
});
const express = require('express')
const bodyParser = require('body-parser')
const { usersNdJobs, userByJob, addUser , addUserToCompany } = require ('./db/db.js')
const app = express()
app.set('view engine', 'pug')
app.use(express.static('public'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.get('/', (req, res) => {
usersNdJobs()
.then((users) => {
res.render('users', { users })
})
.catch(console.error)
})
app.get('/api/company/users', (req, res) => {
const companyname = req.query.companyName
console.log(companyname)
userByJob(companyname)
.then((users) => {
res.render('job', { users })
}).catch(console.error)
})
app.post('/api/users/add', (req, res) => {
const userName = req.body.userName
const jobName = req.body.jobName
console.log("user name = "+userName+", job name : "+jobName)
addUser(userName, jobName)
.then((result) => {
res.status(200).json(result)
})
.catch((error) => {
res.status(404).json({ 'message': error.toString() })
})
})
app.post('/users/add', (request, response) => {
const { userName, job } = request.body
addTeam(userName, job)
.then((user) => {
response.status(200).json({
"userName": user.name,
"city": user.job
})
.catch((err) => {
request.status(400).json({"message": err})
})
})
app.post('/api/user/company/add', (req, res) => {
const userName = req.body.userName
const companyName = req.body.companyName
console.log(userName, companyName)
addUserToCompany(userName, companyName)
.then((result) => {
res.json(result)
})
.catch(console.error)
})
app.get('/api/company/user', (req, res) => {
const companyname = req.query.companyName
console.log(companyname)
userByJob(companyname)
.then((users) => {
res.render('jobs', { users })
})
})
app.listen(3000, () =>
console.log('Example app listening on port 3000!')
)
you can simply use req.query for get query parameter:
app.get('/', (req, res) => {
let color1 = req.query.color1
let color2 = req.query.color2
})
The url module provides utilities for URL resolution and parsing. URL parse without using Express:
const url = require('url');
const queryString = require('querystring');
let rawUrl = 'https://stackoverflow.com/?page=2&size=3';
let parsedUrl = url.parse(rawUrl);
let parse = queryString.parse(parsedUrl.query);
// parse = { page: '2', size: '3' }
Another way:
const url = require('url');
app.get('/', (req, res) => {
const queryObject = url.parse(req.url,true).query;
});
url.parse(req.url,true).query returns { color1: 'red', color2: 'green' }.
url.parse(req.url,true).host returns 'localhost:8080'.
url.parse(req.url,true).search returns '?color1=red&color2=green'.
Just use the app.get:
app.get('/some/page/here', (req, res) => {
console.log(req.query.color) // Your color value will be displayed
})
You can see it on expressjs.com documentation api:
http://expressjs.com/en/api.html
A nice technique i've started using with some of my apps on express is to create an object which merges the query, params, and body fields of express's request object.
//./express-data.js
const _ = require("lodash");
class ExpressData {
/*
* #param {Object} req - express request object
*/
constructor (req) {
//Merge all data passed by the client in the request
this.props = _.merge(req.body, req.params, req.query);
}
}
module.exports = ExpressData;
Then in your controller body, or anywhere else in scope of the express request chain, you can use something like below:
//./some-controller.js
const ExpressData = require("./express-data.js");
const router = require("express").Router();
router.get("/:some_id", (req, res) => {
let props = new ExpressData(req).props;
//Given the request "/592363122?foo=bar&hello=world"
//the below would log out
// {
// some_id: 592363122,
// foo: 'bar',
// hello: 'world'
// }
console.log(props);
return res.json(props);
});
This makes it nice and handy to just "delve" into all of the "custom data" a user may have sent up with their request.
Note
Why the 'props' field? Because that was a cut-down snippet, I use this technique in a number of my APIs, I also store authentication / authorisation data onto this object, example below.
/*
* #param {Object} req - Request response object
*/
class ExpressData {
/*
* #param {Object} req - express request object
*/
constructor (req) {
//Merge all data passed by the client in the request
this.props = _.merge(req.body, req.params, req.query);
//Store reference to the user
this.user = req.user || null;
//API connected devices (Mobile app..) will send x-client header with requests, web context is implied.
//This is used to determine how the user is connecting to the API
this.client = (req.headers) ? (req.headers["x-client"] || (req.client || "web")) : "web";
}
}

Resources