react-router generatePath with query parameters - reactjs

I am using react-router's built in function generatePath to generate a URL. The issue is that as far as I understand this function just returns the path and doesn't provide a mechanism for us to know which fields were added in the path and which were not.
For example, for the following code
generatePath('/user/:id', {
id: 1,
name: 'John',
})
the function returns /user/1 which is correct, but there is no way for us to know that only id was inserted into the path and name needs to be passed as query parameters.
In my application both the path template and the params object are dynamic and I need to add the extra fields in params as the query parameters.
Is there any way to do that ?

For anyone checking now, I ended up using the path-to-regexp library which is the one used internally by react-router to generate the URL. The code looks something like this
import pathToRegexp from 'path-to-regexp';
import qs from 'qs';
const compiledCache = {};
export default function generatePathWithQueryParams(rawPath, params) {
let toPath;
if (compiledCache[rawPath]) {
toPath = compiledCache[rawPath];
} else {
toPath = pathToRegexp.compile(rawPath);
compiledCache[rawPath] = toPath;
}
const queryParams = { ...params };
const path = toPath(params, {
encode: (value, token) => {
delete queryParams[token.name];
return encodeURI(value);
},
});
const queryString = qs.stringify(queryParams);
if (queryString) {
return `${path}?${queryString}`;
}
return path;
};

Related

Add query parameter in the middle of the URL using react fetch

I am writing a service for all my RestApis where I store RestApis and then use it when I need by calling methods.
RestApiService.js
let getData = 'https://myURL?id=123456';
export const getIndividualUsers = (resourceId) => {
const queryParams = {
userId: resourceId
};
queryParams.authorization = sessionStorage.getItem("authorization");
}
Object.keys(queryParams).forEach((k) => {
queryParams[k] = (typeof (queryParams[k]) !== 'string') ? JSON.stringify(queryParams[k]) : queryParams[k];
});
return getData.get('', queryParams);
};
I call this method when I need data using the method name and use the response in my code:
getIndividualUsers(123456).then((response));
Now one of my API has variable in the middle of the URL such as
URL: 'https://myURL/${variable}/children/12'
How do I pass queryParams(variable) in the middle of the Url? I will have variable value when I call the method that fetches the URL and not while defining the the URL.
Which library are you using? Anyway you can do like below always.
const getChild = (parentName, id) => {
let url = `https://myURL/${parentName}/children/${id}`;
...
};

How to get acess to the query argument followed after a questionmark?

After searching for a product inside a search box, my app executes a get request on
"localhost/api/products?searchword".
Now i want to build an express api, that searches for the searchword inside my db.
How do i get access to the searchword?
I tried with req.query.name but it only works with a link like
"localhost/api/products?name=searchword".
How do i get access with my specific link
I have an express API endpoint that does exactly that.
I call it using this URL structure: /blog/search?q=some+search+string
export const blogSearch: RequestHandler = async (req, res) => {
const queryString = (req.query.q || "");
// ...
}
In order to use req.query, you should use the ?key1=value1,key2=value2 structure.
http://expressjs.com/en/api.html#req.query
If you want to manually parse the queryString, you can use req.url and you'll get the /api/products?searchword, so you can split it on the ? the read your searchWord.
Maybe someone will encounter the same problem, so i want to post my code that worked
router.get('/',
expressAsyncHandler( async (req, res)=> {
const name = req.url.split('?')[1] || "";
console.log(name);
const nameFilter= name ? { title: {$regex: name, $options: 'i'} } : {};
const products = await Product.find({
...nameFilter,
}).populate();
res.send(products);
})
);

How do I add a query param to Router.push in NextJS?

