How Express routes similar url links? - node.js

Developing web app with node.js and express.
I have following two urls to distinguish:
/api/v1/source?id=122323
/api/v1/source?timestamp=1555050505&count=10
I come up a naive solution. I leave such similar urls to one route method and use if eles to specify solutions, i.e:
if(id){
//solution with id
}
if(timestamp&&count){
//solution with timestamp and count but without id
}
Apparently, this is not clean. Because in the future,I may want to add new field which will make this router huge and ugly.
So How can I overcome this? Or to change url structure.I want to build a Restful api.

Try to put together all the properties in a list and use Array#every to check if all the values in Array evaluates to true.
Maybe something like this:
(( /* req, res */)=>{
// Dummy express Request Object
const req = {
params : {
//id : '123',
count : 10,
timestamp : 1555050505,
newParameter : 'whatever value'
}
}
let { params } = req;
let {
id
, count
, timestamp
, newParameter
} = params;
if(id){
console.log('Action with id');
return;
}
let secondConditionArray = [
count, timestamp, newParameter
];
if( secondConditionArray.every(Boolean) ){
console.log('Second Action')
} else {
console.log('Some values are no truthy')
}
})()

You can get Url parameters with req.params
if(req.params.id){
//solution with id
}
if(req.params.timestamp && req.params.count){
//solution with timestamp and count but without id
}

Related

ES6 : Object restructuration for mailchimp api

I want to construct a object base on an array and another object.
The goal is to send to mailchimp api my users interests, for that, I've got :
//Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
//List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
//Mapping of user skill to all skills
const outputSkills = skillsUser1.map((skill) => skillsMailchimpId[skill]);
console.log(outputSkills);
The problem is after, outputSkill get me an array :
["ID1", "ID3"]
But what the mailchimp api need, and so what I need : :
{ "list_id_1": true,
"list_id_2": false, //or empty
"list_id_3" : true
}
A simple way would be this (see comments in code for explanation):
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Create an output object
const outputSkills = {};
// Use `Object.entries` to transform `skillsMailchimpId` to array
Object.entries(skillsMailchimpId)
// Use `.forEach` to add properties to `outputSkills`
.forEach(keyValuePair => {
const [key, val] = keyValuePair;
outputSkills[val] = skillsUser1.includes(key);
});
console.log(outputSkills);
The basic idea is to loop over skillsMailchimpId instead of skillsUser.
But that is not very dynamic. For your production code, you probably want to refactor it to be more flexible.
// Array of skills for one user
const skillsUser1 = ["SKILL1", "SKILL3"]
// List of all my skills match to mailchimp interest group
const skillsMailchimpId = {
'SKILL1': 'list_id_1',
'SKILL2': 'list_id_2',
'SKILL3': 'list_id_3',
}
// Use `Object.entries` to transform `skillsMailchimpId` to array
const skillsMailchimpIdEntries = Object.entries(skillsMailchimpId);
const parseUserSkills = userSkills => {
// Create an output object
const outputSkills = {};
// Use `.forEach` to add properties to `outputSkills`
skillsMailchimpIdEntries.forEach(([key, val]) => {
outputSkills[val] = userSkills.includes(key);
});
return outputSkills;
}
// Now you can use the function with any user
console.log(parseUserSkills(skillsUser1));

Getting image URL from Contentful entry id

