Skip to content

Commit

Permalink
Add client utils to app router package (#1659)
Browse files Browse the repository at this point in the history
* Add client utils (FaustProvider)

* Revert testing changes

* Clean up

* Working on ts config for package exports

* Use `Bundler` `moduleResolution` to support package exports while not requiring strict module resolution (file extensions, etc)

* Separate RSC and SSR clients into two functions

* Fix `createSSRApolloClient`

* Remove testing files

* Fix bundle size issue

* Rename `FaustSSRProvider` to `FaustProvider`

* Rename client queries directory

* Make previews more resilient

* Fix package-lock.json

* Update `package-lock.json`

* Add changeset

* Fix jest tests to use esm module syntax

* Fix npm audit

* Change

* Revert "Fix npm audit"

This reverts commit cdfa4a9.

* Update package-lock.json
  • Loading branch information
blakewilson authored Nov 29, 2023
1 parent 2559958 commit 626207b
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 68 deletions.
9 changes: 9 additions & 0 deletions .changeset/violet-taxis-report.md
Original file line number Diff line number Diff line change
@@ -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 `<FaustProvider>` component. This new component is available via:

```tsx
import { FaustProvider } from '@faustwp/experimental-app-router/ssr';
```
22 changes: 9 additions & 13 deletions examples/next/app-router/app/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,23 @@ 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();

if (!client) {
return <PleaseLogin />;
}

/**
* 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
}
Expand All @@ -37,7 +32,8 @@ export default async function Page(props) {
}
`,
variables: {
uri: slug,
id,
idType: isPreview ? 'DATABASE_ID' : 'URI',
asPreview: isPreview,
},
});
Expand Down
36 changes: 19 additions & 17 deletions examples/next/app-router/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -34,24 +34,26 @@ export default async function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>
<div>
<h1>
<Link href="/">{data.generalSettings.title}</Link>
</h1>
<FaustProvider>
<header>
<div>
<h1>
<Link href="/">{data.generalSettings.title}</Link>
</h1>

<h5>{data.generalSettings.description}</h5>
</div>
<h5>{data.generalSettings.description}</h5>
</div>

<ul>
{data.primaryMenuItems.nodes.map((node) => (
<li>
<Link href={node.uri}>{node.label}</Link>
</li>
))}
</ul>
</header>
{children}
<ul>
{data.primaryMenuItems.nodes.map((node) => (
<li>
<Link href={node.uri}>{node.label}</Link>
</li>
))}
</ul>
</header>
{children}
</FaustProvider>
</body>
</html>
);
Expand Down
21 changes: 21 additions & 0 deletions examples/next/app-router/app/making-client-queries/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <FaustProvider>
* is wrapping the app (in app/layout.js)
*/

export default function Page() {
const { data } = useQuery(gql`
query MyQuery {
generalSettings {
title
}
}
`);

return <>{data?.generalSettings?.title}</>;
}
4 changes: 2 additions & 2 deletions examples/next/app-router/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
Expand Down
2 changes: 1 addition & 1 deletion packages/experimental-app-router/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
export default {
roots: ['<rootDir>/tests'],

// Adds Jest support for TypeScript using ts-jest.
Expand Down
2 changes: 2 additions & 0 deletions packages/experimental-app-router/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -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];
}
34 changes: 34 additions & 0 deletions packages/experimental-app-router/src/client/rsc.tsx
Original file line number Diff line number Diff line change
@@ -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();
}
27 changes: 27 additions & 0 deletions packages/experimental-app-router/src/client/ssr.tsx
Original file line number Diff line number Diff line change
@@ -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<object>) {
return (
<ApolloNextAppProvider makeClient={() => createSSRApolloClient(false)}>
{children}
</ApolloNextAppProvider>
);
}
2 changes: 1 addition & 1 deletion packages/experimental-app-router/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { getClient, getAuthClient } from './client.js';
export { getClient, getAuthClient } from './client/rsc.js';
export { faustRouteHandler } from './server/routeHandler/index.js';
export { fetchAccessToken } from './server/auth/fetchAccessToken.js';
export { onLogout } from './server-actions/logoutAction.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line import/extensions
import { cookies } from 'next/headers';
import { cookies } from 'next/headers.js';
import { AuthorizeResponse } from '../routeHandler/tokenHandler.js';
import { getUrl } from '../../lib/getUrl.js';
import { getWpUrl } from '../../faust-core-utils.js';
Expand Down
1 change: 1 addition & 0 deletions packages/experimental-app-router/src/ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FaustSSRProvider as FaustProvider } from './client/ssr.js';
6 changes: 4 additions & 2 deletions packages/experimental-app-router/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "esnext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"target": "es2017",
"rootDir": "src",
"jsx": "react"
"jsx": "react",
"declaration": true
},
"exclude": ["node_modules", "dist"],
"include": ["src"]
Expand Down

0 comments on commit 626207b

Please sign in to comment.