I am new to node which I am now using extensively with firebase functions. I found this wonderful piece of code here:
const app = express();
const main = express();
main.use('/', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({
extended: false
}));
exports.webApi = functions.https.onRequest(main);
const glob = require("glob");
const files = glob.sync('./**/*.function.js', {
cwd: __dirname,
ignore: './node_modules/**'
});
for (let f = 0, fl = files.length; f < fl; f++) {
const file = files[f];
const functionName = file.split('/').pop().slice(0, -12); // Strip off '.function.js'
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === functionName) {
exports[functionName] = require(file);
}
}
This combs through the file stack in functions/filename/etc.f.js and pulls out my export functions which all end in f.js. Works great. Now I have repurposed this to also try and pull in a similar file stack for api routes and it now looks like this
const app = express();
const main = express();
main.use('/', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({
extended: false
}));
exports.webApi = functions.https.onRequest(main);
const glob = require("glob");
const camelCase = require("camelcase");
const files = glob.sync('./**/*.js', {
cwd: __dirname,
ignore: ['./node_modules/**', './index.js']
});
for (let f = 0, fl = files.length; f < fl; f++) {
const file = files[f];
const pathCheck = file.slice(-6, -3);
if (pathCheck === 'api') {
const path = file.slice(1, -7); // Strip off '.api.js'
app.use(`${path}`, require(file));
} else {
const functionName = camelCase(file.slice(0, -5).split('/').join('_')); // Strip off '.f.js'
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === functionName) {
exports[functionName] = require(file);
}
}
}
The file structure for my api stack as follows
functions
---api
------v1
--------deposit.api.js
--------invoice.api.js
------v2
--------deposit.api.js
--------deposit
------------:depistId (file name)
-----------------transaction.api.js
---db
------deposit
------------onCreate.f.js
------------onUpdate.f.js
Expected paths would be
/api/v1/deposit
/api/v1/invoice
/api/v2/deposit
/api/v2/deposit/:deposit/transaction
In each of my filename.api.js I have something to the affect of
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now())
next()
});
router.delete('/:depositId', (req, res) => {
// firebaseHelper.firestore
// .deleteDocument(db, contactsCollection, req.params.contactId);
return res.status(200).send('Deposit deleted');
});
module.exports = router
It just doesn't seem to work. If I manually add the path and require the file in index.js, it works fine. Wondering if there is something wrong with my code, or understanding of how node works, appreciate any help.
Woohoo for first question!,
Thanks in advance,
Chad
Related
currently, it's working using hash routing, I want to remove hash for SEO reasons so it is possible?
it creates routes like
https://mainDomain/program ← program build run
https://mainDomain/program/#detail ← program routing
I want https://mainDomain/program/#detail to https://mainDomain/program/detail
if I use BrowserRouter it shows no such directory
this is my build deploy structure in AWS ↓
might be this Helpful for you. you can create one Node server which can serve your Project builds.
let path = require("path");
let fsp = require("fs/promises");
let express = require("express");
let isProduction = process.env.NODE_ENV === "production";
async function createServer() {
let app = express();
/**
* #type {import("vite").ViteDevServer}
*/
let vite;
if (!isProduction) {
vite = await require("vite").createServer({
root: process.cwd(),
server: { middlewareMode: "ssr" },
});
app.use(vite.middlewares);
} else {
app.use(require("compression")());
app.use(express.static(path.join(__dirname, "dist")));
}
app.use("*", async (req, res) => {
let url = req.originalUrl;
// Use a separate HTML file for the "Inbox" app.
let appDirectory = url.startsWith("/inbox") ? "inbox" : "";
let htmlFileToLoad;
if (isProduction) {
htmlFileToLoad = path.join("dist", appDirectory, "index.html");
} else {
htmlFileToLoad = path.join(appDirectory, "index.html");
}
try {
let html = await fsp.readFile(
path.join(__dirname, htmlFileToLoad),
"utf8"
);
if (!isProduction) {
html = await vite.transformIndexHtml(req.url, html);
}
res.setHeader("Content-Type", "text/html");
return res.status(200).end(html);
} catch (error) {
if (!isProduction) vite.ssrFixStacktrace(error);
console.log(error.stack);
return res.status(500).end(error.stack);
}
});
return app;
}
createServer().then((app) => {
app.listen(3000, () => {
console.log("HTTP server is running at http://localhost:3000");
});
});
for extra information, you can referlink.
I'm trying to dynamically export modules. I'm close but can't figure out how to fix my syntax.
Hard coded:
// index.js inside folder 'models'
const { User } = require('./User');
const { Token } = require('./Token');
const { Session } = require('./Session');
module.exports = {
User,
Token,
Session,
};
Dynamically coded (doesn't work):
// index.js inside folder 'models'
const fs = require('fs');
const path = require('path');
module.exports = () => {
fs.readdirSync(__dirname).forEach((file) => {
if (file === 'index.js') return false;
const fullName = path.join(__dirname, file);
if (file.toLowerCase().indexOf('.js')) {
// I think this somehow needs to be destructured like
// `return {require(fullName)}` or
// `require(fullName)[fullName]` I think
require(fullName);
}
});
};
Elsewhere in my code, I initialize it based on the folder name:
// server.js
require('./models')();
Your dynamic export will not work because you are not returning anything to the exported function.
Try this code as your dynamic model export file
// index.js inside folder 'models'
const fs = require('fs')
const path = require('path')
const models = {}
fs.readdirSync(__dirname)
.filter(file => file !== 'index.js')
.forEach(file => {
const fullName = path.join(__dirname, file)
if (file.toLowerCase().endsWith('.js')) {
// Removes '.js' from the property name in 'models' object
const [filename] = file.split('.')
models[filename] = require(fullName)[filename]
}
})
module.exports = models
This approach no longer exports a function so your require in server.js should now look like this
// server.js
require('./models');
Can't upload files to the server. I've used npm install express-fileupload and also did the var fileUpload = require('express-fileupload') and app.use(fileUpload()). And while calling
router.post('/add-products',(req,res)=>{
console.log(req.body);
console.log(req.files.image);
It says cannot read image of null.
I can give you a working full example.
Project structure:
- storage (empty folder)
- routes
-> upload.route.js
- controllers
-> upload.controller.js
index.js
index.js
const express = require('express');
const app = express();
const route = require('./routes/upload.route');
app.use('/', route);
let port = 8000;
app.listen(port);
console.log(`API listens localhost:${port}`);
This is your upload.route.js
const express = require('express');
const router = express.Router();
const { uploadController } = require('../controllers/upload.controller');
router.use('/media/upload', uploadController);
module.exports = router;
This is upload.controller.js
const formidable = require('formidable');
const path = require('path');
exports.upload = async (req, res) => {
try {
// Receive the media and store it
let [uploadPath, filename] = await processUpload(req);
return res
.status(200)
.send({
success: 1,
message: "File uploaded",
filename,
uploadPath
});
} catch (error) {
return res
.status(400)
.send({
success: 0,
message: "Ops! Something went wrong",
errorObject: error.message
});
}
}
function processUpload(req) {
return new Promise((resolve, reject) => {
try {
let uploadDir = __dirname + `/../storage`;
// We used helper formidable package
let form = new formidable.IncomingForm()
form.multiples = true;
form.keepExtensions = true;
// Upload path
form.uploadDir = uploadDir;
let result;
form.on('fileBegin', function (name, file) {
if (!file.type) reject(new Error("No media specified!"));
const fileExt = path.extname(file.name);
let filename = "test" + fileExt;
file.path = path.join(uploadDir, filename);
// Return the path where file uploaded
result = [file.path, uuid];
});
form.parse(req, (err, fields, files) => {
if (err) return reject("Upload failed.");
resolve(result);
});
} catch (error) {
reject("Upload failed.");
}
});
}
When you call localhost:8000/media/upload with a POST or PUT request with postman form-data. You can see the uploaded file under the storage folder in the project.
Let me know if something goes wrong with the code
Note: You need to use formidable (For uploading) package to run the example
for my project Angular 9 I must install Server-side rendering (SSR), I followed official tutorial https://angular.io/guide/universal. At the beginning I have the problem with the window is not define. So I decided to install SSR with domino and I followed this tutorial enter link description here , but I have a problem when the program build my project : elements is not define. (const elements = stripe.elements() Cannot read property elements of undefined).
Below my server.ts code
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { APP_BASE_HREF } from '#angular/common';
import { existsSync } from 'fs';
import * as core from 'express-serve-static-core';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
// Use the browser index.html as template for the mock window
const template = fs
.readFileSync(path.join(join(process.cwd(), 'dist/captn-boat-angular/browser'), 'index.html'))
.toString();
// Shim for the global window and document objects.
const window = domino.createWindow(template);
global['window'] = window;
global['document'] = window.document;
global ['navigator']=window.navigator;
global ['screen']=window.screen;
global['Event'] = null;
global['window'] = window;
global['document'] = window.document;
global['branch'] = null;
global['object'] = window.object;
global['localStorage'] = window.localStorage;
global['navigator'] = window.navigator ;
global['elements']=window.elements;
global['elements']=null;
global['Event'] = null;
global['KeyboardEvent'] = null;
global['stripe']=window.stripe;
window.screen = { deviceXDPI: 0, logicalXDPI: 0 };
global['MouseEvent'] = window.MouseEvent;
declare interface Window {
Stripe: any; // Or you can define a type for that in this file as well
stripe:null;
elements:null;
}
import { AppServerModule } from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): core.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/captn-boat-angular/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
And then the error : elements of undefined
Thank you for your answer.
Do you use stripe in every place on your application ? I'd use it in the payment section of the related module and would connect it to lazy loading. Thus you could use one of the importer of stripe ?
like here :ngx-stripe installation
so I am trying to import API data from one JS file into another but I keep getting undefined or just errors. The variables I am trying to import are Weather, City and Temperature from the weathermap.js into the app.js. Any help would be greatly appreciated.
Is it to do with how I'm exporting from weathermap.js or how I'm importing into app.js??
app.js
const express = require("express");
const path = require("path");
const hbs = require('hbs');
const myForecast = require('./weathermap');
var appweather = myForecast.weather;
var appcity = myForecast.city;
var apptemperature = myForecast.temperature;
// This actually makes express run in our file
const app = express();
const publicDirectory = path.join(__dirname, "../public");
const viewsPath = path.join(__dirname, '../templates/views');
const partialPath = path.join(__dirname, '../templates/partials');
hbs.registerPartials(partialPath);
// console.log("This is the variable info: ", weather, city, temperature)
myForecast("Manchester", "uk", "metric");
app.use(express.static(publicDirectory));
app.set('view engine', 'hbs');
app.set('views', viewsPath);
// var weather = require('./weathermap.js').weather;
// var city = require('./weathermap.js').city;
// var temperature = require('./weathermap.js').temperature;
app.get("/", (req, res) => {
// console.log(myForecast.city);
res.render("index", {
title: 'Weather App',
author: 'Frazer MacRostie',
city: appcity,
weather: appweather,
temperature: apptemperature
});
});
// app.get("")
app.get("/about", (req, res) => {
res.render("about", {
});
});
app.get('*', (req, res) => {
res.send('<h1>404 your page does not exist</h1>')
})
// console.log(__dirname);
// console.log(__filename);
app.listen(3000, ()=>{
console.log(myForecast.city);
console.log("Server is running on localhost 3000");
})
weathermap.js
const request = require('request');
var weather = "";
var city = "";
var temperature = "";
const forecast = (city, country, units) => {
const encodedCityName = encodeURIComponent(city);
const encodedCountryName = encodeURIComponent(country);
const weatherMapUrl = `http://api.openweathermap.org/data/2.5/weather?q=${encodedCityName},${encodedCountryName}&units=${units}&APPID=4fe147c8dc2f848fd447182ebd444e80`
request({ url: weatherMapUrl, json: true }, (error, response) => {
// console.log(response.body);
weather = `${response.body.weather[0].main}`
city = `${response.body.name}`
temperature = `${response.body.main.temp}`
console.log(weather, city, temperature)
if(error) {
console.log("ERROR! Cannot connect to the API services.")
} else if (city === undefined){
console.log("I'm sorry, that City does not exist.")
} else{
console.log(`Today we have mainly ${weather}`)
console.log(`The current temperature in ${city} is ${temperature}°C`)
}
return weather + city + temperature;
})
}
// module.exports = weather;
// module.exports = city;
// module.exports = temperature;
module.exports = forecast;
// exports.forecast = () => {
// return weather + city + temperature;
// };
// &units=metric
// &units=imperial
You are getting undefined because you are calling the async API in the weathermap.js file. Which will wait for the request to complete. So use the async/await or Promise .then callback to wait for the api to complete the call.
Before your weathermap response data return function call and return, that why getting undefined.
you can set async or promise in your function.