diff --git a/.changeset/violet-taxis-report.md b/.changeset/violet-taxis-report.md
new file mode 100644
index 000000000..beca4fa83
--- /dev/null
+++ b/.changeset/violet-taxis-report.md
@@ -0,0 +1,9 @@
+---
+'@faustwp/experimental-app-router': patch
+---
+
+Added: New util for fetching data on the client side. In a client component (`use client`), you can now use Apollo's `useQuery` to fetch data once your application is wrapped with the `` component. This new component is available via:
+
+```tsx
+import { FaustProvider } from '@faustwp/experimental-app-router/ssr';
+```
diff --git a/examples/next/app-router/app/[slug]/page.tsx b/examples/next/app-router/app/[slug]/page.tsx
index 5a3ed3592..80fd253a4 100644
--- a/examples/next/app-router/app/[slug]/page.tsx
+++ b/examples/next/app-router/app/[slug]/page.tsx
@@ -4,8 +4,8 @@ import { hasPreviewProps } from './hasPreviewProps';
import { PleaseLogin } from '@/components/please-login';
export default async function Page(props) {
- const slug = props.params.slug;
const isPreview = hasPreviewProps(props);
+ const id = isPreview ? props.searchParams.p : props.params.slug;
let client = isPreview ? await getAuthClient() : await getClient();
@@ -13,19 +13,14 @@ export default async function Page(props) {
return ;
}
- /**
- * There is currently a bug in WPGraphQL where you can not query for previews
- * using the contentNode type. This bug will need to be resolved for preview
- * functionality in the below query to work properly. For now, it returns
- * the production ready data for the given contentNode regardless if
- * asPreview is true or false.
- *
- * @see https://github.com/wp-graphql/wp-graphql/issues/1673
- */
const { data } = await client.query({
query: gql`
- query GetContentNode($uri: ID!, $asPreview: Boolean!) {
- contentNode(id: $uri, idType: URI, asPreview: $asPreview) {
+ query GetContentNode(
+ $id: ID!
+ $idType: ContentNodeIdTypeEnum!
+ $asPreview: Boolean!
+ ) {
+ contentNode(id: $id, idType: $idType, asPreview: $asPreview) {
... on NodeWithTitle {
title
}
@@ -37,7 +32,8 @@ export default async function Page(props) {
}
`,
variables: {
- uri: slug,
+ id,
+ idType: isPreview ? 'DATABASE_ID' : 'URI',
asPreview: isPreview,
},
});
diff --git a/examples/next/app-router/app/layout.tsx b/examples/next/app-router/app/layout.tsx
index f07342a72..a61a5f7ec 100644
--- a/examples/next/app-router/app/layout.tsx
+++ b/examples/next/app-router/app/layout.tsx
@@ -2,7 +2,7 @@ import { gql } from '@apollo/client';
import { getClient } from '@faustwp/experimental-app-router';
import Link from 'next/link';
import '@/faust.config.js';
-
+import { FaustProvider } from '@faustwp/experimental-app-router/ssr';
export default async function RootLayout({ children }) {
const client = await getClient();
@@ -34,24 +34,26 @@ export default async function RootLayout({ children }) {
return (
-
-
-
- {data.generalSettings.title}
-
+
+
+
+
+ {data.generalSettings.title}
+
-
{data.generalSettings.description}
-
+
{data.generalSettings.description}
+
-
- {data.primaryMenuItems.nodes.map((node) => (
-
- {node.label}
-
- ))}
-
-
- {children}
+
+ {data.primaryMenuItems.nodes.map((node) => (
+
+ {node.label}
+
+ ))}
+
+
+ {children}
+
);
diff --git a/examples/next/app-router/app/making-client-queries/page.tsx b/examples/next/app-router/app/making-client-queries/page.tsx
new file mode 100644
index 000000000..25668bd95
--- /dev/null
+++ b/examples/next/app-router/app/making-client-queries/page.tsx
@@ -0,0 +1,21 @@
+'use client';
+
+import { gql, useQuery } from '@apollo/client';
+
+/**
+ * You can make client side queries as well with Apollo's `useQuery` hook within
+ * a client component ('use client' directive). Just make sure the
+ * is wrapping the app (in app/layout.js)
+ */
+
+export default function Page() {
+ const { data } = useQuery(gql`
+ query MyQuery {
+ generalSettings {
+ title
+ }
+ }
+ `);
+
+ return <>{data?.generalSettings?.title}>;
+}
diff --git a/examples/next/app-router/tsconfig.json b/examples/next/app-router/tsconfig.json
index 1dfdc3db7..30395783f 100644
--- a/examples/next/app-router/tsconfig.json
+++ b/examples/next/app-router/tsconfig.json
@@ -13,8 +13,8 @@
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "node",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
diff --git a/packages/experimental-app-router/jest.config.js b/packages/experimental-app-router/jest.config.js
index 1c9d776f4..a6a716a12 100644
--- a/packages/experimental-app-router/jest.config.js
+++ b/packages/experimental-app-router/jest.config.js
@@ -1,4 +1,4 @@
-module.exports = {
+export default {
roots: ['/tests'],
// Adds Jest support for TypeScript using ts-jest.
diff --git a/packages/experimental-app-router/package.json b/packages/experimental-app-router/package.json
index 292e8ec1d..9cf535b02 100644
--- a/packages/experimental-app-router/package.json
+++ b/packages/experimental-app-router/package.json
@@ -1,9 +1,11 @@
{
"name": "@faustwp/experimental-app-router",
+ "type": "module",
"version": "0.2.1",
"description": "Experimental: A Faust package to support Next.js' App Router",
"exports": {
".": "./dist/index.js",
+ "./ssr": "./dist/ssr.js",
"./package.json": "./package.json"
},
"types": "dist/index.d.ts",
diff --git a/packages/experimental-app-router/src/client.ts b/packages/experimental-app-router/src/client/config.ts
similarity index 58%
rename from packages/experimental-app-router/src/client.ts
rename to packages/experimental-app-router/src/client/config.ts
index 19c26e51a..a4726fe6b 100644
--- a/packages/experimental-app-router/src/client.ts
+++ b/packages/experimental-app-router/src/client/config.ts
@@ -1,17 +1,16 @@
import {
- ApolloClient,
- InMemoryCache,
+ ApolloLink,
InMemoryCacheConfig,
createHttpLink,
} from '@apollo/client';
// eslint-disable-next-line import/extensions
import { setContext } from '@apollo/client/link/context';
-// eslint-disable-next-line import/extensions
-import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
-import { fetchAccessToken } from './server/auth/fetchAccessToken.js';
-import { getConfig, getGraphqlEndpoint } from './faust-core-utils.js';
+import { getConfig, getGraphqlEndpoint } from '../faust-core-utils.js';
+import { fetchAccessToken } from '../server/auth/fetchAccessToken.js';
-async function createFaustApolloClient(authenticated = false) {
+export function createApolloConfig(
+ authenticated = false,
+): [InMemoryCacheConfig, ApolloLink] {
const { possibleTypes } = getConfig();
const inMemoryCacheObject: InMemoryCacheConfig = {
@@ -56,28 +55,5 @@ async function createFaustApolloClient(authenticated = false) {
* we may set config differently than how we currently do it.
*/
- return new ApolloClient({
- cache: new InMemoryCache(inMemoryCacheObject),
- link: linkChain,
- });
-}
-
-export async function getClient() {
- const faustApolloClient = await createFaustApolloClient(false);
- const client = registerApolloClient(() => faustApolloClient);
-
- return client.getClient();
-}
-
-export async function getAuthClient() {
- const token = await fetchAccessToken();
-
- if (!token) {
- return null;
- }
-
- const faustApolloClient = await createFaustApolloClient(true);
- const client = registerApolloClient(() => faustApolloClient);
-
- return client.getClient();
+ return [inMemoryCacheObject, linkChain];
}
diff --git a/packages/experimental-app-router/src/client/rsc.tsx b/packages/experimental-app-router/src/client/rsc.tsx
new file mode 100644
index 000000000..2d6daa884
--- /dev/null
+++ b/packages/experimental-app-router/src/client/rsc.tsx
@@ -0,0 +1,34 @@
+// eslint-disable-next-line import/extensions
+import { ApolloClient, InMemoryCache } from '@apollo/client';
+// eslint-disable-next-line import/extensions
+import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
+import { fetchAccessToken } from '../server/auth/fetchAccessToken.js';
+import { createApolloConfig } from './config.js';
+
+export function createRSCApolloClient(authenticated = false) {
+ const [inMemoryCacheObject, linkChain] = createApolloConfig(authenticated);
+ return new ApolloClient({
+ cache: new InMemoryCache(inMemoryCacheObject),
+ link: linkChain,
+ });
+}
+
+export async function getClient() {
+ const faustApolloClient = createRSCApolloClient(false);
+ const client = registerApolloClient(() => faustApolloClient);
+
+ return client.getClient();
+}
+
+export async function getAuthClient() {
+ const token = await fetchAccessToken();
+
+ if (!token) {
+ return null;
+ }
+
+ const faustApolloClient = createRSCApolloClient(true);
+ const client = registerApolloClient(() => faustApolloClient);
+
+ return client.getClient();
+}
diff --git a/packages/experimental-app-router/src/client/ssr.tsx b/packages/experimental-app-router/src/client/ssr.tsx
new file mode 100644
index 000000000..ebe9024ee
--- /dev/null
+++ b/packages/experimental-app-router/src/client/ssr.tsx
@@ -0,0 +1,27 @@
+'use client';
+
+// eslint-disable-next-line import/extensions
+import {
+ ApolloNextAppProvider,
+ NextSSRApolloClient,
+ NextSSRInMemoryCache,
+ // eslint-disable-next-line import/extensions
+} from '@apollo/experimental-nextjs-app-support/ssr';
+import React, { PropsWithChildren } from 'react';
+import { createApolloConfig } from './config.js';
+
+export function createSSRApolloClient(authenticated = false) {
+ const [inMemoryCacheObject, linkChain] = createApolloConfig(authenticated);
+ return new NextSSRApolloClient({
+ cache: new NextSSRInMemoryCache(inMemoryCacheObject),
+ link: linkChain,
+ });
+}
+
+export function FaustSSRProvider({ children }: PropsWithChildren