Populating Github GraphQL in React (Gatsby) - reactjs

I am trying to create a page displaying my pinned repos. I am using gatsby and have installed the gatsby-source-github-api
{
resolve: 'gatsby-source-github-api',
options: {
token: 'xxxxxxxxx',
},
},
And I have the query populating the data I want in the GraphQL plaryground.
query {
user(login: "mrpbennett") {
pinnedItems(first: 6, types: [REPOSITORY]) {
edges {
node {
... on Repository {
name
description
url
primaryLanguage {
name
color
}
}
}
}
}
}
}
However I am struggling to get that data to populate into a new component I keep getting 7:13 error Cannot query field "user" on type "Query" graphql/template-strings
This is the component
I am not really to sure how to populate the data i need.
import React from 'react'
import { graphql, useStaticQuery } from 'gatsby'
const PinnedRepos = () => {
const data = useStaticQuery(graphql`
query {
user(login: "mrpbennett") {
pinnedItems(first: 6, types: [REPOSITORY]) {
edges {
node {
... on Repository {
name
description
url
primaryLanguage {
name
color
}
}
}
}
}
}
}
`)
return (
<div>
<p>{data.node.repository.name}</p>
</div>
)
}
export default PinnedRepos
any advice would be greatly appreciated​.

The syntax of your query is the problem here, you need to write the query like
const data = useStaticQuery(graphql`
query user(login: "mrpbennett") {
pinnedItems(first: 6, types: [REPOSITORY]) {
edges {
node {
... on Repository {
name
description
url
primaryLanguage {
name
color
}
}
}
}
}
}
`)

Related

How to get Apollo gql codgen to work with frontend nextJS

I am using nextJS, I want my front end react queries with apollo to be typed out, but no configuration is working. I get an unknown error on my query when I use import {gql} from src/__generated__/gql and the following message when I hover over gql():
The query argument is unknown! Please regenerate the types
My question is do I need to do something different because I am using nextJS? I want to be able to use TypeScript with Apollo Client code gendocs so my gql queries will be typed
My entire pothos schema is in my pages/api/index.ts file (I do not yet know how to spread this code out into multiple files)
Example Query:
const CREATED_EVENT_QUERY = gql(`
query EventById($id: mongoId!) {
eventById(id: $id) {
_id
name
description
location{
coordinates
}
date
eventApplicants{
name
userId
weight
}
link
weights{
weight
spotsAvailable{
name
userId
}
}
}
}
`);
// Apollo Query
const { loading, error, data } = useQuery(CREATED_EVENT_QUERY, {
variables: {
id: params.id
}
});
I have tried the following configurations:
Apollos recommendation
Link Above
import { CodegenConfig } from '#graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'http://localhost:3000/api',
documents: ['*.ts'],
generates: {
'./src/__generated__/': {
preset: 'client',
plugins: [],
presetConfig: {
gqlTagName: 'gql',
}
}
},
ignoreNoDocuments: true,
};
export default config;
the guilds nextJS recomendation
import type { CodegenConfig } from '#graphql-codegen/cli'
const config: CodegenConfig = {
// ...
generates: {
'path/to/file.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
config: {
reactApolloVersion: 3
}
}
}
}
export default config
combination of the two
import { CodegenConfig } from '#graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'http://localhost:3000/api',
documents: ['*.ts'],
generates: {
'pages/api/index.ts': {
plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
config: {
reactApolloVersion: 3
}
}
},
ignoreNoDocuments: true,
};
export default config;

Apollo client - add new item to list following mutation