I need to get an image URL from Contentful entry id.
I am getting such an JSON from Contentful query
{
"sys":{
"space":{
"sys":{
"type":"Link",
"linkType":"Space",
"id":"8v1e7eaw70p2"
}
},
"id":"1JfEwVlD9WmYikE8kS8iCA",
"type":"Entry",
"createdAt":"2018-02-28T18:50:08.758Z",
"updatedAt":"2018-02-28T18:50:08.758Z",
"revision":1,
"contentType":{
"sys":{
"type":"Link",
"linkType":"ContentType",
"id":"image"
}
},
"locale":"en-US"
},
"fields":{
"name":"heat",
"image":{
"sys":{
"type":"Link",
"linkType":"Asset",
"id":"6Inruq2U0M2kOYsSAu8Ywk"
}
}
}
}
I am using JS driver they provide:
client.getEntry()
so how to go thru that link: 6Inruq2U0M2kOYsSAu8Ywk ?
Unfortunately, the js SDK will not be able to resolve links when using the single entry endpoint i.e client.getEntry() because there won't be enough data.
When thing I always recommend to work around this is to use the collection endpoint with a query the desired id as a query param. This way you will always get the desired entry with all it's linked data.
Your code should look something like this
client.getEntries({'sys.id': '6Inruq2U0M2kOYsSAu8Ywk'})
.then(response => console.log(response.items[0].fields.image.fields.file.url))
I hope that helps.
Best,
Khaled
Use client.getEntries({'sys.id': '1JfEwVlD9WmYikE8kS8iCA'})
To get the entry fields and the asset fields.
You can also patch the assets to the fields by running this after fetching the data:
/* Patch all the assets to the fields */
const patchAssets = (fields, assets) => {
Object.keys(fields).forEach(function (key) {
let obj = fields[key];
if (obj.sys && obj.sys.linkType === 'Asset') {
const assetId = obj.sys.id;
const matchAsset = assets.find(asset => {
return asset.id === assetId;
});
obj.file = matchAsset;
}
});
return fields;
};
Another way to get image url is to use getAsset('<asset_id>'). So first, using the getEntry() method, you need to get the entry data, then extract the id from the field: fields.image.sys.id, and pass it to the getAsset method.

Passing path parameters in axios

