📊
Next.js + ApolloでSSRするときにパフォーマンスが低下する問題について
Next.js + Apollo
Next.js の公式リポジトリには Apollo を使用する際のサンプルコードがあるが、
これらを参考に SSR するとかなりパフォーマンスが悪い。
with-apollo
with-typescript-graphql
例えば、with-typescript-graphql の/pages/index.tsx
を見てみる。
...
import {
useViewerQuery,
useUpdateNameMutation,
ViewerDocument,
} from '../lib/viewer.graphql'
import { initializeApollo } from '../lib/apollo'
const Index = () => {
const { viewer } = useViewerQuery().data!
...
return (
...
)
}
export async function getStaticProps() {
const apolloClient = initializeApollo()
await apolloClient.query({
query: ViewerDocument,
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
},
}
}
所々コードは端折っている。
ちなみにuseViewerQuery
はgraphql-code-generatorなどで生成される hooks で実態は Apollo のuseQuery
これをそのまま getServerSideProps に変えて SSR するとかなりパフォーマンスが悪化する。
なぜなら Apollo のuseQuery
がオーバーヘッドになるから。
(具体的には useQuery
で使われている getDataFromTree
が同期的に React Tree 全体を SSR するのが原因)
参考
代替策
SSR する際には Apollo のuseQuery
を使用せずにfetch
API かurqlを使うと良い。
fetch API を使う場合は公式の exampleに例がある。
GitHub API + urqlの場合はこんな感じ
query SearchIssues($query: String!) {
search(query: $query, type: ISSUE, first: 10) {
edges {
node {
... on Issue {
id
title
body
}
}
}
}
}
import { GetServerSideProps } from 'next'
import { createClient } from 'urql'
// graphql-code-generatorで生成した型
import {
SearchIssuesDocument,
SearchIssuesQuery,
} from '../types/graphql-client-api'
type Props = {
data: SearchIssuesQuery
}
const Issues = ({ data }: Props) => {
return ...
}
const client = createClient({
url: 'https://api.github.com/graphql',
fetchOptions: () => {
const token = process.env.GITHUB_API_KEY
return token
? {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/vnd.github.packages-preview+json',
},
}
: {}
},
})
export const getServerSideProps: GetServerSideProps = async () => {
const { data } = await client
.query<SearchIssuesQuery>(SearchIssuesDocument, { query: 'type:issue language:go' })
.toPromise()
return {
props: {
data,
},
}
}
export default Issues
最後に
Next.js + Apollo でパフォーマンスが悪い場合は fetch API や urql を使ってみることをおすすめします。
Discussion