I'm trying to learn the Qwik framework and I'm trying to understand how to dynamically build static pages that requires API calls to populate the content
I have a route that's festival/[slug]/index.tsx. Before I had the routeLoader$ functionality that fetched content from an API server-side.
Right now I'm trying to convert this functionality into statically-generated pages.
I've followed the instruction of the docs (https://qwik.dev/docs/guides/static-site-generation/):
- I've ran the
npm run qwik add staticcommand - To build the static pages I use
npm run build.server - I've added the
onStaticGeneratemethod inside theindex.tsxlike this
export const onStaticGenerate: StaticGenerateHandler = async () => {
const { allFestivals } = await performRequest<IGetAllFestivalSlugs>({
query: GET_ALL_FESTIVAL_SLUGS,
});
return {
params: allFestivals.map(({ slug }) => {
return { slug };
}),
};
};
So now I get all slugs dynamically from the APIs correctly, and I can generate the page statically.
The problem is that I don't know how to fetch datas for all the pages dynamically.
I know I shouldn't use routeLoader$ since it's a server-side function.
Also I don't know how to populate the DocumentHead since before I used the resolveValue with the routeLoader$ in order to get the content.
Here's what my index.tsx looks like:
import { component$ } from "@builder.io/qwik";
import type {
RequestEventLoader,
StaticGenerateHandler,
} from "@builder.io/qwik-city";
import { routeLoader$ } from "@builder.io/qwik-city";
import { type DocumentHead } from "@builder.io/qwik-city";
import {
GET_ALL_FESTIVAL_SLUGS,
GET_SINGLE_FESTIVAL,
} from "~/lib/constants/api/queries";
import { performRequest } from "~/lib/datocms";
import type {
IGetAllFestivalSlugs,
IGetSingleFestival,
} from "~/lib/models/cms";
export const useGetFestivalDetail = routeLoader$(
async (event: RequestEventLoader) => {
const res = await performRequest<IGetSingleFestival>({
query: GET_SINGLE_FESTIVAL,
variables: { slug: event.params.slug },
});
return res.festival;
},
);
export default component$(() => {
const { value } = useGetFestivalDetail();
return (
<>
<h2>{value.title}</h2>
<p>{value.description}</p>
</>
);
});
export const onStaticGenerate: StaticGenerateHandler = async () => {
const { allFestivals } = await performRequest<IGetAllFestivalSlugs>({
query: GET_ALL_FESTIVAL_SLUGS,
});
return {
params: allFestivals.map(({ slug }) => {
return { slug };
}),
};
};
export const head: DocumentHead = ({ resolveValue }) => {
const festival = resolveValue(useGetFestivalDetail);
const meta = festival.seo
.filter((tag) => tag.attributes !== null)
.map(({ attributes }) => ({
property: attributes?.property,
content: attributes?.content,
}));
return { title: festival.title, meta };
};
I'm using DatoCMS as headless CMS through GraphQL.
One thing I've tried: I've tried to put all data inside the return inside onStaticGenerate but as far as I've understood I should just pass the path params, also I cannot put any values inside inside the head object.
adapters/static/vite-config.ts:
import { staticAdapter } from "@builder.io/qwik-city/adapters/static/vite";
import { extendConfig } from "@builder.io/qwik-city/vite";
import baseConfig from "../../vite.config";
export default extendConfig(baseConfig, () => {
return {
build: {
ssr: true,
rollupOptions: {
input: ["@qwik-city-plan"],
},
},
plugins: [
staticAdapter({
origin: "https://yoursite.qwik.dev",
}),
],
};
});
vite.config.ts inside my root project:
import { defineConfig } from "vite";
import { qwikVite } from "@builder.io/qwik/optimizer";
import { qwikCity } from "@builder.io/qwik-city/vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig(() => {
return {
plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
dev: {
headers: {
"Cache-Control": "public, max-age=0",
},
},
preview: {
headers: {
"Cache-Control": "public, max-age=600",
},
},
};
});