I am creating a get request in node js. I want the base url path as http://localhost:80/sample and different requests as /v1, /v2.
So, the concatenated url will be http://localhost:80/sample/v1?querystring=10.
How can I segeregate the base path as http://localhost:80/sample, since when I try to get the pathname, I am getting /sample/v1?
Please help me with node js not using express.
UPDATE:
function onRequest(request, response) {
var pathName = url.parse(request.url).pathname;
}
where the request.url is localhost:80/sample/v1?q=10.
I need to validate if its either "v1" or "v2" by fethcing in the url instead of validating with the entire pathname like pathname.indexOf('v1') process something.
So, I don't know why do you prefer scheme /{method}/{api_version}, because companies are prefer reversed to your order. (E.g. twitter console).
Example #1, if your method doesn't contain additional slashes:
function onRequest(request, response) {
var pathName = url.parse(request.url).pathname.split('/');
var version = pathName[2]; // v1 or v2
var methodName = pathName[1]; // sample
}
Example #2, if you method will contain additional /, e.g. /user/19292/v1:
function onRequest(request, response) {
var pathName = url.parse(request.url).pathname;
var match = pathName.match(/\/v\d+$/);
if(match != null) {
var version = match[0]; //v1 or v2
var methodName = pathName.replace(/\/v\d+$/, ''); // /user/19229
} else {
// No version was provided
}
}
Related
I'm trying to create a Chrome bookmarklet that will take a part of the pathname from one URL and navigate to a new URL using that variable as a parameter (the variable is 1234567 in the example below).
From: 'https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115'
To: 'https://example.com/tool/permissions/resources?namespace=1234567'
The hostname differs depending on the environment I'm working in but will always stay the same when I transform it with the bookmarklet so I'm trying to pull that info when I compose the new URL. This is what I've got so far, but I keep getting "undefined" in the transformed URL (below) when I run the code. Any ideas on what I've got wrong here?
'https://example.com/tool/permissions/resources?namespace=undefined'
My code:
//Sample URL: https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115
var pathArray = location.pathname.split('/');
let secondLevelLocation = pathArray[3];
var newUrl = location.protocol + '//' + location.hostname + '/tool/permissions/resources?namespace=' + secondLevelLocation;
var w=window.open();w.location=newUrl;w.document.close();
In the code you've shown, there is an assumption that the URL hash (fragment identifier) will be included when accessing the pathname:
//Sample URL: https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115
var pathArray = location.pathname.split('/');
let secondLevelLocation = pathArray[3];
This is where the problem occurs. In a URL, the pathname ends when one of the following characters are first encountered:
? (which begins the query string), or
# (which begins the fragment identifier)
The format of the hash / fragment identifier portion of the URL in your example is that of a fully-resolved URL without the origin (starting at the pathname).
Using this knowledge, you can use the native URL class to help you select the desired part of the input URL, then use it again to construct the target URL, as shown in the code below. Once you have the target URL, you can use it to navigate, etc.
function parseNamespace (url) {
const fragment = url.hash.slice(1);
if (!fragment.startsWith('/')) throw new Error('Path fragment not found');
url = new URL(fragment, url);
const namespace = url.pathname.split('/').at(-1);
return namespace;
}
function createUrl (address = window.location.href) {
let url = new URL(address);
const namespace = parseNamespace(url);
const pathname = '/tool/permissions/resources';
url = new URL(pathname, url.origin);
url.searchParams.set('namespace', namespace);
return url;
}
const url = createUrl('https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115');
// You can omit the argument when you want to get the address from the current document:
// const url = createUrl();
console.log(url.href); // "https://example.com/tool/permissions/resources?namespace=1234567"
I have a simple Node/Express app and am trying to pass data from a javascript function to a template (powered by jade).
The javascript function looks like this:
module.exports = {
getFeatures: function() {
var request = require("request")
// ID of the Google Spreadsheet + Base URL
var spreadsheetID = "abcdefg-123456";
var sheetID = "od6";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID + "/" + sheetID + "/public/values?alt=json";
//empty array for features
var features = [];
//get the features
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = body.feed.entry;
data.forEach(function(item) {
var obj = {
pub: item.gsx$publication.$t,
date: item.gsx$date.$t,
title: item.gsx$title.$t,
url: item.gsx$url.$t,
}
features.push(obj);
});
console.log("features", features"); //prints array containing all objects to server console
return features;
}
});
}
};
And the main app looks like this:
'use strict';
var express = require('express');
var jade = require('jade');
var gsheets = require("./gsheets.js"); //pulls in module.exports from above
var featuresOld = require('../private/features.json'); //original json pull (a single array of objects)
var port = process.env.PORT || 3000;
var app = express();
// defining middleweare
app.use('/static', express.static(__dirname + '../../public'));
app.set('view engine', 'jade');
app.set('views', __dirname + '/templates');
...
// features route
app.get('/features', function(req, res) {
var path = req.path;
res.locals.path = path;
var features = gsheets.getFeatures(); //attempting to call js function above
res.render('features', {features: features}); //trying to pass data into a template
});
The first function successfully prints an array of objects to the server console, so I think the error lies in how I'm calling it in the main app.js. (Please note, it's only printing when I have it entered as gsheets.getFeatures();, not var features = gsheets.getFeatures();.)
Please also note that the featuresOld variable is an array of objects that has been successfully passed through to a jade tempalte, so the error is not in the res.render('features', {features: features}); line.
I'm sure this is pretty straightforward, but I can't seem to figure it out. Any help is greatly appreciated, thank you.
I'd recommend you to look into Promises (either Native or using a library like Bluebird).
But without using Promises or generators and keeping things simple, you can pass a callback function that will be called only when the values are retrieved. Within this function you can render the template.
(Note that your function currently does not return anything)
module.exports = {
getFeatures: function(callback) {
var request = require("request")
// ID of the Google Spreadsheet + Base URL
var spreadsheetID = "abcdefg-123456";
var sheetID = "od6";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID + "/" + sheetID + "/public/values?alt=json";
//empty array for features
var features = [];
//get the features
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = body.feed.entry;
data.forEach(function(item) {
var obj = {
pub: item.gsx$publication.$t,
date: item.gsx$date.$t,
title: item.gsx$title.$t,
url: item.gsx$url.$t,
}
features.push(obj);
});
console.log("features", features"); //prints array containing all objects to server console
callback(features); // call the rendering function once the values are available
}
});
}
};
Now in your main app, you just pass a callback to the function
app.get('/features', function(req, res) {
var path = req.path;
res.locals.path = path;
gsheets.getFeatures(function(features) {
res.render('features', {features: features}); //trying to pass data into a template
});
});
Basically, your request function is asynchronous - the request will run in background and the callback function will be called with the value once it's retrieved. In the meantime, the rest of the code will keep running (in your case you'd try to use the value even though it hasn't been retrieved yet).
If you need to do something that depends on that value, then you'd have to put that code in a callback function which would be called when the value is available (as showed above).
Promises provide a nice API for doing that. There are also new features ES6 that helps you better organise asynchronous code.
Context
I've come across excercise in learnyounode
npm install -g learnyounode
called HTTP JSON API SERVER.
Goal of an excercise:
Write an HTTP server that serves JSON data when it receives a GET request to the path '/api/parsetime'. Expect the request to contain a
query string with a key 'iso' and an ISO-format time as the value.
For example:
/api/parsetime?iso=2013-08-10T12:10:15.474Z
I wasn't able to solve it, which I think is due to lack of some specific API knowledge, so i looked up a solution, and found this (which works fine btw):
var http = require('http');
var url = require('url');
function parsetime (time) {
return {
hour: time.getHours(),
minute: time.getMinutes(),
second: time.getSeconds()
};
}
function unixtime (time) {
return { unixtime : time.getTime() };
}
var server = http.createServer(function (req, res) {
var parsedUrl = url.parse(req.url, true);
var time = new Date(parsedUrl.query.iso);
var result;
if (/^\/api\/parsetime/.test(req.url))
result = parsetime(time);
else if (/^\/api\/unixtime/.test(req.url))
result = unixtime(time);
if (result) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} else {
res.writeHead(404);
res.end();
}
})
server.listen(Number(process.argv[2]));
Actual question
I looked at this snippet and froze.
if (/^\/api\/parsetime/.test(req.url))
result = parsetime(time);
else if (/^\/api\/unixtime/.test(req.url))
result = unixtime(time);
Could anyone explain what /^\/api\/parsetime/.test(req.url) does exactly? At first it looks like some kind of regular expression, but I've never seen that RegExp could invoke methods. I guess that it is some form of identifying URL path, but guess is not worth much in industry.
Thanks everyone in advance!
It may look as if
if (/^\/api\/parsetime/.test(req.url))
result = parsetime(time);
else if (/^\/api\/unixtime/.test(req.url))
result = unixtime(time);
is invoking a function, but it is just generating a string output which the actual invoker called .test() turns into a truthful expression.
For more on javascript regex have a look at https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
The instructions say :
Write an HTTP server that serves JSON data when it receives a GET request to the path '/api/parsetime' (...) add second endpoint to the path '/api/unixtime'
which means the GET requests the server gets can be to paths other than '/api/parsetime' (resp. '/api/unixtime')
So, you want to test whether the GET request the server gets is to either of the 2 paths mentioned. And you do that with a regexp. An easy way to check the URL against the 2 URL given, is to use the test() method.
See examples of regexp on MDN :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
In:
if (/^\/api\/parsetime/.test(req.url))
result = parsetime(time);
else if (/^\/api\/unixtime/.test(req.url))
result = unixtime(time)
The first "/" character in the if statement denotes the beginning of a regex object in JavaScript and it is ended with another "/" character. So /a/.test('a') for example would evaluate to true.
The "^" character in a regex just denotes the first character in a regex and the "\/" is because the "/" is a special character that ends the regex, and the "\" in front is another special character that cancels out special characters, so with "\/" we end up with a literal "/" as the first("^") character in our search pattern.
So in the regex above /^\/api\/parsetime/ we keep the pattern going by canceling out the "/" characters. The "^" goes in front to say we need to start with a literal "/". And then we terminate the regex as always with "/".
Regular Expressions and other special characters found here:
https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
var http = require('http')
var url = require('url')
function parsetime (time) {
return {
hour: time.getHours(),
minute: time.getMinutes(),
second: time.getSeconds()
}
}
function unixtime (time) {
return { unixtime: time.getTime() }
}
var server = http.createServer(function (req, res) {
var parsedUrl = url.parse(req.url, true)
var time = new Date(parsedUrl.query.iso)
var result
if (/^\/api\/parsetime/.test(req.url)) {
result = parsetime(time)
} else if (/^\/api\/unixtime/.test(req.url)) {
result = unixtime(time)
}
if (result) {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(result))
} else {
res.writeHead(404)
res.end()
}
})
server.listen(Number(process.argv[2]))
I was wondering how you would use node.js to parse a string parameter from a request url akin to express.
I know this is possible with express, but I would like to know how it can be done with node.js without express.
Express example:
var app = require('express')();
app.get('sample/request/url/:id', function(req, res) {
var parameter = req.params.id;
});
If your are using connect (or just http module) you can use RegExp:
With http:
var http = require('http');
http.createServer(function (req, res) { // Note there's no next here
var match = req.url.match(/^sample\/request\/url\/(.+)$/);
var id = match ? match[1] : null;
}).listen(3000);
...
With connect:
var connect = require('connect');
connect.createServer(funcion(req, res, next) {
var match = req.url.match(/^sample\/request\/url\/(.+)$/);
var id = match ? match[1] : null;
}).listen(3000);
...
This is the simple case. If you want to have your own routing middleware you should start with an array of RegExps (that can be generated dinamically from a String that you add) and loop through them until you find a match.
Each route element should have its RegExp and also its parameters, so that once you find a match you can extract and append the parameters to the req object with an appropriate name that you choose.
EDIT:
As robertklep pointed out in his comment, you can check paramify. Its code is very clear and does some of the things I said in the last part of the answer. For example, you can see it has a function regify to dinamically contruct the RegExps and a loop to extract the parameters of a match:
var params = []
for (var i = 1; i < matches.length; i++) {
var key = reg.keys[i - 1]
if (key) {
params[key.name] = matches[i]
} else {
params.push(matches[i])
}
}
You can get the url property from req and parse as you want:
var server = require('http').createServer(function (req, res) {
console.log(req.url);
// would log "/sample/request/url/123"
});
The parse part can be done using RegEx.
I have the following code, and node.js can't resolve the url:
const request = require('request')
const teamURL = `/users/${user._id}/teams`;
const req = request({
url: teamURL,
json: true
},
function(error, response, body) {
if (!error && response.statusCode == '200') {
res.render('userHome.html', {
user: user,
teams: body
});
}
else {
console.error(error);
next(error);
}
});
is there a good way to use relative paths/urls with the request library on a server-side node.js Express app?
Giving just a relative url only works if it is clear from context what the root part of the url should be. For instance, if you are on stackoverflow.com and find a link /questions, it's clear from context the full url should be stackoverflow.com/questions.
The request library doesn't have this kind of context information available, so it needs the full url from you to do be able to make the request. You can build the full url yourself of course, for instance by using url.resolve():
var url = require('url');
var fullUrl = url.resolve('http://somesite.com', '/users/15/teams');
console.log(fullUrl); //=> 'http://somesite.com/users/15/teams');
But of course this will still require you to know the root part of the url.
Jasper 's answer is correct -- the request module needs full URL. if you are in a situation where you have a single page application, with lots of requests to an API with the same base URL, you can save a lot of typing by creating a module like this:
var url = require('url');
var requestParser = (function() {
var href = document.location.href;
var urlObj = url.parse(href, true);
return {
href,
urlObj,
getQueryStringValue: (key) => {
let value = ((urlObj && urlObj.query) && urlObj.query[key]) || null;
return value;
},
uriMinusPath: urlObj.protocol + '//' + urlObj.hostname
};
})();
then, to grab the base URL anytime you need it: requestParser.uriMinusPath
and grab the value of an arbitrary query parameter: RequestParser.getQueryStringValue('partner_key');