I have an api that requires some parameters to filter based on the the passed in parameter. It is similar top localhost:8000/api/v1/products?page=1&user=62831ebc8cecf3c829c4b2d9&category=62831ebc8cecf3c829c4b2d9
So I created a react action
export const listProducts =
(page = '', user = '', category = '') =>
async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST })
const { data } = await axios.get(
`${process.env.REACT_APP_API}/api/v1/products?&page=${page}&user=${user}&category=${category}`
)
dispatch({
type: PRODUCT_LIST_SUCCESS,
payload: data,
})
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
The Idea is that when I when I dispatch dispatch(listProducts(1, user._id) and I leave the 3rd parameter empty, It returns Invalid category ''
How can I make the api ignore unsent paramaters and only use sent parameters.
I have tried replacing (page = '', user = '', category = '') with (page = null, user = null, category = null)
Now i get Invalid category null
EDIT: In some instances, i would want to pass only categories while in some other instances, I would want to pass user and page only. How can I do this ${process.env.REACT_APP_API}/api/v1/products?&page=${page}&user=${user}&category=${category} to only accept data in the object and ignore what's not available
If the number of parameters passed to the function is frequently changing, or if you don't have control on what will be passed to the function I would achieve this by using a single object as a parameter rather than multiple parameters.
Example:
Instead of export const listProducts = (page = '', user = '', category = '') => you can pass a single object and deconstruct the fields from the object like:
export const listProducts = ({page, user, category }) =>
//will be passed like:
listProducts({
page: 1,
user: data.user,
category: ''
})
Why?
You don't have to control the order of the parameters while passing arguments to the function and if a field is not passed to the function; it will evaluate to undefined:
const obj1 = {
page: 1,
user: 'test',
category: 'first'
}
const obj2 = {
page: 2,
user: 'test2'
}
const obj3 = {
user: 'test3'
}
const listProducts = ({page, user, category}) => {
const str = `Parameters:\n page: ${page}\nuser: ${user}\ncategory: ${category}`
console.log(str)
}
listProducts(obj1)
listProducts(obj2)
listProducts(obj3)
console.log("\n-------------DEFAULT VALUES------\n")
/* Default value example: */
const listProducts2 = ({page = 1, user = '', category = ''}) => {
const str = `Parameters:\n page: ${page}\nuser: ${user}\ncategory: ${category}`
console.log(str)
}
listProducts2(obj1)
listProducts2(obj2)
listProducts2(obj3)
Ignoring undefined parameters (may not be efficient, just a try):
let url = `${process.env.REACT_APP_API}/api/v1/products?`
if(user) url += `&user=${user}`
if(page) url += `&page=${page}`
if(category) url += `&category=${category}`
Related
I want to apply server side search filter by text using redux toolkit.
I have two query builder methods in place. One for fetching all items and second for fetching only filtered data.
Query builder for fetching all items is
getAllBlogs: builder.query<BlogType[], void>({
queryFn: async () => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(collectionRef, limit(1000))
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType),
}
},
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = [
{ type: 'Blogs', id: 'LIST' },
]
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
This works fine and I'm getting the whole list through useGetAllBlogsQuery data.
Query builder for fetching filtered data is here: (Partially completed)
getBlogsByTitle: builder.query<BlogType[], string>({
queryFn: async (title) => {
const collectionRef = collection(Firestore, BLOG_COLLECTION)
const q = query(
collectionRef,
where('searchIndex', 'array-contains', title),
limit(1000),
)
const resp = await getDocs(q)
return {
data: resp.docs.map((doc) => doc.data() as BlogType), // Correct data
}
},
// I'm trying to only push the resultant items in state. This is not working
providesTags: (result) => {
const tags: { type: 'Blogs'; id: string }[] = []
if (result) {
result.forEach(({ id }) => {
tags.push({
type: 'Blogs',
id,
})
})
}
return tags
},
}),
I have react component looks like this where I'm calling these queries.
const Blogs: NextPage = () => {
const { data: blogs } = blogsApi.useGetAllBlogsQuery()
const [getBlogsByTitle] = blogsApi.useLazyGetBlogsByTitleQuery()
const debounced = useDebouncedCallback(async (value) => {
const { data } = await getBlogsByTitle(value)
console.log(data) // Correct data
}, 500)
return (
<div>
<InputText
onChange={(e) => debounced(e.target.value)}
/>
</div>
)}
The above code has two functionalities.
Fetch all the items on initial load.
Filter when debounced function is being called.
What I want is when getBlogsByTitle is called it will auto update the same state blogs in redux and we don't have to do much.
We are getting correct response in getBlogsByTitle but this query is not updating state with only its filtered response.
I'm new to redux-toolkit. Can someone help me out here where am I doing wrong ?
How can I resolve this? I put req.query parameter with an array but I can't destructure or use items from my array. I getting on my next.js API backend just [object Object] or undefined. How can I select what I want?
const fetchData = async (queryKey: any, manufacturer) => {
const res = await fetch(
`http://localhost:3000/api/data/get?&manufacturer=${manufacturers}`
);
return await res.json();
};
const manufacturers = [
{ name: 'Samsung', type: 'TV' },
{ name: 'Nokia', type: 'Phone' },
];
const { data, status } = useQuery(
['somekey', manufacturer],
({ queryKey }) => fetchData(queryKey, manufacturer)
);
And here is the next API where I can't get values from the query I getting just
[Object, Object] or undefined,
this code work if just put on the front end just a simple array or string without an object in variable manufacturers.
But how i can get values from [Object, object]?
I try it
const { name, type } = req.query.manufacturer
const { name, type } = req.query.manufacturer[0]
or select just one field dont work to = const some = req.query.manufacturer[0].name
export const handler = async (
req: NextApiRequest,
res: NextApiResponse<Data>
) => {
const { name, type } = req.query.manufacturer;
};
export default handler;
Could you try const [{ name, type }] = req.query.manufacturer? Seems to be what you need
edit
Also, you may want to try instead of http://localhost:3000/api/data/get?&manufacturer=${manufacturers} => http://localhost:3000/api/data/get?&manufacturer=${encodeURIComponent(JSON.stringify(manufacturers))}
It probably will be accessible with { name, type } = req.query.manufacturer then
Let's say I have this function in my TypeScript API that communicates with database.
export const getClientByEmailOrId = async (data: { email: any, id: any }) => {
return knex(tableName)
.first()
.modify((x: any) => {
if (data.email) x.where('email', data.email)
else x.where('id', data.id)
})
}
In modify block you can see that I check what param was passed - id or email.
In code it looks like this:
const checkIfEmailUsed = await clientService.getClientByEmailOrId({ email: newEmail })
And here is the problem, I can't do that because of missing parameter. But what I need, is to pass it like this, and check what param was passed.
Of course, I can just do this:
const checkIfEmailUsed = await clientService.getClientByEmailOrId({ email: newEmail, id: null })
And this going to work. But does exist solution not to pass it like this: { email: newEmail, id: null }, but just by { email: newEmail }?
I think you are looking for optional parameters. You can mark properties of an object as optional by adding an ? to the type declaration.
type Data = {
email: any,
id?: any // <= and notice the ? here this makes id optional
}
export const getClientByEmailOrId = async (data: Data) => {
return knex(tableName)
.first()
.modify((x: any) => {
if (data.email) x.where('email', data.email)
else x.where('id', data.id)
})
}
When i do this, after first return i don't have user.uid value:
const functions = require(`firebase-functions`);
exports.createNewUser = functions.auth.user().onCreate((user: { uid: string; }) => {
const newUserWallet: UserWallet = new UserWallet(user.uid);
const rootUserWallet = admin.database().ref(`/Users/UserWallet/${user.uid}`); //user.uid have value
return rootUserWallet.set(newUserWallet).then(() => {
const newUserSettings: UserSettings = new UserSettings(user.uid);
const rootUserSettings = admin.database().ref(`/Users/UserSettings/${user.uid}`); //user.uid is empty
return rootUserSettings.set(newUserSettings);
})
})
I try send value like this:
.then((user.uid) => { or this: .then(user.uid => {, but i have error:
Argument of type 'string' is not assignable to parameter of type '((value: void) => void | PromiseLike<void>) | null | undefined'.ts(2345)
How can I sent this value there? It's Firebase Function triggered OnCreate new user. Write in TypeScript.
Not sure what you're trying to do with the { uid: string; } bit. I don't think that's the type of the user object.
In either case, why not put user.uid into a const and use it within your function from there? like this:
exports.createNewUser = functions.auth.user().onCreate(user => {
const uid = user.uid;
// replace all instances of 'user.uid' with just 'uid'?
})
I have the following data:
data = {
id: 10012,
name: "abc",
hobby: ["cricket", "football"]
}
My table structure is:
id int64,
name string(25),
petName string(20),
hobby string(25)
How to insert an array of data into the spanner table?
async insert(data) {
const result;
const params = {
id: data.id,
name: data.name,
petName: null
}
await data.hobby.map(async h => {
params.hobby = h;
const query = 'INSERT INTO my_table (id, name, petName, hobby) VALUES (#id, #name, #petName, #hobby)';
result = await db.RunTrans(query, params);
});
return result;
}
The above code doesn't insert the values into the table. Kindly help me with this..
Destructure out the wanted values (to avoid unnecessary repeated dot notation) then use template literals:
async insert(data) {
const result;
const params = {
id: data.id,
name: data.name,
petName: null
}
await data.hobby.map(async h => {
params.hobby = h;
const { id, name, petName, hobby } = params;
const query = `INSERT INTO my_table (id, name, petName, hobby) VALUES (${id}, ${name}, ${petName}, ${hobby})`;
result = await db.RunTrans(query, params);
});
return result;
}
You aren't handling any promise issues, so it is possible a few different things.
I am not certain your columns are nullable. If they aren't petName being NULL isn't valid for the schema.
#google-cloud/spanner doesn't have a RunTrans method. it has a runTransaction and a runTransactionAsync method though.
The way this is authored, the code would result in multiple rows with the same id. Typically id would be set to a primary key. Since I assume this isn't in error, I made a primary key that is the composite of hobby and id as this would be unique.
The documentation for runTransactionAsync is here:
https://cloud.google.com/nodejs/docs/reference/spanner/3.1.x/Database.html#runTransactionAsync
const {Spanner} = require('#google-cloud/spanner');
const spanner = new Spanner();
const instance = spanner.instance('testinstance');
const db = instance.database('people');
async function insert(data) {
const params = {
id: data.id,
name: data.name,
petName: null
}
const query = 'INSERT INTO my_table (id, name, petName, hobby) VALUES (#id, #name, #petName, #hobby)';
await Promise.all(data.hobby.map(async h => {
await db.runTransactionAsync(async (transaction) => {
params.hobby = h;
await transaction.run({
sql: query,
params: params,
types: {
petName: {
type: 'string'
},
}
});
await transaction.commit();
});
}));
};
insert({id: 0, name: 'foo', hobby:['a','b','c'], extra: 'blah'}).catch((r) => console.log(r))