javascriptreactjsnext.jsapollo

Nextjs - access url params on server side


I am using the official with-apollo example to create a nextjs frontend. I am trying to use the user's slug, which can be found in the url string to render the user profile. However, I am not able to use the url parameter (the slug) as a variable in the graphql query.

The Link to the user profile

<Link href={{ pathname: "/users/[slug]", query: { slug: user.slug } }}>

The user profile component

import { gql, useQuery } from "@apollo/client"
import ErrorMessage from "./ErrorMessage"
import { useRouter } from "next/router";

export const USER_QUERY = gql`
  query getUser($slug: String!) {
    user(slug: $slug) {
      id
      email
    }
  }
`

// I can not get this to work using url parameters
export const userQueryVars = {
  slug: "userSlug", // This should be a url parameter!!
}

export default function UserProfile() {
    const router = useRouter()
    const userSlug = router.query.slug

  const { loading, error, data } = useQuery(USER_QUERY, {
    variables: {slug: userSlug},
  })

  if (error) return <ErrorMessage message="Error loading users." />
  if (loading) return <div>Loading</div>
  if (!data) return <div>No data</div>

  const { user } = data

  return (
    <section>
      <div>
        <h3>
          {user.firstName} {user.lastName}
        </h3>
        <p>{user.email}</p>
      </div>
    </section>
  )
}

The user profile page

import App from "../../../components/App"
import Header from "../../../components/Header"
import UserProfile, {
  USER_QUERY,
  userQueryVars,
} from "../../../components/UserProfile"
import { initializeApollo, addApolloState } from "../../../lib/apolloClient"

const UserProfilePage = () => (
  <App>
    <Header />
    <UserProfile />
  </App>
)

export async function getServerSideProps() {
  const apolloClient = initializeApollo()

  await apolloClient.query({
    query: USER_QUERY,
    variables: userQueryVars, // This is passed from the component!
  })

  return addApolloState(apolloClient, {
    props: {}
  })
}

export default UserProfilePage

What I have tried so far (among a lot of other things):

  1. Using router:

    export const userQueryVars = { slug: router.query.slug, }

Error: You should only use "next/router" inside the client side of your app.

  1. Using router and checking that is it called on client side:

    if (process.browser) { export const userQueryVars = { slug: router.query.slug, } }

Error: 'import' and 'export' may only appear at the top level.

I would be very thankful for any kind of help!!


Solution

  • When using getServerSideProps you can find your slug (and all other dynamic params if you have them) inside context.params:

    export async function getServerSideProps(context) {
      const  { slug } = context.params;
    
      // Do whatever you need with `slug`
      // ...
    }
    

    If you use TypeScript you can follow this example from the docs to add types:

    import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
     
    type Repo = {
      name: string
      stargazers_count: number
    }
     
    export const getServerSideProps: GetServerSideProps<{
      repo: Repo
    }> = async () => {
      const res = await fetch('https://api.github.com/repos/vercel/next.js')
      const repo = await res.json()
      return { props: { repo } }
    }
     
    export default function Page({
      repo,
    }: InferGetServerSidePropsType<typeof getServerSideProps>) {
      return repo.stargazers_count
    }