With NextJS, I'm trying to route to another page using an object that has a to and as field:
export const routes = {
'BrowseList' : {
'to' : '/apps/Browse/list',
'as' : '/browse/list'
}
// ....
}
and then that's imported and used like so:
import { routes } from './__routes';
import Router from 'next/router';
// ....
const { to, as } = routes.BrowseList;
Router.push(to, as);
which all works. My dilemma is that I'm trying to do something similar to this while attaching a query param. I'm trying to follow this example according to the docs:
Router.push({
pathname: '/about',
query: { name: 'Zeit' },
})
What I've tried (which doesn't work):
Router.push({
pathname : to,
as,
query : { user_id: this.props.data.member.user.id },
});
which gives me a console warning of
Unknown key passed via urlObject into url.format: as
I know I can maybe possibly just use string interpolation and do something like this:
Router.push(to, `${as}?user_id=`${this.props.data.member.user.id}`)
but I was wondering if there was something I'm missing in the doc's example that also adds the query param into my as value.
Thank you.
You were close #nyphur. The as value goes as the second parameter of push and not inside the object that corresponds to to (check router.d.ts to see how push is defined). That's why you're getting the error Unknown key passed via urlObject into url.format: as. After 10 months from your question maybe this could still be useful to someone looking for an answer. Assuming you have a way to build the query string for the as parameter, following #Gezim answer or by any other approach:
Router.push({ pathname: to, query: { user_id: this.props.data.member.user.id } }, as, options);
NOTE: Based on #Gezim answer, if you format the string or pathname in the first parameter to contain your query params, it'll work BUT encoded values, if any, like %2B for instance will be decoded so you will get +. This doesn't happen if the query params object go inside query. Consider this if you have any kind of logic that depends on this.
It appears that the router in next.js doesn't have any convenient API to navigate to using a query string.
I created a utility class called LinkCreator with a toQuery method as follows. It uses query-string to create the query string:
import * as qs from 'query-string';
export class LinkCreator {
static query(object) {
return qs.stringify(object);
}
static toQuery(object, path = "/") {
const query = this.query(object);
return path + '?' + query;
}
}
Then, it can be used with Router.push like so:
Router.push(LinkCreator.toQuery({ name: 'Zeit' }), '/about');
Edit: at first I thought merging an object via spreading would be an easy fix, but then as a comment pointed out there needed to be some changes, so I have updated my answer to still utilize spreading, but unfortunately it does now make the Routes more complicated and involved, but the consumption of it is still straight forward.
I would also freeze the Routes object for peace of mind as well.
import Router from 'next/router';
export const Routes = Object.freeze({
BrowseList(query) {
return [
{
pathname: '/apps/Browse/list',
query
},
'/browse/list'
]
}
// ....
})
Router.push(
...Routes.BrowseList({
paramName: "Param value here"
})
)
Additional Abstraction
import Router from 'next/router';
const QueryRoutePath = (to, as, query) => ([
{
pathname: to,
query
},
as
])
export const Routes = Object.freeze({
BrowseList(query) {
return QueryRoutePath(
'/apps/Browse/list',
'/browse/list',
query)
}
// ....
})
const query = {
paramName: "Param value here"
}
Router.push(
...Routes.BrowseList(query)
)

React Router: Handling queries through React router

So, currently, I have a routing component:
<Route path="/lists/:query" component={Lists} />
I get a call like:
http://localhost:4567/lists/page=17&city_codes=2567
In my Lists component, I handle this query in this way:
componentDidMount() {
const query = match.params.query;
const cleanQueryString = query.replace(/[|;$%#"<>()+,]/g, '');
// break up the string using '&' and '=' into an object
const properties = this.queryURL(cleanQueryString);
const cleanQueryObj = _.pick(Object.assign({}, ...properties), [
'page',
'city_codes',
'min_monthly_fee',
'max_monthly_fee',
'order_by',
]);
// update the query object based on component state
this.setState({ query: cleanQueryObj }, () => {
cleanQueryObj.page && this.updateIndex(parseInt(cleanQueryObj.page, 10));
});
// call axios request and update redux
dispatch(handleLists(cleanQueryObj));
// update browser url
this.props.history.push(cleanQueryObj);
Now, I see a lot of major sites using ?q= before the query and I'm wondering what I'm missing or what could be improved?
Thoughts?
While what you are doing is technically valid, it is a bit non-standard. The way you use the router :query param and the way it is formatted, reaaaaly looks like an actual location.search parameter format, and not a path parameter.
A more standard way to do it, would be with the following URL:
http://localhost:4567/lists?page=17&city_codes=2567
And code as follow:
// In your routes, just a simple route with no path params
<Route path="/lists" component={Lists} />
// In your component
import queryString from 'query-string'
[...]
componentDidMount() {
// Use location object from react-router
const { search } = this.props.location
// Parse it using a npm dedicated module
const { page, city_codes } = queryString.parse(search)
// Now you can use those params
]);
Edit: and now an actual answer to the question:
?q=blah is usually used in a search context, with q parameter being a string used to search something. There can be other parameters following for example ?q=blah&ext=txt.
It is hence different from your :query path param, which is encoded to contain multiple parameters, while q here is a single ready-to-use parameter.

FeathersJS custom API method that retrieves enum Type, to fill a Dropdown in React

So I'm trying to fill a select component with a enum type from mongoose
In my user service the schema looks something like :
firstName: { type:String, required: true },
...
ris:{type: String, default: 'R', enum:['R', 'I', 'S']},
In my feathers service I can access the Model with "this.Model"
so in any hook I can do:
this.Model.schema.path('ris').enumValues); //['R','C','I']
and I get the values from the enum type.
Now since I can't create custom API methods other that the officials ones
Feathers calling custom API method
https://docs.feathersjs.com/clients/readme.html#caveats
https://docs.feathersjs.com/help/faq.html#can-i-expose-custom-service-methods
How can I create a service method/call/something so that I can call it in my
componentDidMount(){ var optns= this.props.getMyEnumsFromFeathers}
and have the enum ['R','C','I'] to setup my dropdown
I'm Using React/Redux/ReduxSaga-FeathersJS
I'd create a service for listing Enums in the find method:
class EnumService {
find(params) {
const { service, path } = params.query;
const values = this.app.service(service).Model.schema.path(path).enumValues;
return Promise.resolve(values);
}
setup(app) {
this.app = app;
}
}
app.use('/enums', new EnumService())
Then on the client you can do
app.service('enums').find({ query: {
service: 'myservice',
path: 'ris'
}
}).then(value => console.log('Got ', values));
I was trying to use this code, but, it does not work like plug and play.
after some play with the app service I figured out the code below
async find(params) {
const { service, path } = params.query;
const values = await this.app.service(service).Model.attributes[path].values;
return values || [];
}
setup(app) {
this.app = app;
}
I am not sure if it is a thing of what database is been used, in my case I am in development environment, so, I am using sqlite.

Resources