Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

555 - Server-side query prefetching (SEO, no loadings) #557

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
46 changes: 46 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {ILogo} from '@/components/PageLayout/Footer/Logo'
import {IPost} from '@/components/Posts/Post'
import {FlatPage} from '@/types/api/base'
import {MenuItemShort} from '@/types/api/cms'
import {Competition, Event} from '@/types/api/competition'
import {SeminarId} from '@/utils/useSeminarInfo'

import {apiAxios} from './apiAxios'

type OurCompetition = Omit<Competition, 'history_events'> & {history_events: Event[]}

export const apiOptions = {
cms: {
flatPage: {
byUrl: (pageUrl: string) => ({
queryKey: ['cms', 'flat-page', 'by-url', pageUrl],
queryFn: () => apiAxios.get<FlatPage>(`/cms/flat-page/by-url/${pageUrl}`).then((res) => res.data),
}),
},
logo: () => ({
queryKey: ['cms', 'logo'],
queryFn: () => apiAxios.get<ILogo[]>('/cms/logo').then((res) => res.data),
}),
menuItem: {
onSite: (seminarId: SeminarId, type: 'menu' | 'footer') => ({
queryKey: ['cms', 'menu-item', 'on-site', seminarId, type],
queryFn: () =>
apiAxios.get<MenuItemShort[]>(`/cms/menu-item/on-site/${seminarId}?type=${type}`).then((res) => res.data),
}),
},
post: {
visible: (seminarId: SeminarId) => ({
queryKey: ['cms', 'post', 'visible', seminarId],
queryFn: () => apiAxios.get<IPost[]>(`/cms/post/visible?sites=${seminarId}`).then((res) => res.data),
}),
},
},
competition: {
competition: {
slug: (slug: string) => ({
queryKey: ['competition', 'competition', 'slug', slug],
queryFn: () => apiAxios.get<OurCompetition>(`/competition/competition/slug/${slug}`).then((res) => res.data),
}),
},
},
}
12 changes: 6 additions & 6 deletions src/api/apiAxios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import {apiInterceptor} from '@/api/apiInterceptor'
import {debugServer} from '@/utils/debugServer'
import {getBackendServerUrl} from '@/utils/urlBase'

