Next JS & Calling Internal API


Uncategorized

Updated Dec 31st, 2021

In NextJS, when calling an internal API to receive data, we can use “getStaticProps” or an http request (also getServerSideProps although I’ll set this aside for now).

I am interested in the differences and when it makes sense to use one versus the other.

In the context of a “quiz application” in which a question object has the correct answer.

The “getStaticPaths” seems like the better option since the API is on the same server we can save the http request. This puts the question data, including the answer, in the source code on page load. This also may be good for SEO.

The client side request keeps the data out of the source code, but it is still accessible via the dev tools albeit a little harder to find, (react dev tools in state. Not sure if this is the only place to view).

I’m at a crossroads as to which to implement. After going with GSP option, I noticed clicking the button doesn’t re-run the GSP code and so I don’t get new questions until I reload the page. This is not what I want. I checked GSSP and it looks like this does the same thing. Could read this post which shows how to reload GSprops without a full page load.

The more important question is how can you prevent the answer being in the source code without have to send an HTTP request to the DB to check each answer.

Here is the code to implement each:

getStaticProps

 // server side code runs when the page is pre-rendered
  // make the code in api route available in an export so it can be
  // import these functions and run directly here.
  // dont send a response data code
  // about producing the data for the page component

import type { NextPage } from "next"
import React, { useState } from "react"
import { shuffleArray } from "../utils"
import { connectToDatabase, getQuestions } from "../lib/db"
export type AnswerObject = {
  question: string
  answer: string
  correct: boolean
  correctAnswer: string
}
export type Question = {
  category: string
  correct_answer: string
  difficulty: string
  incorrect_answers: string[]
  question: string
  type: string
}
export type QuestionsState = Question & {
  answers: string[]
}
const Home: NextPage = (props: any) => {
  const [questions, setQuestions] = useState<QuestionsState[]>([])
  
  const startTrivia = () => {
      setQuestions(props.newQuestions)
  
  }
  return (
    <>
      <h3>Questions From GSSP</h3>
       <ul>
        {props.newQuestions.map((question: any) => {
          return <li key={question.index}>{question.question}</li>
        })}
      </ul>
    </>
  )
}
export async function getServerSideProps() {
  const client = await connectToDatabase()
  const unshuffledData = await getQuestions(client)
  const shuffledData = unshuffledData.map((question: Question) => {
    return { ...question, answers: shuffleArray([...question.incorrect_answers, question.correct_answer]) }
  })
  const data = shuffledData
  return {
    props: {
      newQuestions: data
    }
  }
}
export default Home

Traditional Fetch Request

// reach out to db for question data on the server?
// the api runs on the same server
// not doing it this way creates unnecessary http request
// not doing it this way keeps the qs from the source
// how do we handle the loading state/time
// getStaticProps outside of the component to call internal API
// getStaticProps is about getting the data needed for the Home comp
// alernative to using fetch/axios inside
// could do fetch("/api/questions")
// do not talk to db from front-end of application
// send props to this Home component
// the questions data, including the answer,
// will still end up on the client, viewable in source code
// maybe better for SEO
import type { NextPage } from "next"
import React, { useState } from "react"
const Home: NextPage = () => {
  const [fetchedQs, setFetchedQs] = useState([])
  const extractQuestions = () => {
    fetch("/api/questions")
      .then(response => response.json())
      .then(data => setFetchedQs(data))
  }
  return (
    <>
      
      <h3>Questions from Fetch</h3>
      <button onClick={extractQuestions}>Fetch Questions</button>
      {fetchedQs.map((item: any) => {
        return <li key={item.index}>{item.question}</li>
      })}
    </>
  )
}
}

A Note About useEffect

In the original quiz app the request is not in a useEffect. I believe this results is unnecessary re-rendering of requests. The author even discussed this briefly an and says it’s a non issue. This happens because every time we update state the component renders again and fires out request again which updates our state again and creates a loop.

But does putting the call in a dependency array solve this? I believe I’ve seen a “sendCount” piece of state created and used for the dependency (I think this was just for not sending http request inside a reducer function since an http request is considered a side effect. This is still an interesting pattern).

Source: Max Next JS course, React Refresher. Sending a Request Chapter. 8 min mark.

Source: RFTROU course Edit Post continued chapter.