I am using Axios with NodeJs and trying to pass path parameters in axios.get() method. For example, if URL is url = '/fetch/{date}', I want to replace {date} with the actual date while calling axios.get(url).
I went through the source code on Github and StackOverflow, but couldn't find any method.
Is it possible to keep URLs with parameters as a placeholder and replace them while actually calling the get method of Axios?
Axios doesn't have this feature and it looks like the team don't want to add it.
With credit to previous responders for inspiration, to me this seems like the solution closest to what you (and me) are looking for:
1 - Where you want to store all your URLs and their parameters, define them as functions which use a template string to return the composed URL:
export var fetchDateUrl = (date) => `/fetch/${date}`;
If you need any type-specific formatting of the value being concatenated into the URL, this function is a good place to do it.
2 - Where you want to make the request, call the function with the correct parameters:
import { fetchDateUrl } from 'my-urls';
axios.get(fetchDateUrl(someDateVariable))...;
Another variation, if you really like the idea of naming the parameters at the call site, you can define the URL function to destructure an object like this:
var fetchDateUrl = ({date}) => `/fetch/${date}`;
which you'd then use like this:
axios.get(fetchDateUrl({date: someDateVariable}));
Use template strings
url = `/fetch/${date}`
Or just tag it on
url = '/fetch/'+ date
I think using axios interceptors is better to do this :
//create your instance
const instanceAxios = axios.create({
baseUrl: 'http://localhost:3001'
]);
instanceAxios.interceptors.request.use(config => {
if (!config.url) {
return config;
}
const currentUrl = new URL(config.url, config.baseURL);
// parse pathName to implement variables
Object.entries(config.urlParams || {}).forEach(([
k,
v,
]) => {
currentUrl.pathname = currentUrl.pathname.replace(`:${k}`, encodeURIComponent(v));
});
const authPart = currentUrl.username && currentUrl.password ? `${currentUrl.username}:${currentUrl.password}` : '';
return {
...config,
baseURL: `${currentUrl.protocol}//${authPart}${currentUrl.host}`,
url: currentUrl.pathname,
};
});
// use like :
instanceAxios.get('/issues/:uuid', {
urlParams : {
uuid: '123456789'
}
})
For typescript users, you will need to add this, in one of your .d.ts
declare module 'axios' {
interface AxiosRequestConfig {
urlParams?: Record<string, string>;
}
}
( this is a POC, not really tested, doesn't hesitate if you see something wrong )
You can use template strings ie:
let sellerId = 317737
function getSellerAnalyticsTotals() {
return axios.get(`http://localhost:8000/api/v1/seller/${sellerId}/analytics`);
}
Given some API /fetch/${date} you likely want to wrap your axios call in a function.
const fetchData = (date) => axios.get(`/fetch/${date}`);
fetchData(dateObject.toFormat('yyyy-mm-dd'))
.then(result => { ... });
This requires the calling code to format date correctly however. You can avoid this by using a DateTime library that handles date string parsing and do the format enforcement in the function.
const fetchData = (date) => axios.get(`/fetch/${date.toFormat('yyyy-mm-dd')}`);
fetchData(dateObject)
.then(result => { ... });
you can do like this:
getProduct = (id) => axios.get(`product/${id}`);
I always do it like this:
const res = await axios.get('https://localhost:3000/get', { params: { myParam: 123 } });
I find this to be much clearer than template strings.
More explanation here

Search information by user on firebase

This is probably very simple ,so sorry...
I'm trying to create an application where i can create users and store informations by Firebase. There is an example here :
http://image.noelshack.com/fichiers/2015/11/1426182307-log.png
But now I want to check if the name already exists if someone wants to create a new user.
How would I go about grabbing individual user information without knowing their id , like simplelogin:54?
I've found this topic Get users by name property using Firebase but it is not the same thing because in my case I don't know the children after "Users"
Cheers,
Like Frank said, you must know something about the user to be able to look her/him up.
However, here is a general answer. I am assuming that by "name" you mean the property "identifiant" that you've created.
Suggestions
Start by looking over the Firebase Query documentation.
Short Answer
To check if a user exists by the identifiant property, you'd orderByChild("identifiant") and query for a specific user with the .equalTo("<identifient_here>").
For example, to check if a user with identifient="aaa",
var usersRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/Users");
var identifient = "aaa";
usersRef.orderByChild("identifiant").equalTo(identifient).once("value", function(snapshot) {
console.log("Loaded user by identifient:",identifient,snapshot.val());
});
If instead you want to query by the key (such as simplelogin:53), you could query by using orderByKey() instead of orderByChild()... or just simply setting the ref to the user's key like so:
var userKey = 'simplelogin:53';
var userRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/Users" + userKey);
userRef.once("value", function(snapshot) {
console.log("Loaded user",snapshot.val());
});
Long(er) Answer
You can handle this with a user factory (see Angular Providers documentation).
You return a promise in the factory using the $q service.
Here is the Angular API documentation for $q.
Example with UserFactory
Check out this working PLNKR example.
It's tied to one of my public Firebase instances.
I created the same simplelogin:53 user in /Users like you have.
If you search for the identifient = aaa, you'll get the right user.
The controller implementation here is for example purposes, and doesn't really do anything worth while. It's just for reference.
The Data
{
"Users" : {
"simplelogin:53" : {
"identifiant" : "aaa"
}
}
}
UserFactory
.factory('UserFactory', function($q, $firebaseObject, fbUrl){
return function(userId){
var deferred = $q.defer();
if (userId.isNotEmpty()) {
var userRef = new Firebase(fbUrl + '/Users/').orderByChild("identifiant").equalTo(userId);
userRef.once("value",
function(dataSnapshot){
if (dataSnapshot.val()) {
console.log("Loaded user",dataSnapshot.val());
deferred.resolve(dataSnapshot.val());
} else {
console.info("Couldn't find user by id",userId);
deferred.reject("No user found by identifient = '"+userId+"'");
}
},
function(error){
console.error("Error loading user",error);
deferred.reject(error);
}
);
} else {
deferred.reject("No id entered!");
}
return deferred.promise;
}
})
Controller
.controller('HomeController',function($scope, UserFactory) {
$scope.identifient = '';
var showError = function(errorMessage) {
if (errorMessage) {
showUser(false);
$scope.error = errorMessage;
} else {
delete $scope.error;
}
}
var showUser = function (userObject) {
if (userObject) {
showError(false);
$scope.user = userObject;
} else {
delete $scope.user;
}
}
$scope.loadUser = function() {
var userPromise = new UserFactory($scope.identifient);
userPromise.then(function(data){
showUser(data);
}).catch(function(error){
showError(error);
});
}
})
Template
<div ng-controller="HomeController">
<h2>Home Template</h2>
<input ng-model="identifient" placeholder="identifient"/>
<button ng-click="loadUser()">Find User</button>
<hr/>
<div ng-if="user">User: {{user}}</div>
<div ng-if="error">Error: {{error}}</div>
</div>
Hope that helps.

I need to create url for get which is going to accept array, how in node.js/express extract array from request?

I need to create url for get which is going to accept array, how in node.js/express extract array from request ?
I need to pass array with names which parametes I need to back from Person
model.
/api/person # here I need to pass which fields I want to see but to be generic.
One option is using a JSON format.
http://server/url?array=["foo","bar"]
Server side
var arr = JSON.parse(req.query.array);
Or your own format
http://server/url?array=foo,bar
Server side
var arr = req.query.array.split(',');
Express exposes the query parameter as an array when it is repeated more than once in the request URL:
app.get('/', function(req, res, next) {
console.log(req.query.a)
res.send(200)
}
GET /?a=x&a=y&a=z:
// query.a is ['x', 'y', 'z']
Same applies for req.body in other methods.
You can encode an array in percent encoding just "overwriting" a field, formally concatenating the values.
app.get('/test', function(req,res){
console.log(req.query.array);
res.send(200);
});
localhost:3000/test?array=a&array=b&array=c
This query will print ['a','b','c'].
Using next code:
app.use('/', (req, res) => {
console.log(req.query, typeof req.query.foo, Array.isArray(req.query.foo));
res.send('done');
});
On backend, you have two standard approaches. For next requests:
/?foo=1&foo=2
/?foo[]=1&foo[]=2
your NodeJS backend will receive next query object:
{ foo: [ '1', '2' ] } 'object' true
{ foo: [ '1', '2' ] } 'object' true
So, you can choose the way you want to. My recommendation is the second one, why? If you're expect an array and you just pass a single value, then option one will interpret it as a regular value (string) and no an array.
[I said we have two standards and is not ok, there is no standard for arrays in urls, these are two common ways that exist. Each web server does it in it's own way like Apache, JBoss, Nginx, etc]
If you want to pass an array from url parameters, you need to follow the bellow example:
Url example:
https://website.com/example?myarray[]=136129&myarray[]=137794&myarray[]=137792
To retrieve it from express:
console.log(req.query.myarray)
[ '136129', '137794', '137792' ]
Express has a tool to check if your path will match the route you are creating : Express.js route tester.
As Jose Mato says you have to decide how to structure your url:
?foo=1&foo=2
?foo[]=1&foo[]=2
The http request should look like this, if you chose method 1:
http://baseurl/api/?foo=1&foo=2
Your route should have this logic:
app.get('/api/:person', (req, res) => {
/*This will create an object that you can iterate over.*/
for (let foo of req.params.foo) {
/*Do some logic here*/
}
});
You can pass array elements separated by slashes -
GET /api/person/foo/bar/...
Define your route as '/api/person/(:arr)*'
req.params.arr will have the first parameter.
req.params[0] will have the rest as string.
You split and create an array with these two.
app.get('/api/person/(:arr)*', function(req, res) {
var params = [req.params.arr].concat(req.params[0].split('/').slice(1));
...
});
GET /api/person/foo
params = ["foo"]
GET /api/person/foo/bar
params = ["foo", "bar"]
...
Here use this, '%2C' is the HTML encoding character for a comma.
jsonToQueryString: function (data) {
return Object.keys(data).map((key) => {
if (Array.isArray(data[key])) {
return encodeURIComponent(`${key}=${data[key].map((item) => item).join('%2C')}`);
}
return encodeURIComponent(`${key}=${data[key]}`);
}).join('&');
}
To access the query params
const names = decodeURIComponent(req.query.query_param_name);
const resultSplit = names.split(',');
Theres a problem when the array param contains only one value, because then it behaves like a simple string. Or when it doesn't contain any value.
Eg.:
?arrayParam=5
?
?arrayParam=5&arrayParam=6
Here's a function that always extracts the param with the given name, as an array:
export function extractArrQueryParams(
req: express.Request,
paramName: string
) {
let param = req.query[paramName];
let returnArray = [];
if (Array.isArray(param)) {
for (let e of param) {
returnArray.push(e);
}
} else {
if (param) {
returnArray.push(param);
}
}
return returnArray;
}

Resources