Next.js Discord

Discord Forum

Fetch course content from server

Answered
Giant panda posted this in #help-forum
Open in Discord
Giant pandaOP
On my server, I have the following file structure (check screenshot at bottom). I want to fetch all files in the current course directory (using readdir from "fs/promises"). Then for each file (which represents a lesson), I want to require() its contents, which look something like this:

export default [
  {
    name: "The Internet",
    estimatedCompletionTimeInMinutes: 2,
    isProject: false,
    content: [
      <ContentBlock>
        <h1 className="content-heading">Hello</h1>
        <p className="content-text">
          abcdefghijklmnopqrstuvwxyz
        </p>
      </ContentBlock>,
      <ContentBlock>
        <p className="content-text">
          abcdefghijklmnopqrstuvwxyz
        </p>
      </ContentBlock>,
    ],
  },


I want to get the length of content so I can display the amount of chapters on the sidebar, but I want to only ship the actual content from the lesson in the URL (structure is as so: http://localhost:3000/courses/[course]/[lesson]/[chapter])

How do I do all of this on the server and then pass:
- The amount of chapters in each lesson
- The contents of the current lesson
to the client?

getServerSideProps doesn't work because react components cannot be serialized (<ContentBlock>)
Answered by Giant panda
I think at this point I'm just gonna have to represent the contents another way
View full answer

30 Replies

Giant pandaOP
Until now, I was doing something like this:
export default function useLessonData(): ChapterData[] | null {
  const router = useRouter();
  const [lessonData, setLessonData] = useState<ChapterData[] | null>(null);

  useEffect(() => {
    // Router queries are empty on first render, so we need to wait for them to populate
    if (!router.isReady) return;

    importLessonData(router, setLessonData);
  }, [router.query.course, router.query.lesson]);

  return lessonData;
}

async function importLessonData(router: NextRouter, setLessonData: Function) {
  try {
    await import(`@/private/course-data/${router.query.course}/${router.query.lesson}`).then((data) =>
      setLessonData(data.default),
    );
  } catch {
    router.push("/404");
  }
}


And then redirecting away in the main page component if the user is not subscribed:
import CourseView from "./CourseView";

export default function Page() {
  const subscription = true;

  return subscription ? <CourseView /> : <h1>Unauthorized</h1>;
}


But I want the first few lessons to be available, meaning that I need to display the CourseView component for the first few lessons, so I cannot fetch the amount of chapters for each lesson inside of it, as that would reveal the contents of the other lessons to the client.
@joulev Does it make more sense now?
Also I should've probably mentioned that this is what I need the amount of chapters for
As you can see, regardless of the lesson you are viewing, I need to know the amount of chapters in each lesson to display in the sidebar
I only need to know the lesson contents for the current lesson
Giant pandaOP
I think at this point I'm just gonna have to represent the contents another way
Answer
hi there, finally back from work, damn it
@Giant panda I think at this point I'm just gonna have to represent the contents another way
so yeah, you should representt the content another way
ideally, you fetch the list of chapters separately in a server component publicly available Nav component
@joulev so yeah, you should representt the content another way
Giant pandaOP
yeah so I figured that would not be feasible
but that shouldn't include the content of each chapter
Giant pandaOP
because I am doing a lot of inline stuff at the moment
but
instead, fetch only the content of each chapter in its own protected page
Giant pandaOP
I looked into how https://brilliant.org/ does it
and it looks like they compile the components to javascript
and then serve that
do you have any idea how this would work?
idk about their stack so can't say anything about that. but if you want to serialise components, you can look up packages that can do so, e.g. https://stackoverflow.com/q/35093814
Giant pandaOP
I looked into that a little bit as well. I'm actively trying to avoid it but perhaps it's the only way
I just don't know how reliable it'd be
oh yeah, for static react components it might be possible but for interactive it might not be so possible...
to protect your page you can also use middleware or something similar to redirect users away
just need to ensure that any routes where the user is not redirected, you absolutely do not under any circumstances import or fetch the protected data
Japanese anchovy
Yea, using the middleware would be the best bet to protect your routes. Then the pages can be completely static. You could then detect if a unauth/subscribed user is trying to access one of your course pages and redirect to a buy now screen
The chapter items can be client because they have to know if the user has already completed which would just be a simple fetch request but they would be client
or you could make it all SSR render. Depends on what your needs are. I think SSR would be easier in this case