I have a simple firebase function it has to send a file in response to the request the file is in /app/one.html
my project directory structure
blog
|____app
|____index.html
|____one.html
|____functions
|____index.js
I have to access one.html and send it from senFile()
index.js
const functions = require('firebase-functions');
exports.blog = functions.https.onRequest((req, res) => {
res.status(200).sendFile('app/one.html'); #here I need something to do
});
As far as I know firebase deploy for functions deploys only the contents of functions folder. So if you move/copy your app folder to inside of functions folder you can achieve what you are trying to do.
blog
|____functions
|____app
|____index.html
|____one.html
|____index.js
After you changed the paths correctly you can send the file like an express app.
res.sendFile(path.join(__dirname + 'app/one.html'));
Related
Sorry in advance for my lengthy post.
I am having difficulty changing from MERN localhost to an online hosting. I've been hosting my frontend/ client side website on heroku, with the file structure as follows:
Client Structure
- build
- src
- App.js
- Index.js
- Other Components, Images, Fonts, Pages
-package.json
-package-lock.json
This file structure works fine with heroku.
However, I've been following a Youtube tutorial that uses a MERN stack to build a blog: https://www.youtube.com/watch?v=OML9f6LXUUs&t=3s --> it is a 3 part series
In the video, he has a file structure as such:
API and Client Structure
- api
- images
- models
- public
- routes
- index.js
- package.json
- package-lock.json
- client (frontend code)
- build
- src
- App.js
- Index.js
- Other Components, Images, Fonts, Pages
-package.json
-package-lock.json
2 Instances of LocalHost
He runs api on localhost:3001 and client on localhost:3000.
Methodology
1. Storing Images
He does not store images in MongoDB cloud, but rather store the image name only, and stores the images directly on the api/images directory.
In the api folder, he uses multer to store the images.
app.use("/images", express.static(path.join(__dirname, "/images")))
const storage = multer.diskStorage({
destination: (request, file, callback) => {
callback(null, 'images');
},
filename: (request, file ,callback) => {
callback(null, request.body.name);
},
})
const upload = multer({storage: storage});
app.post('/api/upload', upload.single('file'), (request, response) => {
response.status(200).json('File has been uploaded');
});
Then in the client side, he sends the data via axios:
const response = await axios.post('/upload', data);
2. Retrieving Images
When retrieving, he specifies a public folder PF, uses axios to get the image name, and then concatenates them to get the images.
const PF = "http://localhost:3001/images/";
<Image src={PF + post.image} />
My question is:
How can I change from a localhost to a cloud based hosting (preferably Heroku)? What paths do I specify? I would think that this image retrieval works because I am running both locally on the same computer. I wouldn't be able to use it if they are online as the image src directory will not be searchable
Is it possible to host both simultaneously on 1 Heroku Dyno, and how can I do it?
This is my first time learning backend so thanks for the patience!
Firstly, you'll have to remove all hardcoded 'http://localhost:3000`. This would interfere with the cloud site since it uses its own.
Instead, you could use an environment file to store the API site and in production use a different one.
You will need to make a mongodb cluster (its a cloud database and its free) https://www.mongodb.com/docs/atlas/tutorial/create-new-cluster/. You'll have to manually change your database to the cluster link. I would use an environment variable for this as well, so the db link would be something like this in your db file
const mongoURI = process.env.MONGODB_URI || 'mongodb://localhost:27017/ProjectAPI'
where process.env.mongodb_uri contains cluster uri
In the server file where you host your routes add this to the end of your file and change to match your app:
app.use(express.static(__dirname + "/../client/build"));
app.get("*", (req, res) => {
// check for page routes that we expect in the frontend to provide correct status code.
const goodPageRoutes = ["/", "/login", "/home"];
if (!goodPageRoutes.includes(req.url)) {
// if url not in expected page routes, set status to 404.
res.status(404);
}
// send index.html
res.sendFile(__dirname + "/../client/build/index.html");
});
After that just follow the guide: https://devcenter.heroku.com/articles/git and https://devcenter.heroku.com/articles/deploying-nodejs
There is a solution to this error without the use of Firebase here when using app.listen(8080) however this does not work while serving in cloud functions with exports.app = functions.https.onRequest(app)
Here is a simple reproduction code
const app = express();
app.get('**', (req, res) => res.send('working'));
app.use((err, req, res, next) => res.redirect('/404'));
exports.app = functions.https.onRequest(app) // doesnt work
// app.listen(5000) // works
How do you go about catching this error in firebase functions? I would like the redirect to work.
firebase test: firebase serve --only functions
express serve: node index.js
URL to test: http://localhost:5000/%CO
Note that the additional %CO is the one that cannot be decoded by express. This error is caught while serving with the express method but not with the firebase functions method.
As this seems like a bug, I have also created an issue here on github incase I find no workaround on it.
Try changing the name of app in exports.
exports.newApp = functions.https.onRequest(app)
According to the documentation, the correct way of using an Express app with Firebase Functions is to pass the application to a Function like:
// Expose Express API as a single Cloud Function:
exports.app = functions.https.onRequest(app);
Listening to a port for requests does not apply when running through Firebase Functions. As for how to catch errors when passing your app to a function, it’s done in the same way as the question you referenced as far as I have reviewed. You can catch errors by using Express middleware while using Cloud Functions.
Moreover, implementing redirects with Firebase Functions is explained in this related question, which makes use of the documentation to configure how to redirect by modifying the firebase.json file.
(Note: I'm using javascript, not typescript in my Functions)
My Firebase Project has a single 'oauth' function, which has a series of endpoints created through express app/routers.
I don't understand how to run the functions at these endpoints from the Cloud Functions Shell to debug them locally.
Here is my index.js
const twitter = require("./oauth/twitter");
const app = express();
app.use("/signin/twitter", twitter.router);
exports.oauth = functions.https.onRequest(app);
My actual endpoints are in a twitter.js file (and others for other providers)
router.get("/authorize", (req, res) => {...});
router.get("/authorize_callback", (req, res) => {...});
router.get("/deauthorize", (req, res) => {...});
If I run 'firebase functions:shell' in my terminal, it only shows the 'oauth' function.
I would like to access a function such as 'oauth/signin/twitter/authorize' just like I do in the browser after deploying, but I have no idea how to!
Is this possible?
I believe this is the documentation you are looking for. Essentially, you can invoke those express-like routes by calling the method (get, post, etc.) within the Cloud Function in the shell like such: functionName.get('/test')
I need to access FTP in another server (Ubuntu).
My Node.js API receive an image from user, and then needs to upload it to another server using FTP connection. However, if user folder doesn't exist, I need to create folder before sending the image.
How can i do this?
I'm using express-upload to get files:
const express = require('express');
const upload = require('express-fileupload');
const app = express();
app.use(upload());
app.use('/upload', async (req, res, next) => {
console.log(req.files.image);
})
You can use Basic FTP, an FTP client module, and use its ensureDir() method to implement the requirement "if user folder doesn't exists, I need to create folder before sending image".
According to its document:
...we make sure a remote path exists, creating all directories as necessary.
await client.ensureDir("my/remote/directory")
Then, you can send the image using its upload() method.
I am new to nodeJS server area, need help in understanding how to work with REST API (using express) and deploy the angular application over a singe node server and same ports.
By deploying i want to understand if user hit below url http://localhost:8000/<page_name> then the specified page should open.
And is user hit below url using get or post request
http://localhost:8000/api/<api_name> then a json or a text will be returned.
How to run both the thing over a single node server.
Lets assume, you have all your static files in the /public folder of you app. Generally spoken, if you are using express.static, you should also get your index.html because this is handled by default for each directory.
In your case, as you are using Angular, the routing is handled from the client side (SPA). You should only have one single index.html after building your Angular app. All files from your dist folder should then be placed into your /public folder. Then you need to make sure, that initial file serving provides your index.html like so:
In this example static files are served first, then your API and if nothing is found, you are getting back you index file.
const express = require('express');
const app = express();
// serve static files
app.static(__dirname + '/public'));
// serve your API
app.get('/api/welcome', function (req, res) {
res.send('Welcome');
});
// fallback routing (server side handling)
app.get(/.*/, function (req, res) {
res.sendFile(__dirname + ‘/public/index.html‘
});
app.listen(3000);
Next time please make sure, to give all necessary information in your question ;-)
With the help from Sebastian, so far I can find a solution but its not working when i am hitting URL for different pages.
const express = require('express');
const app = express();
app.use(express.static('public'))
Please provide your suggestions.