I am working on a react app using "#apollo/client": "3.6.5". In the app a user can create payment requests that are displayed in a list.
I need to add a newly created payment request to the list of payment requests in the Apollo cache following the mutation to create one. The problem is the new payment request is not added to the cached list and Apollo does not output any error messages to explain why.
This is my version of the update function I've copied from the Apollo docs to try and update the cache.
const [onCreatePaymentRequest, createPaymentRequest] = useMutation<
CreatePaymentRequest,
CreatePaymentRequestVariables
>(CreatePaymentRequestMutation, {
update(cache, { data: { createPaymentRequest } }) {
cache.modify({
fields: {
paymentRequests(existingPaymentRequests = []) {
const newPaymentRequestRef = cache.writeFragment({
data: createPaymentRequest,
fragment: gql`
fragment NewPaymentRequest on PaymentRequest {
id
amount
status
}
`
});
return [...existingPaymentRequests, newPaymentRequestRef];
}
}
});
}
});
The mutation to create a new payment request:
export const CreatePaymentRequestMutation = gql`
mutation CreatePaymentRequest($input: CreatePaymentRequestInput!) {
createPaymentRequest(input: $input) {
paymentRequest {
id
amount
status
}
}
}
`;
This is query used to fetch payment requests:
export const GetAccountPaymentRequestsQuery = gql`
query GetAccountPaymentRequests(
$accountId: UUID!
$first: Int
$before: String
$last: Int
$after: String
) {
currentUser {
id
accountMembers(filter: { account: { id: $accountId } }) {
edges {
node {
account {
paymentRequests(
first: $first
last: $last
before: $before
after: $after
) {
edges {
node {
id
amount
status
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
}
}
}
`;
I think the problem may be that payment requests are nested within the schema. Using the useQuery hook I access the payment requests within a component using the following:
const paymentRequests = data.currentUser.accountMembers.edges[0].node.account.paymentRequests
.edges
I have tried numerous iterations of the update function to get this to work but so far no luck.
Thanks

Problems with GraphQL and TypeScript when I map data to components

