Node js packages for modules - javascript

Description:
I have a node js project, I have package models(which contains module with my model object) , have routes package (where module with routes handler functions stored), and app.js file with 'express' configurations and http server.
Question is: How can I import model object from models package to routes package, index.js module?
index.js:
var ArticleProvider = require('smth goes here').ArticleProvider;
var articleProvider= new ArticleProvider();
exports.index = function(req, res){
articleProvider.findAll(function(error, docs){
//console.log(error)
res.send(docs);
});
};
mymodel.js
ArticleProvider = function(){};
ArticleProvider.prototype.dummyData = [];
ArticleProvider.prototype.findAll = function(callback){
callback(null, this.dummyData);
};
ArticleProvider.prototype.save = function(articles, callback) {
var article = null;
if (typeof(articles.length) == "undefined") { articles = [articles]; }
//callback();
};
exports.ArticleProvider = ArticleProvider;
Note: I can import anything from bottom level(for example: if I would have models package inside routes package. Example: require("./models/mymodel.js")).

The solution is
var ArticleProvider = require('../models/articleprovider.js').ArticleProvider;

take a look at npm link https://npmjs.org/doc/link.html
It's allow you to build a packages as a dependency, into node_modules, and then you can require it wherever you want

Related

How to use a custom made library

I have a AWS dependency layer on on folder nodejs/
There is node_modules/ and package.json with npm dependencies
I created a folder called utils/ and my file is util.js
Since it's a layer on AWS, I import using const utils = require('/opt/nodejs/utils/util'); on my app.js
Problem is that my test cases started failing Cannot find module '/opt/nodejs/utils/util' from 'backend/lambdas/cars/app.js'
How can I fix my test case??
const app = require('./app');
describe('lambda', function () {
it('something', async () => {
const response = await app.lambdaHandler();
....
});
});
app.js
const httpStatusCode = require('http-status-codes');
const cors = require('/opt/nodejs/utils/util');
exports.lambdaHandler = async (event) => {
return {
statusCode: httpStatusCode.OK
};
};
PS: This nodejs folder is on the same level as the lambdas folder
You should import like this const utils = require('../nodejs/utils/util')

Strapi custom routes to redirect to public directory

