Skip to content

Commit

Permalink
feat(qwik-city): add a way to get server-side env vars from `onStatic…
Browse files Browse the repository at this point in the history
…Generate`

The function `onStaticGenerate()` now takes an `EnvGetter`, allowing the end-user to generate SSG
routes parameters by fetching data from a service which is protected by credentials (e.g.: an
API or a database), and where those credentials are available as server-side env vars.

fix #4907
  • Loading branch information
Kocal committed Aug 7, 2023
1 parent 2e96ae0 commit da653a6
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ export default component$(() => {
return <p>Example: {params.id}</p>;
});

export const onStaticGenerate: StaticGenerateHandler = async () => {
export const onStaticGenerate: StaticGenerateHandler = async ({ env }) => {
// example of loading params for this use case
// every implementation will be different
const ids = await loadProductIds();
const ids = await loadProductIds({
apiKey: env.get('API_KEY'),
});

return {
params: ids.map((id) => {
Expand All @@ -99,7 +101,8 @@ export const onStaticGenerate: StaticGenerateHandler = async () => {
};
```

In the above example, the `onStaticGenerate()` function is loading the product IDs from a `loadProductIds()` function. This function would be custom for each implementation, but the general idea is that you'll need to load the data for each product ID, and then generate HTML files for each product ID.
In the above example, the `onStaticGenerate()` function is loading the product IDs from a `loadProductIds()` function by requesting an API behind an API key retrieved from an environment variable.
This function would be custom for each implementation, but the general idea is that you'll need to load the data for each product ID, and then generate HTML files for each product ID.

The `onStaticGenerate` function should be exported from the top-level of the module, and should return an object with a `params` property. The `params` property should be an array of objects, where each object is a set of params for the route path. For example, if the route path is `/product/:id`, then the `params` array should be an array of objects with an `id` property.

Expand Down
8 changes: 7 additions & 1 deletion packages/qwik-city/middleware/request-handler/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import type { Action } from '@builder.io/qwik-city';
import type { _deserializeData } from '@builder.io/qwik';
import type { EnvGetter as EnvGetter_2 } from '@builder.io/qwik-city/middleware/request-handler';
import type { FailReturn } from '@builder.io/qwik-city';
import type { Loader } from '@builder.io/qwik-city';
import type { QwikCityPlan } from '@builder.io/qwik-city';
Expand Down Expand Up @@ -69,6 +70,12 @@ export interface CookieValue {
// @public (undocumented)
export type DeferReturn<T> = () => Promise<T>;

// @public (undocumented)
export interface EnvGetter {
// (undocumented)
get(key: string): string | undefined;
}

// @public (undocumented)
export function getErrorHtml(status: number, e: any): string;

Expand Down Expand Up @@ -99,7 +106,6 @@ export interface RequestEventBase<PLATFORM = QwikCityPlatform> {
readonly cacheControl: (cacheControl: CacheControl) => void;
readonly clientConn: ClientConn;
readonly cookie: Cookie;
// Warning: (ae-forgotten-export) The symbol "EnvGetter" needs to be exported by the entry point index.d.ts
readonly env: EnvGetter;
readonly headers: Headers;
readonly method: string;
Expand Down
1 change: 1 addition & 0 deletions packages/qwik-city/middleware/request-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export type {
DeferReturn,
RequestEventBase,
ClientConn,
EnvGetter,
} from './types';
5 changes: 4 additions & 1 deletion packages/qwik-city/runtime/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Cookie } from '@builder.io/qwik-city/middleware/request-handler';
import { CookieOptions } from '@builder.io/qwik-city/middleware/request-handler';
import { CookieValue } from '@builder.io/qwik-city/middleware/request-handler';
import { DeferReturn } from '@builder.io/qwik-city/middleware/request-handler';
import type { EnvGetter } from '@builder.io/qwik-city/middleware/request-handler';
import { JSXNode } from '@builder.io/qwik';
import { QRL } from '@builder.io/qwik';
import { QwikIntrinsicElements } from '@builder.io/qwik';
Expand Down Expand Up @@ -414,7 +415,9 @@ export interface StaticGenerate {
}

// @public (undocumented)
export type StaticGenerateHandler = () => Promise<StaticGenerate> | StaticGenerate;
export type StaticGenerateHandler = ({ env, }: {
env: EnvGetter;
}) => Promise<StaticGenerate> | StaticGenerate;

// Warning: (ae-forgotten-export) The symbol "ContentState" needs to be exported by the entry point index.d.ts
//
Expand Down
7 changes: 6 additions & 1 deletion packages/qwik-city/runtime/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
RequestEventLoader,
RequestHandler,
ResolveSyncValue,
EnvGetter,
} from '@builder.io/qwik-city/middleware/request-handler';
import type { ReadonlySignal } from 'packages/qwik/src/core/state/signal';
import type * as zod from 'zod';
Expand Down Expand Up @@ -321,7 +322,11 @@ export interface ClientPageData extends Omit<EndpointResponse, 'status'> {
/**
* @public
*/
export type StaticGenerateHandler = () => Promise<StaticGenerate> | StaticGenerate;
export type StaticGenerateHandler = ({
env,
}: {
env: EnvGetter;
}) => Promise<StaticGenerate> | StaticGenerate;

/**
* @public
Expand Down
8 changes: 7 additions & 1 deletion packages/qwik-city/static/main-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@ export async function mainThread(sys: System) {
if (Array.isArray(paramNames) && paramNames.length > 0) {
if (typeof pageModule.onStaticGenerate === 'function' && paramNames.length > 0) {
// dynamic route page module
const staticGenerate = await pageModule.onStaticGenerate();
const staticGenerate = await pageModule.onStaticGenerate({
env: {
get(key: string) {
return sys.getEnv(key);
},
},
});
if (Array.isArray(staticGenerate.params)) {
for (const params of staticGenerate.params) {
const pathname = getPathnameForDynamicRoute(
Expand Down

0 comments on commit da653a6

Please sign in to comment.