export const newApiAxios = (base: 'server' | 'client') => {
export const newApiAxios = () => {
const isServer = typeof window === 'undefined'

const instance = axios.create({
// axios requesty mozu byt z FE next serveru alebo z browsru.
// - server vola BE URL (podla env vars) priamo
// - browser vola lokalnu /api URL
// - na deployed verzii (test.strom.sk, strom.sk) to chyti nginx a posle na deployed BE
// - na localhoste to chyti next middleware, kde to rewritneme na BE URL (podla env vars)
baseURL: base === 'server' ? `${getBackendServerUrl()}/api` : '/api',
baseURL: isServer ? `${getBackendServerUrl()}/api` : '/api',
// auth pozostava z comba:
// 1. `sessionid` httpOnly cookie - nastavuje a maze su server pri login/logout
// 2. CSRF hlavicka - server nastavuje cookie, ktorej hodnotu treba vlozit do hlavicky. axios riesi automaticky podla tohto configu
Expand All @@ -22,7 +24,7 @@ export const newApiAxios = (base: 'server' | 'client') => {
withCredentials: true,
})

if (base === 'server') {
if (isServer) {
// prvy definovany interceptor bezi posledny. logujeme finalnu URL
instance.interceptors.request.use((config) => {
const {method, url, baseURL} = config
Expand All @@ -44,6 +46,4 @@ export const newApiAxios = (base: 'server' | 'client') => {
}

// nasa "globalna" API instancia
export const apiAxios = newApiAxios('client')

export const serverApiAxios = newApiAxios('server')
export const apiAxios = newApiAxios()
15 changes: 15 additions & 0 deletions src/api/commonQueries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {QueryClient} from '@tanstack/react-query'

import {getSeminarInfoFromPathname} from '@/utils/useSeminarInfo'

import {apiOptions} from './api'

export const commonQueries = (queryClient: QueryClient, resolvedUrl: string) => {
const {seminarId} = getSeminarInfoFromPathname(resolvedUrl)

return [
queryClient.prefetchQuery(apiOptions.cms.menuItem.onSite(seminarId, 'menu')),
queryClient.prefetchQuery(apiOptions.cms.menuItem.onSite(seminarId, 'footer')),
queryClient.prefetchQuery(apiOptions.cms.logo()),
]
}
23 changes: 6 additions & 17 deletions src/components/PageLayout/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import Grid from '@mui/material/Unstable_Grid2'
import {useQuery} from '@tanstack/react-query'
import {FC} from 'react'

import {apiAxios} from '@/api/apiAxios'
import {apiOptions} from '@/api/api'
import {Link} from '@/components/Clickable/Link'
import {Loading} from '@/components/Loading/Loading'
import {ILogo, Logo} from '@/components/PageLayout/Footer/Logo'
import {MenuItemShort} from '@/types/api/cms'
import {Logo} from '@/components/PageLayout/Footer/Logo'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

export const Footer: FC = () => {
Expand All @@ -17,21 +16,11 @@ export const Footer: FC = () => {
data: menuItemsData,
isLoading: menuItemsIsLoading,
error: menuItemsError,
} = useQuery({
queryKey: ['cms', 'menu-item', 'on-site', seminarId, '?footer'],
queryFn: () => apiAxios.get<MenuItemShort[]>(`/cms/menu-item/on-site/${seminarId}?type=footer`),
})
const menuItems = menuItemsData?.data ?? []
} = useQuery(apiOptions.cms.menuItem.onSite(seminarId, 'footer'))
const menuItems = menuItemsData ?? []

const {
data: logosData,
isLoading: logosIsLoading,
error: logosError,
} = useQuery({
queryKey: ['cms', 'logo'],
queryFn: () => apiAxios.get<ILogo[]>('/cms/logo'),
})
const logos = logosData?.data.filter((logo) => !logo.disabled) ?? []
const {data: logosData, isLoading: logosIsLoading, error: logosError} = useQuery(apiOptions.cms.logo())
const logos = logosData?.filter((logo) => !logo.disabled) ?? []

return (
<Grid
Expand Down
12 changes: 5 additions & 7 deletions src/components/PageLayout/MenuMain/MenuMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import {useQuery} from '@tanstack/react-query'
import {useRouter} from 'next/router'
import {FC, useEffect, useState} from 'react'

import {apiAxios} from '@/api/apiAxios'
import {apiOptions} from '@/api/api'
import {Link} from '@/components/Clickable/Link'
import {CloseButton} from '@/components/CloseButton/CloseButton'
import {Loading} from '@/components/Loading/Loading'
import Menu from '@/svg/menu.svg'
import {MenuItemShort} from '@/types/api/cms'
import {useHasPermissions} from '@/utils/useHasPermissions'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

Expand Down Expand Up @@ -41,11 +40,10 @@ export const MenuMain: FC = () => {
}
}, [router, fullWidthMenu])

const {data: menuItemsData, isLoading: menuItemsIsLoading} = useQuery({
queryKey: ['cms', 'menu-item', 'on-site', seminarId, '?menu'],
queryFn: () => apiAxios.get<MenuItemShort[]>(`/cms/menu-item/on-site/${seminarId}?type=menu`),
})
const menuItems = menuItemsData?.data ?? []
const {data: menuItemsData, isLoading: menuItemsIsLoading} = useQuery(
apiOptions.cms.menuItem.onSite(seminarId, 'menu'),
)
const menuItems = menuItemsData ?? []

const lg = useMediaQuery<Theme>((theme) => theme.breakpoints.up('lg'))
const iconSize = lg ? 34 : 24
Expand Down
15 changes: 6 additions & 9 deletions src/components/Posts/Posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@ import {Stack, Typography} from '@mui/material'
import {useQuery} from '@tanstack/react-query'
import {FC} from 'react'

import {apiAxios} from '@/api/apiAxios'
import {apiOptions} from '@/api/api'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

import {Loading} from '../Loading/Loading'
import {IPost, Post} from './Post'
import {Post} from './Post'

export const Posts: FC = () => {
const {seminarId} = useSeminarInfo()

const {
data: postsData,
isLoading: postsIsLoading,
error: postsError,
} = useQuery({
queryKey: ['cms', 'post', 'visible'],
queryFn: () => apiAxios.get<IPost[]>(`/cms/post/visible?sites=${seminarId}`),
})
const posts = postsData?.data ?? []

const {seminarId} = useSeminarInfo()
} = useQuery(apiOptions.cms.post.visible(seminarId))
const posts = postsData ?? []

if (postsIsLoading) return <Loading />

Expand Down
32 changes: 19 additions & 13 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {CssBaseline} from '@mui/material'
import {ThemeProvider} from '@mui/material/styles'
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {HydrationBoundary, QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {ReactQueryDevtools} from '@tanstack/react-query-devtools'
import {isAxiosError} from 'axios'
import {AppProps} from 'next/app'
Expand Down Expand Up @@ -33,6 +33,10 @@ const ReactQueryProvider: FC<PropsWithChildren> = ({children}) => {
if (failureCount >= 3) return false
return true
},
// https://tanstack.com/query/latest/docs/framework/react/guides/ssr#initial-setup
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
mutations: {
// globalny error handler requestov cez useMutation
Expand Down Expand Up @@ -89,18 +93,20 @@ const MyApp: FC<AppProps> = ({Component, pageProps}) => {
</Head>
<AlertContainer.Provider>
<ReactQueryProvider>
<ReactQueryDevtools />
<CookiesProvider>
<AuthContainer.Provider>
<BannerAnimationContainer.Provider>
<ThemeProvider theme={theme}>
<CssBaseline />
<AlertBox />
<Component {...pageProps} />
</ThemeProvider>
</BannerAnimationContainer.Provider>
</AuthContainer.Provider>
</CookiesProvider>
<HydrationBoundary state={pageProps.dehydratedState}>
<ReactQueryDevtools />
<CookiesProvider>
<AuthContainer.Provider>
<BannerAnimationContainer.Provider>
<ThemeProvider theme={theme}>
<CssBaseline />
<AlertBox />
<Component {...pageProps} />
</ThemeProvider>
</BannerAnimationContainer.Provider>
</AuthContainer.Provider>
</CookiesProvider>
</HydrationBoundary>
</ReactQueryProvider>
</AlertContainer.Provider>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/malynar/[page].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import StromStaticPage, {seminarBasedGetServerSideProps} from '../strom/[page]'
import StromStaticPage, {getServerSideProps} from '../strom/[page]'

export default StromStaticPage

export const getServerSideProps = seminarBasedGetServerSideProps('malynar')
export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/admin/opravit-ulohu/[[...params]].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../../strom/admin/opravit-ulohu/[[...params]]'
import Page, {getServerSideProps} from '../../../strom/admin/opravit-ulohu/[[...params]]'

const ProblemAdministration: NextPage = () => {
return <Page />
}

export default ProblemAdministration

export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/admin/opravovanie/[[...params]].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../../strom/admin/opravovanie/[[...params]]'
import Page, {getServerSideProps} from '../../../strom/admin/opravovanie/[[...params]]'

const SemesterAdmnistration: NextPage = () => {
return <Page />
}

export default SemesterAdmnistration

export {getServerSideProps}
4 changes: 2 additions & 2 deletions src/pages/malynar/akcie/[[...params]].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import StaticPage, {competitionBasedGetServerSideProps} from '../../strom/akcie/[[...params]]'
import StaticPage, {getServerSideProps} from '../../strom/akcie/[[...params]]'

export default StaticPage

export const getServerSideProps = competitionBasedGetServerSideProps('malynar')
export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/archiv/[[...params]].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../strom/archiv/[[...params]]'
import Page, {getServerSideProps} from '../../strom/archiv/[[...params]]'

const Archiv: NextPage = () => {
return <Page />
}

export default Archiv

export {getServerSideProps}
8 changes: 5 additions & 3 deletions src/pages/malynar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../strom/index'
import Page, {getServerSideProps} from '../strom/index'

const Malynar: NextPage = () => {
const Home: NextPage = () => {
return <Page />
}

export default Malynar
export default Home

export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/profil/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../strom/profil/index'
import Page, {getServerSideProps} from '../../strom/profil/index'

const Profile: NextPage = () => {
return <Page />
}

export default Profile

export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/profil/uprava.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../strom/profil/uprava'
import Page, {getServerSideProps} from '../../strom/profil/uprava'

const Profil: NextPage = () => {
return <Page />
}

export default Profil

export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/registracia.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../strom/registracia'
import Page, {getServerSideProps} from '../strom/registracia'

const Register: NextPage = () => {
return <Page />
}

export default Register

export {getServerSideProps}
4 changes: 2 additions & 2 deletions src/pages/malynar/reset-password/[...resetToken].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PasswordReset from '../../strom/reset-password/[...resetToken]'
import PasswordReset, {getServerSideProps} from '../../strom/reset-password/[...resetToken]'

export default PasswordReset

export {getServerSideProps} from '../../strom/reset-password/[...resetToken]'
export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/verify-email/[verificationKey].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../strom/verify-email/[verificationKey]'
import Page, {getServerSideProps} from '../../strom/verify-email/[verificationKey]'

const Verify: NextPage = () => {
return <Page />
}

export default Verify

export {getServerSideProps}
4 changes: 3 additions & 1 deletion src/pages/malynar/vysledky/[[...params]].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NextPage} from 'next'

import Page from '../../strom/vysledky/[[...params]]'
import Page, {getServerSideProps} from '../../strom/vysledky/[[...params]]'

const Vysledky: NextPage = () => {
return <Page />
}

export default Vysledky

export {getServerSideProps}
Loading
Loading