Passing input to predefined URL path parameter in js - javascript

I am new to react, redux and axios. I am using the library to make calls to a backend. I would like to make a request with a template like /api/posts/:id. After reading the documentation it seems that axios support only query string parameters by using the params property. Is there any solution in which I could pass the parameters using the library, aside from the obvious solution of adding the parameters myself to the url?

What I understand is you want to send to api/posts/:id and not in query string like api/posts?id=someid and if that is the case then you can create the url yourself and hit it as :
const url = 'api/posts/' + id;
axios.get(url)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

Unfortunately, at the time of this writing, there's no native predefined string formatting by object in js. You'll have to rely on string templating library such as lodash.template, mustache.js, handlebar.js.
Example:
/**
* Formatting string using lodash
* install it by:
* $ npm i lodash.template
*/
const template = require('lodash.template');
const compiled = template('/api/posts/<%= id %>/other/filter/<%= encodeURIComponent(name) %>/<%= nested.id %>');
let output = compiled({id: "12345678", name: "<good bot>", nested: {id: "777"}});
console.log(output); //output: "/api/posts/12345678/other/filter/<good bot>/777"
But in your case, this pose a problem: lodash doesn't offer an option to escape strings other than HTML, you'll have to escape it either in your template (as demonstrated with encodeURIComponent(name) in code above), or your object's value.
Another option is using mustache.js, overwrite the mustache HTML's escape function:
/**
* Formatting string using mustache
* install it by:
* $ npm install mustache --save
*/
const mustache = require('mustache');
mustache.escape = (value) => encodeURIComponent(value);
const output = mustache.render(
'/api/posts/{{id}}/other/filter/{{name}}/{{nested.id}}',
{id: "12345678", name: "<good bot>", nested: {id: "777"}}
);
console.log(output); //output: "/api/posts/12345678/other/filter/%3Cgood%20bot%3E/777"
Then you can now pass output to axios.

