How to use take() options with relations? - node.js

I don't know why take() options not get correct number as I expect.
Example ,I type take:2 but it just get one product, but when I remove relations it work normal.It is so strange with me,Am I missing something?
My code
async getProducts(#Arg("searchOptions") searchOptions : SearchOptionsInput): Promise<ProductResponse> {
try {
const {skip,type} = searchOptions
const findOptions: { [key: string]: any } = {
skip,
take: 2,
relations: ["prices"],
};
switch (type) {
case "PRICE_DESC":
findOptions.order = {
prices: {
price: "DESC"
}
}
break;
default:
findOptions.order = {
createdAt:"DESC"
}
break;
}
const products = await Product.find(findOptions);
return {
code: 200,
success: true,
products,
};
} catch (error) {
return {
code: 500,
success: false,
message: `Server error:${error.message}`,
};
}
}
My query
query GetProducts{
getProducts(searchOptions:{
skip:0,
type:"PRICE_DESC"
}){
code
success
products{
id
prices{
price
}
}
}
}

Related

Error with sending an arrays and an object in a GET API and getting them in the reducer

I'm working on a project where I need to send an arrays and an object from the backend (nodejs) through a GET api to the frontend (reactjs) and have both of those be accessible in my reducer. I have never done this, and I'm not sure if I'm going about it the right way. I am currently getting an error saying that totalPages from this line: export const orderMineListReducer = (state = {orders:[], totalPages}, action) => { is not defined. I would really appreciate any help or advice on how to go about sending a GET api with an arrays and an object and receiving an arrays and an object in the reducer. Thank you!
Below, I have included what I have tried to do so far:
Backend:
orderRouter.js
orderRouter.get(
'/mine',
isAuth,
expressAsyncHandler(async (req, res) => {
const page = req.query.page || 1;
const perPage = 20
const orders = await Order.find({ user: req.user._id }).skip(page * perPage).limit(perPage);
const total = await Order.countDocuments();
const totalPages = Math.ceil(total / perPage).toString();
res.status(200).send({
data:
[orders],
totalPages,
});
}),
);
Frontend
orderReducer.js
export const orderMineListReducer = (state = {orders:[], totalPages}, action) => {
switch (action.type) {
case ORDER_MINE_LIST_REQUEST:
return { ...state, loading: true };
case ORDER_MINE_LIST_SUCCESS:
return { ...state, loading: false, orders: action.payload.orders, totalPages: action.payload.totalPages,};
case ORDER_MINE_LIST_FAIL:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
orderActions.js
export const listOrderMine = (page) => async (dispatch, getState) => {
dispatch({ type: ORDER_MINE_LIST_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get(`${BASE_URL}/api/orders/mine?page=${page}`, {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
});
dispatch({ type: ORDER_MINE_LIST_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: ORDER_MINE_LIST_FAIL, payload: message });
}
};
I've also tried just having
res.status(200).send({
orders,
totalPages,
});
instead of res.status(200).send({data: { orders, totalPages,}});
with my reducer like so:
export const orderMineListReducer = (state = { data: {} }, action) => {
switch (action.type) {
case ORDER_MINE_LIST_REQUEST:
return { ...state, loading: true };
case ORDER_MINE_LIST_SUCCESS:
return { ...state, loading: false, data: action.payload,};
case ORDER_MINE_LIST_FAIL:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
however in my OrderHistoryScreen.js where I have
const orderMineList = useSelector((state) => state.orderMineList);
const { loading, data, error,} = orderMineList;
const dispatch = useDispatch();
useEffect(() => { dispatch(listOrderMine());
}, [dispatch]);
I am getting undefined for console.log(data.orders) and empty {} for console.log(data).
Your response has this scheme:
{
data: {
orders,
totalPages
}
}
Axios.get will resolve to an object with this schema:
{
data: {
data: {
orders,
totalPages
}
},
status: 200,
statusText: 'OK',
...
}
So you need to change the destructuring or dispatch data.data like this:
dispatch({ type: ORDER_MINE_LIST_SUCCESS, payload: data.data });
Check the Axios documentation on the response schema: https://axios-http.com/docs/res_schema

Apollo GraphQL resolvers how to pass arguments to query child type

I have a Query that takes an argument with child type which also takes an argument. I would like to pass arguments on both the query and the query child type. I need help on how to implement this logic.
When I hard code the "after" variable the app works fine. How do I implement the resolver to get the after variable from the front-end and then pass is to playerInFoAPI in the dataSources?
SCHEMA
const { gql } = require("apollo-server-express");
const typeDefs = gql`
scalar Date
type Query {
text: String!
club(slug: String!): Club!
}
type Club {
id: ID!
name: String!
pictureSecondaryUrl: String
domesticLeague: DomesticLeague
players(first: Int, after: String): PlayerConnection!
}
type PlayerConnection {
edges: [playerEdge!]!
nodes: [Player!]!
pageInfo: PageInfo!
}
type PageInfo {
endCursor: String
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
}
type Player {
id: ID!
displayName: String!
slug: String!
age: Int!
birthDate: Date
position: String!
country: Country!
subscriptionsCount: Int!
pictureUrl: String
shirtNumber: Int
status: PlayerStatus!
activeClub: Club
allSo5Scores: So5ScoreConnection!
}
type playerEdge {
cursor: String!
node: Player
}
type Country {
code: String!
}
type PlayerStatus {
id: ID!
lastFifteenSo5Appearances: Int
lastFifteenSo5AverageScore: Float
lastFiveSo5Appearances: Int
lastFiveSo5AverageScore: Float
playingStatus: String
}
type So5ScoreConnection {
nodes: [So5Score!]!
}
type So5Score {
score: Float
}
type DomesticLeague {
id: ID!
displayName: String!
}
`;
module.exports = typeDefs;
GRAPHQL DATA SOURCE WITH QUERY
const { GraphQLDataSource } = require("apollo-datasource-graphql");
const { gql } = require("apollo-server-express");
const PLAYER_INFO = gql`
query PLAYER_INFO($slug: String!, $after: String) {
club(slug: $slug) {
players(first: 2, after: $after) {
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
edges {
# start node
node {
id
displayName
slug
age
birthDate
position
country {
slug
code
}
subscriptionsCount
pictureUrl
shirtNumber
activeClub {
id
name
pictureSecondaryUrl
domesticLeague {
id
displayName
}
}
status {
id
lastFifteenSo5Appearances
lastFifteenSo5AverageScore
lastFiveSo5Appearances
lastFiveSo5AverageScore
playingStatus
}
allSo5Scores {
nodes {
score
}
}
} #end node
}
}
}
}
`;
class PlayerInfoAPI extends GraphQLDataSource {
constructor() {
super();
this.baseURL = "https://api.sorare.com/graphql/";
}
async getPlayerInfo(slug,after) {
try {
const response = await this.query(PLAYER_INFO, {
variables: {
slug,
after
},
});
return this.playerInfoReducer(response.data.club.players);
} catch (err) {
console.log(err);
throw new Error(err.message);
}
}
playerInfoReducer(data) {
return {
players: {
pageInfo: {
endCursor: data.pageInfo.endCursor,
startCursor: data.pageInfo.startCursor,
hasNextPage: data.pageInfo.hasNextPage,
hasPreviousPage: data.pageInfo.hasPreviousPage,
},
},
};
}
}
module.exports = PlayerInfoAPI;
RESOLVER
const dateScalar = require("../Utils/CustomDate");
const resolvers = {
Date: dateScalar,
Query: {
text: () => "Hello There!",
club: (_, { slug }, { dataSources }) =>
dataSources.playerInfoAPI.getPlayerInfo(slug),
},
// Club: {
// players(_, { after }, { dataSources }) {
// return dataSources.playerInfoAPI.getPlayerInfo(after);
// },
// },
};
module.exports = resolvers;
FRONT END WITH FETCHMORE FUNCTION
const SLUG = "slug-example";
const PlayerListTable = () => {
const { data, loading, error, networkStatus, fetchMore } = useQuery(
PLAYERS_INFO,
{
variables: { slug: SLUG, after: null },
notifyOnNetworkStatusChange: true,
}
);
const onLoadMore = () => {
//destructure end cursor
const { endCursor } = data.club.players.pageInfo;
console.log(endCursor);
fetchMore({
variables: {
after: endCursor,
},
updateQuery: (prevResult, { fetchMoreResult }) => {
console.log(fetchMoreResult);
},
});
};
You cannot simply forward args to child resolver as this would collide with child args if any. Also the API does not reveal args passed to parent from within a child resolver. Remember that first argument of child resolver will always be whatever is returned from parent resolver. This lets you can pass data from parent to child. This is by design to achieve separation of concerns.

Typeorm find options with order and where

I would like to order this find function through the table relation.
const [people, total] = await typePersonServiceInstance.find(
{
take,
skip,
where: (qb: any) => {
qb.where('person.type IN (:...type)', { type });
qb.andWhere('person.status IN (:...status)', { status });
if (query.search) {
qb.andWhere(new Brackets((subQb) => {
subQb.where('name like :name', { name: `%${query.search}%` });
subQb.orWhere('fantasyName like :fantasyName', { fantasyName: `%${query.search}%` });
subQb.orWhere('person.city like :city', { city: `%${query.search}%` });
subQb.orWhere('person.state like :state', { state: `%${query.search}%` });
subQb.orWhere('person.id = :id', { id: query.search });
}));
}
},
order: {
person: {
status: 'ASC'
}
}
},
);
The issue i'm facing is when trying to order by some attribute from person table, if I do
order: {
anyColumnFromTypePersonHere: 'ASC' | 'DESC'
}
It works pretty fine, but if I want to order by status (that is an attribute from person) it will not work
Just add this line:
qb.addOrderBy('person.status', "ASC") ;

Redux display error messages from Nodejs backend

I want to display the error messages I am receiving from my backend in an alert, whenever my login failes:
My login-button triggers this function from my user.actions:
function login(username, password) {
return dispatch => {
dispatch(request({ username }));
userService.login(username, password)
.then(
user => {
dispatch(success(user));
history.goBack();
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
);
};
function request(user) { return { type: userConstants.LOGIN_REQUEST, user } }
function success(user) { return { type: userConstants.LOGIN_SUCCESS, user } }
function failure(error) { return { type: userConstants.LOGIN_FAILURE, error } }
}
My alert.reducer looks as following:
import { alertConstants } from '../_constants';
export function alert(state = {}, action) {
switch (action.type) {
case alertConstants.SUCCESS:
return {
type: 'alert-success',
message: action.message
};
case alertConstants.ERROR:
return {
type: 'alert-danger',
message: action.message
};
case alertConstants.CLEAR:
return {};
default:
return state
}
}
In my App.js I receive this state with mapStateToProps:
function mapStateToProps(state) {
const { alert } = state;
return {
alert
};
}
After that, I want to display an alert with the alert message:
{alert.message &&
alert(alert.message)
}
Can you help me with that?
your action/reducer code looks ok, I think maybe it is a conflict with the props name and the native alert function.
Have you tried to change the props names? Something like:
function mapStateToProps(state) {
const { alert } = state;
return {
alertState: alert
};
}
and
{ alertState.message && alert(alertState.message) }

How to receive an array as member of an input parameter of a GraphQL service?

Given this schema:
input TodoInput {
id: String
title: String
}
input SaveInput {
nodes: [TodoInput]
}
type SavePayload {
message: String!
}
type Mutation {
save(input: SaveInput): SavePayload
}
Given this resolver:
type TodoInput = {
id: string | null,
title: string
}
type SaveInput = {
nodes: TodoInput[];
}
type SavePayload = {
message: string;
}
export const resolver = {
save: (input: SaveInput): SavePayload => {
input.nodes.forEach(todo => api.saveTodo(todo as Todo));
return { message : 'success' };
}
}
When I sent this request:
mutation {
save(input: {
nodes: [
{id: "1", title: "Todo 1"}
]
}) {
message
}
}
Then the value for input.nodes is undefined on the server side.
Does anybody knows what am I doing wrong?
Useful info:
The mutation works properly with scalar values (such as String as input and return)
I'm using typescript, express and express-graphql.
You need to make changes in the key in the resolver,
export const resolver = {
save: (args: {input: SaveInput}): SavePayload => {
args.input.nodes.forEach(todo => api.saveTodo(todo as Todo));
return { message : 'success' };
}
}

Resources