DEV Community

Cover image for My Experience with GraphQl Relay Vite and Github APi
Dennis kinuthia
Dennis kinuthia

Posted on

My Experience with GraphQl Relay Vite and Github APi

relay

Relay is a JavaScript framework for fetching and managing GraphQL data in React applications that emphasizes maintainability, type safety and runtime performance



i'd simply summarize it as very opinionated

full code

ideal use cases for relay

  • Nested compnenets with multiple layers of pagination

    this is the main reason i picked it up and i wish i gave it a chance sooner

            {!fragData.isLoadingNext &&
      fragData.hasNext ? (
        <button
          className="m-2 hover:text-purple-400 shadow-lg hover:shadow-purple"
          onClick={() => {
            fragData.loadNext(5);
          }}
        >
          --- load more ---
        </button>
      ) : null}
    

    with only that snippet it'll fetch the next 5 in the list and automatically mereg them to the previous data , if you've worked with apollo-client/urql/react-query you'll appreciate this simplicity

    • ### complex application which relies on big queries composing a framents for every component , then letting their compiler stich them together into the most optimum query is magic

Downsides

  • the setup process is tedious and the cmmunity documentatin is really scarce
  • it doesn't have loading states and error states directly in the query results and you'll have to wrap compnents in Suspense and ErrorBoundary
  const res = await response.json()
  if(!response.ok){
    throw new Error(res.message,res)
  }

  // Get the response as JSON
  return res;
Enter fullscreen mode Exit fullscreen mode

i also added that snippet to throw an error if the status wasn't ok becasuse errors from this specific graphql api were embedded as the response which would cause relay client to error out withut knowing what specific error was returned

  • the documentation can compund your confusion
  • the prefetching feature in usePreloadedQuery which is recomemnded fetch strategy is not supprted in react-router but this can be side sepped by just using uselazyLadedQuery instead
  • requires the server to have a compliant schema in my case am using the github graphql api , but if you had your own server yu culd use something like Hasura or just follw the spec specified when making your own > you can transform your response before it's passed into the provider if you still want to use it with our current data like shown in this tutorial

usage

  • npm install
  • add .env file with your github personal access token
VITE_TOKEN = your_personall_access_token
Enter fullscreen mode Exit fullscreen mode
  • npm run dev > this will only run the compiler once , my attempts to run the compiler wit the --watch flag were futile so i run npm run relay in another terminal everytime i change the query

brief explanation

my obective with this was to createa a github repository dash board for my github API project , live preview

one of the things i wanted was to list all the branches and list all the cmmits under each branch with pagination on both layers ,
paginnatating over the first layer worked with react query but the pproblems kept piling and the experience got more inconsistent when trying to do nesetd pagination , apollo wasn't great at it too , Urql was the worst sinceit's pagination strategy was swapping out variables to trigger the re-rener and in all of the above you'd have to merger arrays that are nested almost 3 levels deep that was very inefficient , didn't workas expected so i switched to relay and it fixed that issue remarhably well

Note: in cases like these i'd usually break the query down and execute thechild query in the cild component but in this case this was the query:

 export const REPOREFS = gql`
         # github graphql query to get more details
         query getRepoRefs(
           $repoowner: String!
           $reponame: String!
           $first: Int
           $after: String
           $firstcommits: Int
           $$aftercommits: String
         ) {
           repository(owner: $repoowner, name: $reponame) {
             nameWithOwner

             #refs: get branches and all the recent commits to it

             refs(
               refPrefix: "refs/heads/"
               orderBy: { direction: DESC, field: TAG_COMMIT_DATE }
               first: $first
               after: $after
             ) {
               edges {
                 node {
                   name
                   id
                   target {
                     ... on Commit {
                       history(first: $firstcommits, after: $aftercommits) {
                         edges {
                           node {
                             committedDate
                             author {
                               name
                               email
                             }
                             message
                             url
                             pushedDate
                             authoredDate
                             committedDate
                           }
                         }
                       }
                     }
                   }
                 }
               }
               pageInfo {
                 endCursor
                 hasNextPage
                 hasPreviousPage
                 startCursor
               }
               totalCount
             }

             # end of refs block
           }
         }
       `;
Enter fullscreen mode Exit fullscreen mode

there was no varaible that i coud pass into the child compnent in order for it to fetch this query in order to query the commits in a child component

the relay alternative looks like this
onerepo.tsx

export const FULLREPO = graphql`

  query onerepoFullRepoQuery(
    $repoowner: String!,
    $reponame: String!,
    ) {
    repository(owner: $repoowner, name: $reponame) {
    nameWithOwner,
    forkCount,
    ...Stars_stargazers
    ...Branches_refs
    # stargazers(after:$after,first:$first)  @connection(key: "Stars_stargazers"){
    #   ...Stars_stars
    # }
   }
  }
`;
Enter fullscreen mode Exit fullscreen mode

and then every child component only defines a fragement of the main query
branches.tsx

export const Branchesfragment = graphql`
  fragment Branches_refs on Repository
  @argumentDefinitions(
    first: { type: "Int", defaultValue: 3 }
    after: { type: "String" }
  )
  @refetchable(
    queryName: "BranchesPaginationQuery"
  ) {
    refs(
      refPrefix: "refs/heads/"
      orderBy: {
        direction: DESC
        field: TAG_COMMIT_DATE
      }
      first: $first
      after: $after
    ) @connection(key: "Branches_refs",) {
      edges {
        node {
          name
          id
          target {
            ...Commits_history
          }
        }
      }
    }
  }
`;

Enter fullscreen mode Exit fullscreen mode

Commits.tsx

export const CommitsOnBranchFragment = graphql`
  fragment Commits_history on Commit
  @argumentDefinitions(
    first: { type: "Int", defaultValue: 5 }
    after: { type: "String" }
  )
  @refetchable(
    queryName: "CommitsPaginationQuery"
  ) {
    history(first: $first, after: $after)
       @connection(key: "Commits_history") {
      edges {
        node {
          committedDate
          author {
            name
            email
          }
          message
          url
          pushedDate
          authoredDate
          committedDate
        }
      }
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
        startCursor
      }
      totalCount
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

giving the child compnents relative autonomy and isolating changes and re-renders to the corresponding components

lastly , it (relay-compiler) generates types for you , they have a weird ting with flow in all their examples but it'll gererate typescrit types if you define it in the compiler options in the package.json

  "relay": {
    "src": "./src",
    "schema": "schema.docs.graphql",
    "language": "typescript",
    "eagerEsModules": true,
    "exclude": [
      "**/node_modules/**",
      "**/__mocks__/**",
      "**/__generated__/**"
    ]
  }
Enter fullscreen mode Exit fullscreen mode

full code

the article link

helpfull references

relay pagination tutorial

another helpfull article

brief video explanation

graphql client playground

to configure for github graphql api just add your personal access token in the headers and cchange the endpoint

gql playground by hasura

and test out queries , hit ctrl spacebar to get autocomplete
and field suggstion.

this package and the compiler rely on babel , i sued vite react fro this projectandit doesn't use babel as the build tool but there's a plugin that helps with that

Also just in case you're building something from the ground up consider a different router

--- The End ---

Top comments (2)

Collapse
 
mickdelaney profile image
mick delaney

Prefetching is supported in React Router now.

reactrouter.com/en/main/route/loader

Collapse
 
tigawanna profile image
Dennis kinuthia

I actually switched to react-router , it works better than the react location equivalent