transformResponse in typescript to sort a post result - redux-toolkit

I am trying to sort the response of a post with RTK query but I have this error
"Object is from type unknown"
transformResponse: res=>res.sort((a: { id: number; },b: { id: number; })=>b.id-a.id),
I am quite newbie with Redux Toolkit and with Typescript: How can I define the type of the result?
Thanks!
I send a post call and I would like to sort my result in desc order, so that I can see in the table of results the new posted article

You have to type the response. Try this:
transformResponse: (res: {id:number}[])=>res.sort((a: { id: number; },b: { id: number; })=>b.id-a.id),

Related

RTK Query correct way to map props with transformResponse

I'm working on linking up a scheduler component with data that I am receiving through an RTK Query api call. In order for the scheduler to work, I have to adjust the prop names to names the scheduler utilizes, before the state is available in the component (otherwise it does not load when I've tried in the React component). There is not a clear example of this being done in the RTK Query docs (https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#customizing-query-responses-with-transformresponse) , but it seems like it should be possible. When I've implemented it, the query gets fulfilled, but comes back as an empty object, or an array with a single empty object depending on how I've tried to structure it.
getUserScheduleVisits: builder.query({
query: userId => `/visitapi/visits/users/${userId}`,
providesTags: ['Visit', 'User'],
transformResponse: (response) => {
return {
data: [{
id: response._id,
startDate: response.visitStart,
endDate: response.visitEnd,
title: response?.client?.fullName
}]
}
},
}),
When I've run the query without the transform response, the data comes through as expected, however remains unusable by the Scheduler component I'm trying to implement. I'm still learning the complexities to RTK Query so I'm sure its how I'm trying to use transformResponse, I'm just not sure how to approach it correctly.
This would end up as result.data.data if you do const result = useMyQuery().
Did you maybe mean to do this?
getUserScheduleVisits: builder.query({
query: userId => `/visitapi/visits/users/${userId}`,
providesTags: ['Visit', 'User'],
transformResponse: (response) => {
return [{
id: response._id,
startDate: response.visitStart,
endDate: response.visitEnd,
title: response?.client?.fullName
}]
},
}),

how to resolve typescript issue with mongodb

I have an issue with this code
const doc = await req.db
.collection('clients')
.find({}, { lName: 1, fName: 1, id: 1 })
giving the typescript error
Type error: Argument of type '{ lName: number; fName: number; id: number; }' is not assignable to parameter of type 'FindOneOptions<any>'.
how can I fix this? thanks!
Solution
This should resolve it for you:
const doc = await req.db
.collection<Client>('clients')
.find({ lName: "", fName: "", id: 1 })
Explaination
The .find() method of Db takes one or two options, where the first option is the FilterQuery and the second is FindOneOptions.
find<T = TSchema>(query?: FilterQuery<TSchema>): Cursor<T>;
find<T = TSchema>(query: FilterQuery<TSchema>, options?: FindOneOptions<T extends TSchema ? TSchema : T>): Cursor<T>;
The options for the second argument are things like sort and limit, not the object to find. What you have in the second argument is the FilterQuery which needs to be the first argument.
The FilterQuery type is generic, so in order for typescript to know that we are filtering clients, we need to pass the generic when calling collection<Client>('clients'). That will cause it to return a collection of Client.
This is the interface I used, but I'm sure there are more properties
interface Client {
lName: string;
fName: string;
id: number;
}
Now, if you pass an invalid type to .find(), typescript knows and will throw and error.
.find({ lName: 1, fName: 1, id: 1 })
This is now an error because I said that fName and lName must be string.
Typescript Playground Link

How to get an array or list as result from a graphql query?

Hello I am new to graphql and I was wondering if it is possible to create a root query in my schema that will return an array or list as a result.
To be more precise I have created the following type in my schema:
const ResourceType = new GraphQLObjectType({
name:'Resource',
fields:()=>({
id: {type: GraphQLID},
url: {type: GraphQLString},
title: {type: GraphQLString},
author: {type: GraphQLString},
license:{type: GraphQLString},
LicenseScore: {type: GraphQLFloat},
})
});
And I want to query all the resources in my database so that I get their license type and then assign a score according to the license. Is it possible to have a query that gets the license for each resource and returns an array like this: [[id:1209,score:3.5],[1203,4.5]....]?
I have created a mutation that updates the score to the database, here it is:
updateAllLicenses: {
type: new GraphQLList(ResourceType),
async resolve(parent,args){
var licns;
var scr;
await Resource.find({}).then(async function(results){
var num = results.length;
var scr=0;
for (var i=0; i<num; i++){
if (! (results[i].license in licenseScores)){
scr = 0;
}
else{
scr = licenseScores[results[i].license];
}
await Resource.findByIdAndUpdate(results[i].id{LicenseScore:scr});
}
});
return await Resource.find({});
}
}
However I was wondering if there is a way to get these scores directly from the query instead of saving them in the database. I tried changing the type to new GraphQLList(GraphQLFloat) and returning an array with the scr values but I get an error in the graphql api saying I have the wrong output type in my mutation.
From your schema, it seems to me that you are able to fetch results, as an array of objects, with an output like this:
[
{
id: 1,
license: "value1"
},
{
id: 2,
license: "value2"
}
]
If this is all you need, then the approximate query should work:
query Resource {
id
url
license
licenseScore
}
Once you have your array of objects, you can convert it to an array of arrays, although I would not advise that.
Since you are just getting started in GraphQL, I recommened these resources:
HowToGraphql: clean videos/posts on GraphQL
Apollo Graphql's documentation and blogs. Here is one of the posts on queries
Also, if you want to test your queries and get accustomed to them, use GraphQL Playground

How do I use GraphQL with Mongoose and MongoDB without creating Mongoose models

Creating models in Mongoose is quite pointless since such models are already created with GraphQL and existing constructs (ie TypeScript interface).
How can we get GraphQL to use Mongoose's operations on models supplied from GraphQL without having to recreate models in Mongoose?
Also, it almost seems as if there should be a wrapper for GraphQL that just communicates with the database, avoiding having to write MyModel.findById etc
How does one do that?
Every example on the Internet that talks about GraphQL and Mongodb uses Mongoose.
You should look at GraphQL-to-MongoDB, or how I learned to stop worrying and love generated query APIs. It talks about a middleware package that leverages GraphQL's types to generate your GraphQL API and parses requests sent from clients into MongoDB queries. It more or less skips over Mongoose.
Disclaimer: this is my blog post.
The package generates GraphQL input types for your schema field args, and wraps around the resolve function to parse them into MongoDB queries.
Given a simple GraphQLType:
const PersonType = new GraphQLObjectType({
name: 'PersonType',
fields: () => ({
age: { type: GraphQLInt },
name: {
type: new GraphQLNonNull(new GraphQLObjectType({
name: 'NameType',
fields: () => ({
firstName: { type: GraphQLString },
lastName: { type: GraphQLString }
})
}))
}
})
});
For the most common use case, you'll build a field in the GraphQL schema with a getMongoDbQueryResolver and getGraphQLQueryArgs. The filter, projection, and options provided by the wrapper can be passed directly to the find function.
person: {
type: new GraphQLList(PersonType),
args: getGraphQLQueryArgs(PersonType),
resolve: getMongoDbQueryResolver(PersonType,
async (filter, projection, options, source, args, context) =>
await context.db.collection('person').find(filter, projection, options).toArray()
)
}
An example of a query you could send to such a field:
{
person (
filter: {
age: { GT: 18 },
name: {
firstName: { EQ: "John" }
}
},
sort: { age: DESC },
pagination: { limit: 50 }
) {
name {
lastName
}
age
}
}
There's also a wrapper and argument types generator for mutation fields.

Associated Data Not Returned From Apollo to Client?

Here's relevant Apollo code. (Many database fields have been omitted to make it faster/easier to read this post.)
CONNECTORS
const userAccountsDataModel = db.define('userAccountsData', {
id: {type: Sequelize.STRING, primaryKey: true},
picture_medium: {type: Sequelize.STRING}
});
const associatesDataModel = db.define('associatesData', {
_id: {type: Sequelize.STRING, primaryKey: true},
first_name: {type: Sequelize.STRING},
last_name: {type: Sequelize.STRING},
userID: {type: Sequelize.STRING},
});
associatesDataModel.belongsTo(userAccountsDataModel, {foreignKey: 'userID'});
SCHEMA
type userAccountsData {
id: String
picture_medium: String
}
type associatesData {
_id: String
first_name: String
last_name: String
userID: String
userAccountsData: [userAccountsData]
}
QUERY
const GETONEASSOCIATE_QUERY = gql`
query getOneAssociate($_id: String!) {
getOneAssociate(_id: $_id) {
_id
first_name
last_name
userID
accountsData{
id
picture_medium
}
}
} `;
RESOLVER
getOneAssociate(_, args) {
console.log('in getOneAssociate');
var ret = connectors.associatesData.findAll({
where: args,
include: [connectors.userAccountsData],
logging: console.log
}).then((res) => res.map((item) => item.dataValues));
return ret;
},
Via Sequelize, the resolver generates the following SQL:
SELECT "associatesData"."_id",
"associatesData"."first_name",
"associatesData"."last_name",
"associatesData"."userID",
"userAccountsDatum"."id" AS "userAccountsDatum.id",
"userAccountsDatum"."picture_medium" AS "userAccountsDatum.picture_medium"
FROM "associatesDatas" AS "associatesData"
LEFT OUTER JOIN "userAccountsData" AS "userAccountsDatum"
ON "associatesData"."userID" = "userAccountsDatum"."id"
WHERE "associatesData"."_id" = '35';
When I run the above SQL in my SQL client, I can see that all the data sought is being returned, including the data from the related "userAccountsData" table.
However, when the data gets to my client code running in javascript the browser, the data from the related table is missing-- the object that should contain it is null.
What am I missing?
Thanks in advance to all for any info!
This turned out to be an anomaly with Sequelize's automatic pluralizing of table names.
By default, Sequelize automatically pluralizes table names. It is supposed to be possible to turn this behavior off by using Sequelize's freezeTableName property. However, per this Sequelize issue post, there are cases in which Sequelize ignores the freezeTableName property and pluralizes (and sometimes singularizes) the table names anyway.
After discovering this I was able to resolve the anomaly by pluralizing the relevant table name and going through all my Apollo code to update the references to this table so that they would be pluralized as well.