godon019
2/20/2019 - 2:17 PM

GraphQL (need to add others here)

GraphQL

Query with filter

Frontend

import React from 'react'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'

export const RITES_QUERY = gql`
query RitesQuery($personName: String){
  rites(personName: $personName){
    createdAt
    title
    amount
    quantity
    deposits{
      amount
      comment
    }
  }
}
`
const VARIABLES = { personName: "김명자" }

class QueryControl extends React.Component {
  render() {
    const { children, query } = this.props;
    return (
      <Query query={query} variables={VARIABLES}>
        {({ loading, error, data }) => {
          if (loading) return <div>Fetching</div>
          if (error) return <div>Error</div>
          return (
            <React.Fragment>
              {children(data)}
            </React.Fragment>
          )
        }}
      </Query>
    )
  }
}

export default QueryControl

Backend

Query.ts

  async rites(parent, args, ctx: Context) {
    const { personName } = args;

    if (personName) {

      const personExists = await ctx.prisma.$exists.person({
        name: personName,
      })
      if (!personExists) {
        throw new Error(`No person exists with name: ${personName}`)
      }

      const rites = await ctx.prisma.rites({
        where: {
          person: {
            name: personName,
          }
        }
      })
      if (rites.length === 0) {
        throw new Error(`Empty rites`)
      }
      return rites
    }

    return ctx.prisma.rites({ ...args })
  }

schema.graphql

type Query {
  rites(personName: String): [Rite!]!
}

Polling and Refetch

Queries | Apollo Client

Polling

 pollInterval={500}

Polling is an excellent way to achieve near-realtime data without the complexity of setting up GraphQL subscriptions.

fetchPolicy

React Apollo | Apollo Client

<Query
   query={RITES_QUERY}
   variables={{ personName: selectedPerson.name }}
   fetchPolicy={'cache-and-network'}
 >

Good query practice

<Query
  query={RITES_QUERY}
  variables={{ personName: selectedPerson.name }}
  fetchPolicy={'cache-and-network'}
>
  {({ loading, error, data, refetch, networkStatus }) => {
    // if (loading) return <div>Fetching</div>
    if (error) return <div>Error {JSON.stringify(error)}</div>
    const { rites } = data;
    return (<div>
      {loading && <div>Fetching</div>}
      {rites && rites.map((rite, index) => (
        <React.Fragment key={index}>
          <RiteTable rite={rite} />
          <div style={{ height: "20px" }} />
        </React.Fragment>
      ))}
    </div>)
  }}
</Query>

Explain

  1. use fetchPolicy={'cache-and-network'} show cached one first and then show fetched data later
  2. instead of using loading props as returing <div>Fetching</div> right away use loading inside of main return statement where I can show the cached data while showing fetching status too!

Caveat

returing error from backend service like below

const rites = await ctx.prisma.rites({
  where: {
    person: {
      name: personName,
    }
  }
})
if (rites.length === 0) {
  throw new Error(`Empty rites by the name of ${personName} time: ${Date.now()}`)
}

makes a problem with caching for <Query/> component in frontend so it is better just returning without Error when it is like minor error stuation such as emtpy result and deal with it in frontend to show the proper result

Interface

ref

declare

import { Context } from '../utils'

export const Reportable = {
  __resolveType: (reportable, ctx: Context, info) => {
    const { category } = reportable

    if(category === "불사금"){
      return 'Bulsageum'
    }

    if(category === "지출"){
      return 'Expense'
    }

    if(category === "보시금"){
      return 'Bosigeum'
    }

    return null

  },
}

in query

import { Context } from '../../utils'

export const reportables = async (parent, args, ctx: Context) => {
  const expenses = await ctx.prisma.expenses()
  const bulsageums = await ctx.prisma.bulsageums()
  const bosigeums = await ctx.prisma.bosigeums()
  let merged = [...expenses, ...bulsageums, ...bosigeums]
  merged.sort((a, b) => {
    return Date.parse(b.createdAt) - Date.parse(a.createdAt)
  })
  return merged
}