I'm making an SSR with GraphQL and Next.js but I have a problem when I try to map some data.
I'm bringing some data from Github GraphQL API (Followers, Following, Public Repos) and when I pass this data to the code all my data works fine. But when I try to map my Showcase Repos to print on screen, the problems starts, anyone can help me?
My stack at this moment is GraphQL (GraphQl-request), Next.js, styled-components and TypeScript.
This is my query:
fragment totalFollowing on User {
following(first: 1) {
totalCount
}
}
fragment totalFollowers on User {
followers(first: 1) {
totalCount
}
}
fragment totalRepositories on User {
repositories(affiliations: OWNER, first: 1) {
totalCount
}
}
fragment showcase on User {
itemShowcase {
items(first: 10) {
nodes {
... on Repository {
name
description
url
}
}
}
}
}
query GET_GITHUB_QUERIES {
viewer {
...totalFollowing
...totalFollowers
...totalRepositories
...showcase
}
}
and this is my types at moment:
export type FollowingProps = {
totalCount: number
}
export type FollowersProps = {
totalCount: number
}
export type RepositoriesProps = {
totalCount: number
}
export type itemShowCaseProps = {
itemShowcase: {
items: {
nodes: [
{
name: string
description: string
url: string
}
]
}
}
}
export type QueryProps = {
following: FollowingProps
followers: FollowersProps
repositories: RepositoriesProps
itemShowcase: itemShowCaseProps
}
My call on index.tsx
export const getStaticProps: GetStaticProps = async () => {
const { viewer } = await client.request(query)
return {
props: {
...viewer
}
}
}
Still on my index.tsx, i have this component where i need to pass the data:
<S.MyWorkSection id="my-work" data-aos="fade-up">
<MyWorkGithub
repositories={repositories}
following={following}
followers={followers}
{...{ itemShowcase }}
/>
</S.MyWorkSection>
And finaly this is my component:
const MyWorkGithub: React.FC<QueryProps> = ({
following,
followers,
repositories,
itemShowcase
}) => (
<>
<Title text="Works" icon={true} />
<S.HighLightWrapper>
<HighLightCard
hlNumber={repositories.totalCount}
hlDescription={'repositories'}
/>
<HighLightCard
hlNumber={followers.totalCount}
hlDescription={'followers'}
/>
<HighLightCard
hlNumber={following.totalCount}
hlDescription={'following'}
/>
</S.HighLightWrapper>
<S.CardWrapper>
{itemShowcase.itemShowcase.items.nodes.map((repo, index) => (
<CardComponent
key={index}
url={repo.url}
name={repo.name}
description={repo.description}
/>
))}
</S.CardWrapper>
</>
)
this is my error:
TypeError: cannot read property 'items' of undefined
<SCardWrapper>
{itemShowcase.itemShowcase.items.node.map((repo, index) => (
...
https://i.imgur.com/LzOmb2z.png
Other datas like repositories, followers and following still works fine.
Edit: When I put on Github Explorer, all data is fine.

Why does react Apollo on update subscription returns the already updated value as prev?

I'm using apollo for a react project and with subscription on the creation, update and deletion of object. I use the subscribeToMore functionality to start the subscriptions. The prev value of the updateQuery callback is the correct value for the creation and deletion subscription. But for the update subscription the prev value already contains the updated value. Overall this is really nice, as I don't need to add my custom implementation on how to update the object and can just return prev, but I don't understand why this happens. From my understanding the previous value should be returned. Is this just backed in functionality of apollo or is this some weird bug?
Here is the component which implements the subscriptions:
import { useEffect } from 'react';
import { useQuery } from '#apollo/react-hooks';
import * as Entities from './entities';
import * as Queries from './queries';
interface IAppointmentData {
appointments: Entities.IAppointment[];
error: boolean;
loading: boolean;
}
function getAppointmentsFromData(data) {
return (data && data.appointments) || [];
}
export function useAllAppointments(): IAppointmentData {
const initialResult = useQuery(Queries.GET_APPOINTMENTS);
const { data, error, loading, subscribeToMore } = initialResult;
useEffect(() => {
const unsubscribeNewAppointments = subscribeToMore({
document: Queries.NEW_APPOINTMENTS_SUB,
variables: {},
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) {
return prev;
}
const { newAppointment } = subscriptionData.data;
return Object.assign({}, prev, {
appointments: [...prev.appointments, newAppointment],
});
},
});
const unsubscribeUpdateAppointment = subscribeToMore({
document: Queries.UPDATE_APPOINTMENTS_SUB,
variables: {},
updateQuery: (prev, { subscriptionData }) => {
return prev
},
});
const unsubscribeDeleteAppointments = subscribeToMore({
document: Queries.DELETE_APPOINTMENTS_SUB,
variables: {},
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) {
return prev;
}
const { deleteAppointment: {
id: deletedAppointmentId
} } = subscriptionData.data;
return Object.assign({}, prev, {
appointments: prev.appointments.filter(item => item.id !== deletedAppointmentId),
});
},
});
return function unsubscribe() {
unsubscribeNewAppointments()
unsubscribeDeleteAppointments()
unsubscribeUpdateAppointment()
}
}, [subscribeToMore]);
return {
appointments: getAppointmentsFromData(data),
error: !!error,
loading,
};
}
And these are my graphql queries / subscriptions:
import { gql } from 'apollo-boost';
export const GET_APPOINTMENTS = gql`
{
appointments {
id
responsibleCustomer {
id
user {
id
firstName
lastName
}
}
companions {
id
user {
id
firstName
lastName
}
}
time {
plannedStart
plannedEnd
}
type
}
}
`;
export const NEW_APPOINTMENTS_SUB = gql`
subscription newAppointment {
newAppointment {
id
responsibleCustomer {
id
user {
id
firstName
lastName
}
}
companions {
id
user {
id
firstName
lastName
}
}
time {
plannedStart
plannedEnd
}
type
}
}
`;
export const UPDATE_APPOINTMENTS_SUB = gql`
subscription updateAppointment {
updateAppointment {
id
responsibleCustomer {
id
user {
id
firstName
lastName
}
}
companions {
id
user {
id
firstName
lastName
}
}
time {
plannedStart
plannedEnd
}
type
}
}
`;
export const DELETE_APPOINTMENTS_SUB = gql`
subscription deleteAppointment {
deleteAppointment {
id
}
}
`;
According to the official Apollo documentation
Note that the updateQuery callback must return an object of the same
shape as the initial query data, otherwise the new data won't be
merged.
After playing around for a bit it seems that if the id you are returning is the same as in your cache and the object has the same shape, the update happens automatically.
But if you actually follow the CommentsPage example in the link I provided you will see that the id that is returned is new, that is why they explicity assign the object with the new data.
if (!subscriptionData.data) return prev;
const newFeedItem = subscriptionData.data.commentAdded;
return Object.assign({}, prev, {
entry: {
comments: [newFeedItem, ...prev.entry.comments]
}
});
Ive tested this with my own message app Im working on, when receiving new messages with new id I have to return the merged data myself. But when im updating the chatRooms to display which chatRoom should be at the top of my screen (which one has the newest message), then my update happens automatically and I just return prev.
If however you want a work around to explicitly check the data before updating it you could try this workaround I found on GitHub. You will just need to use a reverse lookup id or just a different variable other than id.
This is what I would do in your example to achieve this:
updateAppointment {
// was id
idUpdateAppointment // im sure updateAppointmentId should also work... i think
responsibleCustomer {
id
user {
id
firstName
lastName
}
}
}