I deployed my react app to /public directory in strapi, everything work's correctly but, when I refreshed page, strapi override my react-router routs.
So... how can I redirect strapi to open public directory when i use specific routs?
e.g redirect /posts to public directory?
Strapi /public folder is here to server public assets and not to host your front end application. And it's not a good practice to do that.
I had to write that before answering your question.
Here is how static files are served.
https://github.com/strapi/strapi/blob/master/packages/strapi/lib/middlewares/public/index.js
It uses the public middleware.
So you will have to create your own middleware by following this documentation.
https://strapi.io/documentation/3.x.x/advanced/middlewares.html#custom-middlewares
So in ./middelwares/custom/index.js add the following code:
const path = require('path');
module.exports = strapi => {
return {
initialize: function(cb) {
strapi.router.route({
method: 'GET',
path: '/post',
handler: [
async (ctx, next) => {
ctx.url = path.basename(`${ctx.url}/index.html`);
await next();
},
strapi.koaMiddlewares.static(strapi.config.middleware.settings.public.path || strapi.config.paths.static, {
maxage: strapi.config.middleware.settings.public.maxAge,
defer: true
})
]
});
cb();
}
};
};
Then you will have to enable your middleware.
You will have to update the ./config/custom.json file with the following code:
{
"myCustomConfiguration": "This configuration is accessible through strapi.config.myCustomConfiguration",
"custom": {
"enabled": true
}
}
That's it!
I build my Strapi and CRA (create-react-app) at the build time, and says I want to mount my react app under /dashboard path.
and the file structure is:
yourapp/
└── apps/
├── frontend (react app)
└── backend (strapi)
add a homepage property in frontend's package.json if you are using CRA, this will tell Webpack to add a prefix to your static assets, e.g
// in frontend's package.json
{
...
"homepage": "/dashboard"
}
move your built react app to a subfolder /dashboard of backend project, by modifying the yarn build script, I'm doing like this, be careful before copy/paste my code, there is a rm -rf cmd.
// package.json in root path
{
...
"scripts": {
"build": "yarn build:front && yarn build:back && rm -rf apps/backend/dashboard && mv apps/frontend/build apps/backend/dashboard",
...
}
}
add a custom middleware in Strapi to be your "view router", that will handle all requests to /dashboard/* to serve the react app assets under apps/backend/dashboard
create a file under <strapiapp>/middlewares/viewRouter/index.js
const path = require("path");
const koaStatic = require("koa-static");
const fs = require("fs");
module.exports = strapi => {
return {
async initialize() {
const { maxAge } = strapi.config.middleware.settings.public;
const basename = "/dashboard";
const dashboardDir = path.resolve(strapi.dir, "dashboard");
// Serve dashboard assets.
strapi.router.get(
`${basename}/*`,
async (ctx, next) => {
ctx.url = ctx.url.replace(/^\/dashboard/, "");
if (!ctx.url) ctx.url = basename;
await next();
},
koaStatic(dashboardDir, {
index: "index.html",
maxage: maxAge,
defer: false
})
);
const validRoutes = [
"/dashboard",
"/subpath1",
"/subpath2"
];
// server dashboard assets and all routers
strapi.router.get(`${basename}*`, ctx => {
const routePath = ctx.url.split("?")[0];
let fileName = ctx.url;
if (validRoutes.includes(routePath)) fileName = "index.html";
ctx.type = "html";
ctx.body = fs.createReadStream(
path.join(dashboardDir + `/${fileName}`)
);
});
}
};
};
enable the custom middleware in <strapiapp>/config/custom.json
{
"myCustomConfiguration": "This configuration is accessible through strapi.config.myCustomConfiguration",
"viewRouter": { // use the middleware name
"enabled": true
}
}
and visit http://localhost:1337/dashboard you'll see the react page.
The actual answer for strapi#4.3.2 is here
I faced the same problem. All you need to do are these two steps:
Create a custom middleware. I named it spa.js and put it in the folder /src/middlewares/spa.js (I am not sure about naming). I didn't have this folder before. I created it by myself. The file spa.js should contain a code like this:
module.exports = () => {
return async (ctx, next) => {
const url = ctx.url;
// Here you should process your redirects on index.html always,
// except URLs like `admin`, `content-manager`, `i18n`, `static`, `api`, `graphql`, `uploads` etc.
// These settings should be completely the same as in your Nginx config
// for SPA (Single Page Application). Usually, we set up `Location` in Nginx
if (!url.match(/\/admin|\/content-manager\/|\/i18n\/|\/static|\/graphql|\/uploads\/|\.json/)) {
ctx.url = '/';
}
// let strapi go further
await next();
};
};
Register your new middleware in /config/middlewares.js. I had this file and it contained only strings ('strapi::errors', 'strapi::security', 'strapi::cors',). I added an object with a resolve field with a relative path to my new middleware spa.js. There are different options for how you can set up this path, you can also use just a name.
My /config/middleware.js looks like this now:
module.exports = [
{
resolve: './src/middlewares/spa.js',
},
'strapi::errors',
'strapi::security',
'strapi::cors',
'strapi::poweredBy',
'strapi::logger',
'strapi::query',
'strapi::body',
'strapi::session',
'strapi::favicon',
'strapi::public',
];
Relaunch your server by strapi start. It should work. Routes after reloading any page that was reached by React-router before will work as they should work in SPA. All routes go to / (index.html)
UPD: Please, be careful. I see many routes for "internal use". For example, /content-manager/ and /i18n/. My admin panel didn't work unless I add the content-manager route. I suppose there may be many new routes in the future and we should mark in our middleware only allowed routes that are redirected and don't change behavior for other routes.

How do I mock my config file for testing?

I have a Koa app I just started and I need to test something that grabs data from a config file.
I need to test with specific data, but I'm not sure how to modify what data the test receives from the config file.
Example:
app.js
var router = require('koa-router');
var config = require('./config.js');
var db = require('./db.js');
var auth = require('./auth');
var app = require('koa')();
router.get('/', function *() {
if(auth(this.req, config.credentials.secret)) { // Authenticates request based on a hash created using a shared secret
this.body = "Request has been authenticated";
}
});
app.use(router.routes());
app = module.exports = http.createServer(app.callback());
app.listen(3000);
appSpec.js
var request = require('supertest');
var app = require('../app.js');
describe('app', function() {
it('should authenticate all requests against config shared secret', function() {
var secret = 'some_secret';
var validHash = /* hash created from test secret and query */;
request(app)
.get('/')
.query({query: 'some_query'})
.query({hash: validHash})
.expect(403, done);
});
});
This spec will fail because the app will use the secret from the config file(empty string) instead of my test secret.
Alright, I played around with some different ways to handle this.
The best option I found, for my particular use case, was proxyquire. It's an npm package that lets you override dependencies in any file that you require in your test suites.
So if I am testing this module:
./controllers/myController.js
var config = require('../config.js');
module.exports = function() {
// Do some stuff
};
I would do something like this:
./test/controllers/myControllerSpec.js
var proxyquire = require('proxyquire');
var config = {
credentials: {
secret: 'my_secret'
}
// other fake config stuff
};
var myController = proxyquire('../../controllers/myController.js', {'../config.js', config});
describe('myController', function() {
// TESTS
});
and this instance of myController will use my test config.
This won't work for end to end testing, unless the only place you import your config is the main app file.
I use node-config for my config files and configuration loading based on machine or env variable.
You can specify your config in a variety of formats (.json, .js, yaml, etc.) Using the default settings, you need to create a config folder in your app root and a default.<format> with your default config.
To override that for testing you can create a test.<format> file in your config directory. When you set your NODE_ENV=test, then node-config will see load your default config file and then it will load your test config file and if there are any conflicts for the values, your test config file will override the values in your default file.
Here are the full docs for setting up Configuration Files in node-config
Below is an example using node-config with a .js config file format.
./config/default.js
module.exports = {
credentials: {
secret: ''
}
}
./config/test.js
module.exports = {
credentials: {
secret: 'abcdef123456'
}
}
app.js
var router = require('koa-router');
var config = require('config');
var db = require('./db.js');
var auth = require('./auth');
var app = require('koa')();
var credentials = config.get('credentials');
router.get('/', function *() {
if(auth(this.req, credentials.secret)) { // Authenticates request based on a hash created using a shared secret
this.body = "Request has been authenticated";
}
});
app.use(router.routes());
app = module.exports = http.createServer(app.callback());
app.listen(3000);
Jest has nice support for this case. In your test file, add
jest.mock('../config.js', () => ({
credentials: {
secret: 'my_secret'
}
// other fake config stuff }));

NodeJs : modules.export : Cannot find one of my own custom module

I am trying to include my own users.js module to my router file. It keeps throwing the error:
Cannot find module './router/users.js'
My directory structure is as follows:
nodejs (Main folder on my drive)
-- expressserver.js (My server file)
-- package.json
-- router (folder containing main.js router and users.js user details file)
----- main.js
----- users.js
----- orders.js
Here my users module is in the same folder as my router (main.js)
My code for router is:
var url = require('url');
var users = require('./router/users.js');
module.exports = function (app) {
app.get('/', function (req, res) {
res.render('index.html');
console.log("Home page displayed");
});
app.get('/login', function (req, res) {
res.render('login.html');
console.log("Log in page displayed");
});
app.get('/api/orders/:id', function (req, res) {
console.log(req.params.id);
res.json(ORDER.orders[req.params.id]);
});
app.get('/api/orders/', function (req, res) {
console.log(ORDER);
res.json(ORDER);
});
app.get('/api/users/:id', function (req, res) {
console.log(req.params.id);
res.json(USERS.users[req.params.id]);
});
app.get('/api/users/', function (req, res) {
console.log(USERS);
res.json(USERS);
});
My code for users.js:
module.exports = function () {
// Create User prototype and objects
var USERS = { users: [] };
function User(type, useremail, password) {
this.type = type;
this.useremail = useremail;
this.password = password;
}
var Bob = new User("rep", "bob#bob.com", "qwerty");
USERS.users.push(Bob);
var Helen = new User("rep", "helen#helen.com", "test");
USERS.users.push(Helen);
var Dominic = new User("customer", "dom#dom.com", "1234");
USERS.users.push(Dominic);
var James = new User("grower", "james#james.com", "pass1");
USERS.users.push(James);
};
I'm pretty new to this are but have been reading up alot on modules but still can't figure it out. Any suggestions on where I have gone wrong? or what I need to do to rectify the problem?
Note: Previously I did the same thing for including router into the server file using module.exports = function (app) { around my router and in my server as: require('./router/main')(app);
Since you have specified a relative path to the module, that path is relative to the directory of the source file where require is performed. In your case, it is already relative to the 'router' directory. This will work:
var users = require('./users.js');
Or even just the following, since the extension is automatically resolved:
var users = require('./users');
The path for require(), is set for node_modules folder by default.
That is why you are able to require all modules such as var url = require('url'); in your case.
If the module is not found there in your current project, the module will be searched globally(if there are path variables set on machine).
Now when you define custom modules , you can either keep them within the folder node_modules, OR you can give a path relative to your current JS file within which you are requiring the module.
For example,
var users = require('./users');
If there is another folder, in your current working directory, say modules,
then you can do require it like this:
router
----- main.js
----- orders.js
----------------modules(folder)
-----------------------users.js
var users = require('./modules/users');
So the path for require always starts from node_modules folder

Node.js EXDEV rename error

I've been playing with some code I found in a book about Node.js. It is a simple app which uploads images.
It shows the EXDEV error (500 Error: EXDEV, rename).
Could someone give me a hint? Here's my code:
exports.submit = function(dir) {
return function(req, res, next) {
var img = req.files.photo.image;
var name = req.body.photo.name || img.name;
var path = join(dir, img.name);
fs.rename(img.path, path, function (err) {
if(err) return next(err);
Photo.create({
name: name,
path: img.name
}, function (err) {
if(err) return next(err);
res.redirect('/');
});
});
};
};
Renaming files cannot be done cross-device. My guess is that your upload directory (which by default is /tmp) is on another partition/drive as your target directory (contained in the dir variable).
Some solutions:
configure the upload directory to be on the same partition/drive as your target directory; this depends on which module you're using to handle file uploads, express.bodyParser (and the module it uses, connect.multipart) accepts an uploadDir option that you can use;
before starting your Node app, set the TMPDIR environment variable to point to a temporary directory on the same partition/drive as your target directory. If you're using a Unix-type OS:
env TMPDIR=/path/to/directory node app.js
instead of setting the environment variable from your shell, set it at the top of your Node app:
process.env.TMPDIR = '/path/to/directory';
instead of renaming, use a module like mv that can work cross-device;
Using Windows XP, I added to app.js:
process.env.TMPDIR = '.'; //new

Categories

Resources