You can send GET parameter as a second argument in axios.
The Syntax:
axios.get(url[, config])
An example:
axios.get('/api/posts/', {
params: {
id: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

Related

How can I read json data which has its tag/identifier in quotes?

I'm using NodeJS to fetch the NPM registry with the code below.
import fetch from "node-fetch";
let url = "https://registry.npmjs.com/[package-name]";
let settings = { method: "Get" };
fetch(url, settings)
.then(res => res.json())
.then((json) => {
console.log(json.versions)
});
So, the response that I get in my result is:
{
_id: '123456789',
name: 'package-name',
'dist-tags': { latest: '2.0.0' },
versions: {
'2.0.0': '...'
}
...
}
I am trying to read the 'versions' section, but the issue I have is that the version number 2.0.0 is encased in quotes and it might change but I want to find that value. How can I read it?
Thanks in advance, and let me know any other info I missed.
You'll have to check if the versions has the key you want.
if(Object.keys(response.versions).includes('2.0.0')){
// response.versions['2.0.0'] exists
}
Or you could iterate through Object.keys(response.versions) and handle each version entry as you wish.

Error `Template parameter names *must* be separated`

I am using mithril.js in frontend application and backend application is running on ipv6 environment.
Calling post ajax request to backend using mithril.js.
async post(url, body = {}) {
return new Promise((resolve, reject) => {
m.request({method: 'POST', url, body}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
Backed url is like this: http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend.
But getting this error Template parameter names *must* be separated while calling backend api.
Explanation of the error
Based on the documentation of m.request(), you can specify dynamic URLs:
Request URLs may contain interpolations:
m.request({
method: "GET",
url: "/api/v1/users/:id",
params: {id: 123},
}).then(function(user) {
console.log(user.id) // logs 123
})
In the code above, :id is populated with the data from the params object, and the request becomes GET /api/v1/users/123.
Interpolations are ignored if no matching data exists in the params property.
m.request({
method: "GET",
url: "/api/v1/users/foo:bar",
params: {id: 123},
})
In the code above, the request becomes GET /api/v1/users/foo:bar?id=123
Since your backend URL contains colons, it's interpreted as being a dynamic URL.
According to the documentation of m.buildPathname(), m.request() uses m.buildPathname() internally to process dynamic URLs.
The beginning of m.buildPathname() contains the following check regarding parameters of a path template (dynamic URL = path template populated with path parameters):
if ((/:([^\/\.-]+)(\.{3})?:/).test(template)) {
throw new SyntaxError("Template parameter names *must* be separated")
}
(Source: https://github.com/MithrilJS/mithril.js/blob/v2.0.4/mithril.js#L1288-L1292)
And, again, since your backend URL contains colons, this is where you are getting the error. (You can verify this by trying to run m.buildPathname('http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend') – you'll get the same error.)
How to fix it
Since you can't get away from that regex check at the beginning of m.buildPathname(), your best bet might be to use a dynamic URL. Like so:
m.buildPathname(':url...', { url: 'http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend' })
// => http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend
Or when applied to your code:
async post(url, body = {}) {
return new Promise((resolve, reject) => {
m.request({method: 'POST', url: ':url...', body, params: {url}}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
Or alternatively you can specify the (dynamic) URL as the first argument of m.request():
async post(url, body = {}) {
return new Promise((resolve, reject) => {
m.request(':url...', {method: 'POST', body, params: {url}}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
Notice that there are three dots after the path parameter :url. Otherwise its value would be escaped/encoded. This is mentioned in the documentation of path handling. Example:
m.buildPathname(':url', { url: 'http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend' })
// => http%3A%2F%2F%5B340f%3Ac0e0%3A1d1%3A5gc0%3Ag4fs%3A2%3A%3A%5D%3A22923%2Fbackend
Handling URL parameters
As mentioned in the other answer, if the URL contains parameters, the question mark will be duplicated:
m.buildPathname(':url...', { url: 'https://example.com/foo?bar=baz' })
// => https://example.com/foo??bar=baz
// ^^
One way to solve that would be to include the parameters in the path template:
const url = 'https://example.com/foo?bar=baz'
const [baseUrl, params] = url.split('?')
const template = ':baseUrl...' + (params ? `?${params}` : '')
m.buildPathname(template, { baseUrl })
// => https://example.com/foo?bar=baz
However, if there are colons in the URL parameters, there's a possibility that you'll get the same error as originally ("Template parameter names *must* be separated").
There might be a way to solve this, but the previous code sample is already quite complex for this relatively simple use case. Which leads us to:
Alternative solution: don't use m.request()
m.request() is just "a thin wrapper around XMLHttpRequest." It "returns a promise and triggers a redraw upon completion of its promise chain."
If m.request() is difficult to work with due to using IPv6 URLs (or for other reasons), it can be easier to use something else for doing XHR requests. You could for example use fetch() – just remember to call m.redraw() at the end (m.request() does this automatically).
Sure, m.request() does more than just calls m.redraw() at the end (see the docs), but it's also okay to use something else.
Thanks mts knn for the reply. We have implemented your solution however we faced below issues.
Question mark is passing two times in http url of api. Please find attached below screenshot.
In order to fix this problem, please find the updated code below
async post(url, body = {}) {
var queryIndex = url.indexOf('?');
var httpPart = url.slice(0,queryIndex);
var finalUrl = url.replace(httpPart,":url...");
return new Promise((resolve, reject) => {
m.request({method: 'POST', url: finalUrl, body, params: {url: httpPart}}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
You can also provide efficient solution If any.

Read settings contained in a json file in javascript

Im tring to read a simple setting from a json file, the json is this :
{
"Label": "some string here"
}
form my javascript part i do:
import settings from '../settings.json';
then:
var settings= ()=> {
const headers = new Headers();
const requestOptions = {
method: 'GET',
headers: { ...headers.authentication, ...headers.culture, 'ContentType':'application/json',
};
return fetch(`${settings.Label}`, requestOptions).then(() => {
return response.text().then(text => {
const data = text ? text && JSON.parse(text) : {};
let token = response.headers.get('X-Token');
if (token) {
data.token = token;
}
if (!response.ok) {
// manage error here
}
return Promise.reject(error);
}
return data;
})
});
};
// use settings here
Despite my many searches and attempts im not very expert in javascript,i have tried in many ways before, but the my variable 'settings' is not contain nothing.
I believe you need to add an export to your JSON file
export const settings = {
"label": "some string here"
}
Not much information given here, but this probably has to do with transpiling your javascript. You can use:
const settings = require('../settings.json')
instead.
try this answer https://stackoverflow.com/a/59844868/7701381
Also, change the name of the imported json settings or the var settings, cuz this might cause unexpected behaviors
I had completely wrong the approach, the file is already available and I don't have to request to download it from the server, I just have to return string, without use of fetch or other:
return (`${settings.Label}`
Sorry and thank a lot for the support

Use existing variables when using refetchQueries in react-apollo

I am using postsConnection query for infinite scroll. It contains variables like after.
After doing an upvote mutation, I want to refetchQueries... like this 👇
const upvote = await client.mutate({
mutation: UPVOTE_MUTATION,
variables: {
postId: this.props.post.id
},
refetchQueries: [
{ query: POST_AUTHOR_QUERY }
]
})
Above code gives error because POST_AUTHOR_QUERY accepts few variables. Here's that query 👇
export const POST_AUTHOR_QUERY = gql`
query POST_AUTHOR_QUERY($authorUsername: String! $orderBy: PostOrderByInput $after: String){
postsAuthorConnection(authorUsername: $authorUsername orderBy: $orderBy after: $after) {
....
}
}
I do not want to add variables manually. Variables are already stored in the cache. How do I reuse them while using refetchQueries???
Here are a few resources I have read about this issue 👇
https://github.com/apollographql/react-apollo/issues/817
https://github.com/apollographql/apollo-client/issues/1900
As mentioned in the issue you linked, you should be able to do the following:
import { getOperationName } from 'apollo-link'
const upvote = await client.mutate({
// other options
refetchQueries={[getOperationName(POST_AUTHOR_QUERY)]}
})
From the docs:
Please note that if you call refetchQueries with an array of strings, then Apollo Client will look for any previously called queries that have the same names as the provided strings. It will then refetch those queries with their current variables.
getOperationName simply parses the document you pass it and extracts the operation name from it. You can, of course, provide the operation name yourself as a string instead, but this way avoids issues if the operation name changes in the future or you fat finger it.
If you don't want to pull in apollo-link, you can also get this via the base graphql package (note that I use optional chaining for convenience:
import { getOperationAST } from 'graphql';
const operationName = getOperationAST(POST_AUTHOR_QUERY)?.name?.value;
// Note that this could technically return `undefined`
const upvote = await client.mutate({
mutation: UPVOTE_MUTATION,
variables: {
postId: this.props.post.id
},
refetchQueries: [operationName]
})

Can't extract data from $resource (consuming rest webservice)

I'm trying to consume a REST webservice, responding with a JSON String containing a fairly "complex" schema.
I created a model that contains every fields sent by the webservice.
Here are the relevant codes that should be a problem :
public getUser(user_id: number): PlanDeCharge.Modeles.User {
var toto;
this.UserRest.get({ user_id: user_id }, function(){}, function(err){
this.$window.location.href = "http://localhost:8080/myapp_webapp/login.do";
}).$promise.then(function(data){
toto = data;
});
return toto;
}
-
this.userConnecte = this.gestionUserService.getUser(759);
-
export function userRest($resource: ng.resource.IResourceService, $cookies: ng.cookies.ICookiesService): PlanDeCharge.Modeles.IUserResource {
this.key = $cookies.get("encodedKey");
var urlService: string = "http://localhost:8080/teambox_webapp/resource-rest/V1_1/users/:user_id";
return <PlanDeCharge.Modeles.IUserResource> $resource(urlService, {user_id: "#user_id"}, {
get:{
headers:{"key" : this.key}
}
});
}
app.factory("UserRest", ["$resource", "$cookies", userRest]);
I did a lot of modifications, trying to fix the call without success... The request actually get a response containing the JSON string, but I can't put it inside an object to be use (like user['id'] = 2)
Thanks in advance
I deleted the last post and made this new one, the first one wasn't clear enough and people were confused
When working with promises you should let Angular handle the resolvement.
Am I right, if you are actually using AngularJS 1 and not ng2 as the question is tagged? The syntax is ng1 anyways.
Some notes on the getUser method. Return the reference created by $resource instead of creating one your self. Further more, use the fat-arrow syntax on the callbacks to bind this to the proper context. See this article for more on this.
To remove even more code use TypeScripts object initialization and init the user id object with just { user_id }. This creates a JavaScript object with a property user_id with the value of user_id.
public getUser(user_id: number): SomeModel {
return this.UserRest
.get({ user_id }, () => { }, () => {
this.$window.location.href = "http://localhost:8080/myapp_webapp/login.do";
});
}
In your component or controller access
this.userConnecte = this.gestionUserService.getUser(759);
Lastly, the factory/service.
Use the fact that $resource is generic and set your variables as constants when not changed.
export function userRest(
$resource: ng.resource.IResourceService,
$cookies: ng.cookies.ICookiesService
): ng.resource.IResourceClass<PlanDeCharge.Modeles.IUserResource> {
this.key = $cookies.get("encodedKey");
const urlService = "http://localhost:8080/teambox_webapp/resource-rest/V1_1/users/:user_id";
return $resource<PlanDeCharge.Modeles.IUserResource>(urlService, { user_id: "#user_id" }, {
get: {
headers: { "key": this.key }
}
});
}
This should fix your problems and make to code more readable. :)

Categories