Relay generating invalid query after using `setVariables`-- am I doing something wrong?

For some reason, if I generate a root query which takes in parameters before injecting the child component, like so:
import Relay from 'react-relay';
export default {
production: (Component) => Relay.QL`
query {
getProduction(id: $productionId) {
${Component.getFragment('production')}
}
}
`
};
Relay originally generates this query:
query MyProductionDetailsQuery($id_0:ID!,$where_1:ProductionRoleWhereArgs!) {
getProduction(id:$id_0) {
id,
...F0
}
}
fragment F0 on Production {
id,
...
_roles4oPiwv:roles(first:10,where:$where_1) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
}
}
variables:
{id_0: "UHJvZHVjdGlvbjoxNg==", where_1: {archived: {eq: true}}}
However, If the Component's relay container has variables of its own, running this.props.relay.setVariables({...variables}) completely changes the request query generated by relay into something like this:
query My_production_details_page_ProductionRelayQL($id_0:ID!,$where_1:ProductionRoleWhereArgs!) {
node(id:$id_0) {
...F0
}
}
fragment F0 on Production {
id,
_roles6J5gK:roles(first:10,where:$where_1) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
}
}
variables:
{id_0: "UHJvZHVjdGlvbjoxNg==", where_1: {archived: {eq: false}}}
However, setVariables works fine if I have a root query with no parameters:
import Relay from 'react-relay';
export default {
viewer: (Component, variables) => Relay.QL`
query {
viewer {
${Component.getFragment('viewer', { ...variables })}
}
}
`
};
Here's the generated query:
query ViewerQuery($where_0:ProductionWhereArgs!) {
viewer {
...F0
}
}
fragment F0 on Viewer {
user {
_productions2IPZAw:productions(first:10,where:$where_0) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}
}
variables:
{where_0: {expDate: {gt: "2016-11-04T16:29:11.677Z"}, archived: {eq: false}}}
After setVariables in the working setup:
query ViewerQuery($where_0:ProductionWhereArgs!) {
viewer {
...F0
}
}
fragment F0 on Viewer {
user {
_productions1CyNvL:productions(first:10,where:$where_0) {
edges {
node {
id,
...
},
cursor
},
pageInfo {
hasNextPage,
hasPreviousPage
}
},
id
}
}
variables:
{where_0: {expDate: {lt: "2016-11-04T16:34:12.537Z"}, archived: {eq: false}}}
versions:
"react-relay": "^0.9.3",
"react-router-relay": "^0.13.5"
I'm not sure if I'm doing something wrong with the configuration, or if it's just a bug on Relay's end.
Does anyone know what might be causing this issue?
When you run setVariables it leads to refetching only necessary data.
Relay looks which part of query could be affected by changing variable and requests from GraphQL server needed fragment.
It is possible because of Node interface(i.e. fetching object by opaque Relay id). See more in documentation.
I think, in your case you should implement Node Interface for Production type on GraphQL server.

Resources