diff --git a/.eslintignore b/.eslintignore index 1ac2665ed..53b3f1f9b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ node_modules *.esm.js /drupal /drupal-* +/local-next-drupal \ No newline at end of file diff --git a/packages/next-drupal/README.md b/packages/next-drupal/README.md index da8107fa6..aaac31938 100644 --- a/packages/next-drupal/README.md +++ b/packages/next-drupal/README.md @@ -21,9 +21,9 @@ https://next-drupal.org A page with all "Article" nodes. ```jsx -import { DrupalClient } from "next-drupal" +import { NextDrupal } from "next-drupal" -const drupal = new DrupalClient("https://cms.next-drupal.org") +const drupal = new NextDrupal("https://cms.next-drupal.org") export default function BlogPage({ articles }) { return ( diff --git a/packages/next-drupal/src/next-drupal-base.ts b/packages/next-drupal/src/next-drupal-base.ts index 2feb82c50..5d9db158e 100644 --- a/packages/next-drupal/src/next-drupal-base.ts +++ b/packages/next-drupal/src/next-drupal-base.ts @@ -30,6 +30,9 @@ const DEFAULT_HEADERS = { Accept: "application/json", } +/** + * The base class for NextDrupal clients. + */ export class NextDrupalBase { accessToken?: NextDrupalBaseOptions["accessToken"] @@ -173,6 +176,13 @@ export class NextDrupalBase { return this._token } + /** + * Fetches a resource from the given input URL or path. + * + * @param {RequestInfo} input The input URL or path. + * @param {FetchOptions} init The fetch options. + * @returns {Promise} The fetch response. + */ async fetch( input: RequestInfo, { withAuth, ...init }: FetchOptions = {} @@ -215,6 +225,12 @@ export class NextDrupalBase { return await fetch(input, init) } + /** + * Gets the authorization header value based on the provided auth configuration. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {Promise} The authorization header value. + */ async getAuthorizationHeader(auth: NextDrupalAuth) { let header: string @@ -250,6 +266,13 @@ export class NextDrupalBase { return header } + /** + * Builds a URL with the given path and search parameters. + * + * @param {string} path The URL path. + * @param {EndpointSearchParams} searchParams The search parameters. + * @returns {URL} The constructed URL. + */ buildUrl(path: string, searchParams?: EndpointSearchParams): URL { const url = new URL(path, this.baseUrl) @@ -269,7 +292,15 @@ export class NextDrupalBase { return url } - // async so subclasses can query for endpoint discovery. + /** + * Builds an endpoint URL with the given options. + * + * @param {Object} options The options for building the endpoint. + * @param {string} options.locale The locale. + * @param {string} options.path The path. + * @param {EndpointSearchParams} options.searchParams The search parameters. + * @returns {Promise} The constructed endpoint URL. + */ async buildEndpoint({ locale = "", path = "", @@ -291,6 +322,16 @@ export class NextDrupalBase { ).toString() } + /** + * Constructs a path from the given segment and options. + * + * @param {string | string[]} segment The path segment. + * @param {Object} options The options for constructing the path. + * @param {Locale} options.locale The locale. + * @param {Locale} options.defaultLocale The default locale. + * @param {PathPrefix} options.pathPrefix The path prefix. + * @returns {string} The constructed path. + */ constructPathFromSegment( segment: string | string[], options: { @@ -338,6 +379,15 @@ export class NextDrupalBase { }) } + /** + * Adds a locale prefix to the given path. + * + * @param {string} path The path. + * @param {Object} options The options for adding the locale prefix. + * @param {Locale} options.locale The locale. + * @param {Locale} options.defaultLocale The default locale. + * @returns {string} The path with the locale prefix. + */ addLocalePrefix( path: string, options: { locale?: Locale; defaultLocale?: Locale } = {} @@ -356,6 +406,12 @@ export class NextDrupalBase { return `${localePrefix}${path}` } + /** + * Gets an access token using the provided client ID and secret. + * + * @param {NextDrupalAuthClientIdSecret} clientIdSecret The client ID and secret. + * @returns {Promise} The access token. + */ async getAccessToken( clientIdSecret?: NextDrupalAuthClientIdSecret ): Promise { @@ -435,6 +491,12 @@ export class NextDrupalBase { return result } + /** + * Validates the draft URL using the provided search parameters. + * + * @param {URLSearchParams} searchParams The search parameters. + * @returns {Promise} The validation response. + */ async validateDraftUrl(searchParams: URLSearchParams): Promise { const path = searchParams.get("path") @@ -468,10 +530,22 @@ export class NextDrupalBase { return response } + /** + * Logs a debug message if debug mode is enabled. + * + * @param {string} message The debug message. + */ debug(message) { this.isDebugEnabled && this.logger.debug(message) } + /** + * Throws an error if the response contains JSON:API errors. + * + * @param {Response} response The fetch response. + * @param {string} messagePrefix The error message prefix. + * @throws {JsonApiErrors} The JSON:API errors. + */ async throwIfJsonErrors(response: Response, messagePrefix = "") { if (!response?.ok) { const errors = await this.getErrorsFromResponse(response) @@ -479,6 +553,12 @@ export class NextDrupalBase { } } + /** + * Extracts errors from the fetch response. + * + * @param {Response} response The fetch response. + * @returns {Promise} The extracted errors. + */ async getErrorsFromResponse(response: Response) { const type = response.headers.get("content-type") let error: JsonApiResponse | { message: string } @@ -506,6 +586,12 @@ export class NextDrupalBase { } } +/** + * Checks if the provided auth configuration is basic auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is basic auth, false otherwise. + */ export function isBasicAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthUsernamePassword { @@ -515,6 +601,12 @@ export function isBasicAuth( ) } +/** + * Checks if the provided auth configuration is access token auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is access token auth, false otherwise. + */ export function isAccessTokenAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthAccessToken { @@ -524,6 +616,12 @@ export function isAccessTokenAuth( ) } +/** + * Checks if the provided auth configuration is client ID and secret auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is client ID and secret auth, false otherwise. + */ export function isClientIdSecretAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthClientIdSecret { diff --git a/packages/next-drupal/src/next-drupal-pages.ts b/packages/next-drupal/src/next-drupal-pages.ts index fd0af89a1..e37d4916c 100644 --- a/packages/next-drupal/src/next-drupal-pages.ts +++ b/packages/next-drupal/src/next-drupal-pages.ts @@ -27,6 +27,10 @@ import type { NextApiResponse, } from "next" +/** + * The NextDrupalPages class extends the NextDrupal class and provides methods + * for interacting with a Drupal backend in the context of Next.js pages. + */ export class NextDrupalPages extends NextDrupal { private serializer: DrupalClientOptions["serializer"] @@ -59,6 +63,13 @@ export class NextDrupalPages extends NextDrupal { ) => this.serializer.deserialize(body, options) } + /** + * Gets the entry point for a given resource type. + * + * @param {string} resourceType The resource type. + * @param {Locale} locale The locale. + * @returns {Promise} The entry point URL. + */ async getEntryForResourceType( resourceType: string, locale?: Locale @@ -74,6 +85,14 @@ export class NextDrupalPages extends NextDrupal { return new DrupalMenuTree(links, parent) } + /** + * Gets a resource from the context. + * + * @param {string | DrupalTranslatedPath} input The input path or translated path. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResourceFromContext( input: string | DrupalTranslatedPath, context: GetStaticPropsContext, @@ -157,6 +176,14 @@ export class NextDrupalPages extends NextDrupal { return resource } + /** + * Gets a collection of resources from the context. + * + * @param {string} type The type of the resources. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched collection of resources. + */ async getResourceCollectionFromContext( type: string, context: GetStaticPropsContext, @@ -177,6 +204,14 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Gets a search index from the context. + * + * @param {string} name The name of the search index. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched search index. + */ async getSearchIndexFromContext( name: string, context: GetStaticPropsContext, @@ -189,6 +224,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Translates a path from the context. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The translated path. + */ async translatePathFromContext( context: GetStaticPropsContext, options?: { @@ -208,6 +250,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Gets the path from the context. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {string} The constructed path. + */ getPathFromContext( context: GetStaticPropsContext, options?: { @@ -223,6 +272,14 @@ export class NextDrupalPages extends NextDrupal { getPathsFromContext = this.getStaticPathsFromContext + /** + * Gets static paths from the context. + * + * @param {string | string[]} types The types of the resources. + * @param {GetStaticPathsContext} context The static paths context. + * @param {Object} options Options for the request. + * @returns {Promise["paths"]>} The fetched static paths. + */ async getStaticPathsFromContext( types: string | string[], context: GetStaticPathsContext, @@ -291,6 +348,13 @@ export class NextDrupalPages extends NextDrupal { return paths.flat() } + /** + * Builds static paths from resources. + * + * @param {Object[]} resources The resources. + * @param {Object} options Options for the request. + * @returns {Object[]} The built static paths. + */ buildStaticPathsFromResources( resources: { path: DrupalPathAlias @@ -313,6 +377,13 @@ export class NextDrupalPages extends NextDrupal { : [] } + /** + * Builds static paths parameters from paths. + * + * @param {string[]} paths The paths. + * @param {Object} options Options for the request. + * @returns {Object[]} The built static paths parameters. + */ buildStaticPathsParamsFromPaths( paths: string[], options?: { pathPrefix?: PathPrefix; locale?: Locale } @@ -342,6 +413,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Handles preview mode. + * + * @param {NextApiRequest} request The API request. + * @param {NextApiResponse} response The API response. + * @param {Object} options Options for the request. + */ async preview( request: NextApiRequest, response: NextApiResponse, @@ -411,6 +489,12 @@ export class NextDrupalPages extends NextDrupal { } } + /** + * Disables preview mode. + * + * @param {NextApiRequest} request The API request. + * @param {NextApiResponse} response The API response. + */ async previewDisable(request: NextApiRequest, response: NextApiResponse) { // Disable both preview and draft modes. response.clearPreviewData() @@ -427,6 +511,13 @@ export class NextDrupalPages extends NextDrupal { response.end() } + /** + * Gets the authentication configuration from the context and options. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {JsonApiWithAuthOption} options Options for the request. + * @returns {NextDrupalAuth} The authentication configuration. + */ getAuthFromContextAndOptions( context: GetStaticPropsContext, options: JsonApiWithAuthOption diff --git a/packages/next-drupal/src/next-drupal.ts b/packages/next-drupal/src/next-drupal.ts index 11b904689..51b20d53f 100644 --- a/packages/next-drupal/src/next-drupal.ts +++ b/packages/next-drupal/src/next-drupal.ts @@ -44,6 +44,10 @@ export function useJsonaDeserialize() { } } +/** + * The NextDrupal class extends the NextDrupalBase class and provides methods + * for interacting with a Drupal backend. + */ export class NextDrupal extends NextDrupalBase { cache?: NextDrupalOptions["cache"] @@ -86,6 +90,14 @@ export class NextDrupal extends NextDrupalBase { } } + /** + * Creates a new resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {JsonApiCreateResourceBody} body The body of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The created resource. + */ async createResource( type: string, body: JsonApiCreateResourceBody, @@ -127,6 +139,14 @@ export class NextDrupal extends NextDrupalBase { : /* c8 ignore next */ json } + /** + * Creates a new file resource for the specified media type. + * + * @param {string} type The type of the media. + * @param {JsonApiCreateFileResourceBody} body The body of the file resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The created file resource. + */ async createFileResource( type: string, body: JsonApiCreateFileResourceBody, @@ -172,6 +192,15 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Updates an existing resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiUpdateResourceBody} body The body of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The updated resource. + */ async updateResource( type: string, uuid: string, @@ -216,6 +245,14 @@ export class NextDrupal extends NextDrupalBase { : /* c8 ignore next */ json } + /** + * Deletes an existing resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} True if the resource was deleted, false otherwise. + */ async deleteResource( type: string, uuid: string, @@ -250,6 +287,14 @@ export class NextDrupal extends NextDrupalBase { return response.status === 204 } + /** + * Fetches a resource of the specified type by its UUID. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiOptions & JsonApiWithCacheOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResource( type: string, uuid: string, @@ -306,6 +351,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Fetches a resource of the specified type by its path. + * + * @param {string} path The path of the resource. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResourceByPath( path: string, options?: { @@ -416,6 +468,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(data) : data } + /** + * Fetches a collection of resources of the specified type. + * + * @param {string} type The type of the resources. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched collection of resources. + */ async getResourceCollection( type: string, options?: { @@ -454,6 +513,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Fetches path segments for a collection of resources of the specified types. + * + * @param {string | string[]} types The types of the resources. + * @param {JsonApiOptions & JsonApiWithAuthOption & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise<{ path: string, type: string, locale: Locale, segments: string[] }[]>} The fetched path segments. + */ async getResourceCollectionPathSegments( types: string | string[], options?: { @@ -563,6 +629,13 @@ export class NextDrupal extends NextDrupalBase { return paths.flat(2) } + /** + * Translates a path to a DrupalTranslatedPath object. + * + * @param {string} path The path to translate. + * @param {JsonApiWithAuthOption & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The translated path. + */ async translatePath( path: string, options?: JsonApiWithAuthOption & JsonApiWithNextFetchOptions @@ -595,6 +668,13 @@ export class NextDrupal extends NextDrupalBase { return await response.json() } + /** + * Fetches the JSON:API index. + * + * @param {Locale} locale The locale for the request. + * @param {JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The JSON:API index. + */ async getIndex( locale?: Locale, options?: JsonApiWithNextFetchOptions @@ -620,6 +700,12 @@ export class NextDrupal extends NextDrupalBase { return await response.json() } + /** + * Builds an endpoint URL for the specified parameters. + * + * @param {Parameters[0] & { resourceType?: string }} params The parameters for the endpoint. + * @returns {Promise} The built endpoint URL. + */ async buildEndpoint({ locale = "", resourceType = "", @@ -657,6 +743,13 @@ export class NextDrupal extends NextDrupalBase { ).toString() } + /** + * Fetches the endpoint URL for the specified resource type. + * + * @param {string} type The type of the resource. + * @param {Locale} locale The locale for the request. + * @returns {Promise} The fetched endpoint URL. + */ async fetchResourceEndpoint(type: string, locale?: Locale): Promise { const index = await this.getIndex(locale) @@ -680,6 +773,13 @@ export class NextDrupal extends NextDrupalBase { return url } + /** + * Fetches a menu by its name. + * + * @param {string} menuName The name of the menu. + * @param {JsonApiOptions & JsonApiWithCacheOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise<{ items: T[], tree: T[] }>} The fetched menu. + */ async getMenu( menuName: string, options?: JsonApiOptions & @@ -746,6 +846,13 @@ export class NextDrupal extends NextDrupalBase { return menu } + /** + * Fetches a view by its name. + * + * @param {string} name The name of the view. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise>} The fetched view. + */ async getView( name: string, options?: JsonApiOptions & JsonApiWithNextFetchOptions @@ -788,6 +895,13 @@ export class NextDrupal extends NextDrupalBase { } } + /** + * Fetches a search index by its name. + * + * @param {string} name The name of the search index. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched search index. + */ async getSearchIndex( name: string, options?: JsonApiOptions & JsonApiWithNextFetchOptions @@ -823,16 +937,24 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Deserializes the response body. + * + * @param {any} body The response body. + * @param {any} options Options for deserialization. + * @returns {any} The deserialized response body. + */ deserialize(body, options?) { if (!body) return null return this.deserializer(body, options) } - // Error handling. - // If throwJsonApiErrors is enabled, we show errors in the Next.js overlay. - // Otherwise, we log the errors even if debugging is turned off. - // In production, errors are always logged never thrown. + /** + * Logs or throws an error based on the throwJsonApiErrors flag. + * + * @param {Error} error The error to log or throw. + */ logOrThrowError(error: Error) { if (!this.throwJsonApiErrors) { this.logger.error(error) diff --git a/packages/next-drupal/src/types/next-drupal-base.ts b/packages/next-drupal/src/types/next-drupal-base.ts index 53ed464ef..873005de6 100644 --- a/packages/next-drupal/src/types/next-drupal-base.ts +++ b/packages/next-drupal/src/types/next-drupal-base.ts @@ -61,7 +61,7 @@ export type NextDrupalBaseOptions = { /** * Set custom headers for the fetcher. * - * * **Default value**: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json" } + * * **Default value**: `{ "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json" }` * * **Required**: *No* * * [Documentation](https://next-drupal.org/docs/client/configuration#headers) diff --git a/www/.gitignore b/www/.gitignore index 6686735a5..27c0264e9 100644 --- a/www/.gitignore +++ b/www/.gitignore @@ -12,6 +12,9 @@ /cypress/screenshots /cypress/videos +# Files generated automatically by typedoc +/content/docs/api + # next.js /.next/ /out/ diff --git a/www/config/docs.ts b/www/config/docs.ts index 1bdbed9d7..edc91e2bd 100644 --- a/www/config/docs.ts +++ b/www/config/docs.ts @@ -34,7 +34,7 @@ export const docsConfig: DocsConfig = { ], }, { - title: "Drupal Client", + title: "NextDrupal Client", items: [ { title: "Introduction", @@ -94,6 +94,10 @@ export const docsConfig: DocsConfig = { title: "Advanced Example", href: "/docs/pages#advanced-example", }, + { + title: "Pages Router", + href: "/docs/pages#pages-router", + }, ], }, { @@ -118,83 +122,11 @@ export const docsConfig: DocsConfig = { ], }, { - title: "Reference", + title: "API", items: [ { - title: "getResource", - href: "/docs/reference/getresource", - }, - { - title: "getResourceByPath", - href: "/docs/reference/getresourcebypath", - }, - { - title: "getResourceCollection", - href: "/docs/reference/getresourcecollection", - }, - { - title: "createResource", - href: "/docs/reference/createresource", - }, - { - title: "createFileResource", - href: "/docs/reference/createfileresource", - }, - { - title: "updateResource", - href: "/docs/reference/updateresource", - }, - { - title: "deleteResource", - href: "/docs/reference/deleteresource", - }, - { - title: "getResourceCollectionPathSegments", - href: "/docs/reference/getresourcecollectionpathsegments", - }, - { - title: "translatePath", - href: "/docs/reference/translatepath", - }, - { - title: "constructPathFromSegment", - href: "/docs/reference/constructpathfromsegment", - }, - { - title: "buildEndpoint", - href: "/docs/reference/buildendpoint", - }, - { - title: "getAccessToken", - href: "/docs/reference/getaccesstoken", - }, - { - title: "getMenu", - href: "/docs/reference/getmenu", - }, - { - title: "getView", - href: "/docs/reference/getview", - }, - { - title: "getSearchIndex", - href: "/docs/reference/getsearch", - }, - { - title: "buildUrl", - href: "/docs/reference/buildurl", - }, - { - title: "fetch", - href: "/docs/reference/fetch", - }, - { - title: "deserialize", - href: "/docs/reference/deserialize", - }, - { - title: "getAuthorizationHeader", - href: "/docs/reference/getauthorizationheader", + title: "API Reference", + href: "/docs/api/globals", }, ], }, diff --git a/www/config/site.ts b/www/config/site.ts index 63ec53e39..83effd88b 100644 --- a/www/config/site.ts +++ b/www/config/site.ts @@ -44,12 +44,16 @@ export const site: SiteConfig = { }, versions: [ { - version: "v1.6.0", + version: "v2.0.0", active: true, }, { - version: "v1.0.0", + version: "v1.6.0", url: "https://v1.next-drupal.org", }, + { + version: "canary", + url: "https://next.next-drupal.org", + }, ], } diff --git a/www/content/blog/next-drupal-2-0.mdx b/www/content/blog/next-drupal-2-0.mdx new file mode 100644 index 000000000..d84fda528 --- /dev/null +++ b/www/content/blog/next-drupal-2-0.mdx @@ -0,0 +1,110 @@ +--- +title: Next-Drupal 2.0 +author: brianperry +date: January 10, 2025 +created: "2025-01-25" +excerpt: Next.js 15, Drupal 11 and App Router Support. +published: true +--- + +After multiple alpha and beta releases, we're excited to announce the stable release of Next-Drupal 2.0. This release brings long-awaited **App Router support**, along with support for **Next.js 15** and **Drupal 11**. + +As part of our App Router support we've also added **tag-based invalidation** which allows you to invalidate content on your Next.js site using Drupal's powerful cache tag system. + +--- + +## App Router + +Next-Drupal 2.0 uses the App Router by default to match Next.js, but we will continue to provide support for the Pages Router. + +### Starters + +The Next-Drupal basic starter is now configured to use the App Router by default. + +Run the following command to create a new Next.js project using the App Router: + +```sh +npx create-next-app -e https://github.com/chapter-three/next-drupal-basic-starter +``` + +The Pages router version of the basic starter is also available, now named `next-drupal-pages-starter`. + +Run the following command to create a new Next.js project using the Pages Router: + +```sh +npx create-next-app -e https://github.com/chapter-three/next-drupal-pages-starter +``` + +### NextDrupal Client + +Similarly, the Next-Drupal Client has been divided into a version compatible with the App Router and a version compatible with the Pages Router. + +Create an instance of `NextDrupal` to use the client with the App Router: + +```js +import { NextDrupal } from "next-drupal" + +// Create a new NextDrupal client. +const drupal = new NextDrupal("https://example.com") +``` + +Create an instance of `NextDrupalPages` to use the client with the Pages Router: + +```js +import { NextDrupalPages } from "next-drupal" + +// Create a new NextDrupal client. +const drupal = new NextDrupalPages("https://example.com") +``` + +Backwards compatibility is also provided for instances of `DrupalClient` which will continue to work with the Pages Router. + +### Documentation + +Our documentation has been updated to reflect these changes and new features. All examples in our documentation now assume the App Router by default, but call out any Page Router specific differences where applicable. + +--- + +## Tag-based Invalidation + +For Next.js entity types configured in Drupal, a new 'tags' plugin can be selected under the on-demand revalidation options. When an entity of that type is updated, Drupal will now make a request to the Next.js revalidation endpoint and provide all associated cache tags. The API endpoint will then use Next's `invalidateTags` method to invalidate data related to the associated tags. + +For this to be useful, it is also necessary to set tags within your Next.js site. `NextDrupal` Client methods like `getResource` now accept a `tags` option which expects an array of cache tag values: + +```tsx title=app/[...slug]/page.tsx +// app/[...slug]/page.tsx +export default function Page({ params }) { + const {slug} = params; + const path = drupal.translatePath(slug) + + // Create a tag in the format of `entity_type:entity_id`. + const tag = `${path.entity.type}:${path.entity.id}` + + const node = await drupal.getResource(path, path.entity.uuid { + // Provide the cache tag when requesting the resource. + tags: [tag] + }) + + return +} +``` + +This will associate the page data with the provided cache tag. + +This configuration can provide much more targeted cache invalidation than path based invalidation alone. + +--- + +## Compatibility + +We have updated all of the next-drupal starters (JSON:API and GraphQL) to Next.js 15. + +The next module has been updated to Drupal 11. We've removed all deprecated code and APIs. + +Following end-of-life for the projects themselves, we no longer officially support Next.js 13 or Drupal 9. + +--- + +## Upgrading + +You can upgrade to `2.0` by following our upgrade guide [here](/docs/upgrade-guide). diff --git a/www/content/docs/client.mdx b/www/content/docs/client.mdx index 5e6290805..e6f4b6373 100644 --- a/www/content/docs/client.mdx +++ b/www/content/docs/client.mdx @@ -26,6 +26,8 @@ It also comes with full support for JSON:API write operations which means you ca ## Usage +### App Router + ```ts import { NextDrupal } from "next-drupal" @@ -41,3 +43,21 @@ const article = await drupal.getResource( "f4c61473-8297-4bf3-bab7-21c9633a7ca7" ) ``` + +### Pages Router + +```ts +import { NextDrupalPages } from "next-drupal" + +// Create a new DrupalClient. +const drupal = new NextDrupalPages("https://example.com") + +// Fetch articles. +const articles = await drupal.getResourceCollection("node--article") + +// Fetch one article by id. +const article = await drupal.getResource( + "node--article", + "f4c61473-8297-4bf3-bab7-21c9633a7ca7" +) +``` diff --git a/www/content/docs/configuration.mdx b/www/content/docs/configuration.mdx index 3e5293afa..2dbf33005 100644 --- a/www/content/docs/configuration.mdx +++ b/www/content/docs/configuration.mdx @@ -5,6 +5,8 @@ excerpt: Initialization and options for NextDrupal client. ## Initialization +### App Router + To create a new `NextDrupal` client, use the following initialization: ```ts @@ -19,6 +21,22 @@ Where `NEXT_PUBLIC_DRUPAL_BASE_URL` is the URL to your Drupal site defined as an NEXT_PUBLIC_DRUPAL_BASE_URL=http://example.com ``` +### Pages Router + +To create a new `NextDrupal` client, use the following initialization: + +```ts +import { NextDrupaPages } from "next-drupal" + +const drupal = new NextDrupalPages(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL) +``` + +Where `NEXT_PUBLIC_DRUPAL_BASE_URL` is the URL to your Drupal site defined as an [environment variable](/docs/environment-variables). + +```txt title=.env.local +NEXT_PUBLIC_DRUPAL_BASE_URL=http://example.com +``` + --- ## Options @@ -89,6 +107,24 @@ new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { You can find more info about using a custom deserializer [here](/docs/deserializer). +#### Pages Router + +This option is called `serializer` in the `NextDrupalPages` client. Aside from that, the usage is the same. + +```ts +import { Deserializer } from "jsonapi-serializer" + +const jsonDeserializer = new Deserializer({ + keyForAttribute: "camelCase", +}) + +const customDeserializer = jsonSerializer.deserialize.bind(jsonSerializer) + +new NextDrupalPages(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { + serializer: customDeserializer, +}) +``` + --- ### fetcher @@ -224,5 +260,3 @@ JSON:API errors are thrown in non-production environments by default. The errors - **Required**: No By default, the resource endpoint will be based on the resource name. If you turn this off, a JSON:API request will retrieve the resource's endpoint url. - ---- diff --git a/www/content/docs/deserializer.mdx b/www/content/docs/deserializer.mdx index 50a81197b..2c7d1d95e 100644 --- a/www/content/docs/deserializer.mdx +++ b/www/content/docs/deserializer.mdx @@ -29,3 +29,27 @@ export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { deserializer: customDeserializer, }) ``` + +### Pages Router + +This option is called `serializer` in the `NextDrupalPages` client. Aside from that, the usage is the same. + +```ts title=lib/drupal.ts +import { NextDrupal } from "next-drupal" +import { Deserializer } from "jsonapi-serializer" + +// Create a custom deserializer. +const jsonDeserializer = new Deserializer({ + keyForAttribute: "camelCase", +}) + +const customDeserializer = jsonDeserializer.deserialize.bind(jsonDeserializer) + +// Pass the custom deserializer to the client. +export const drupal = new NextDrupalPages( + process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, + { + serializer: customDeserializer, + } +) +``` diff --git a/www/content/docs/faq.mdx b/www/content/docs/faq.mdx index 88b0f6630..59947680b 100644 --- a/www/content/docs/faq.mdx +++ b/www/content/docs/faq.mdx @@ -7,6 +7,10 @@ excerpt: Frequently Asked Questions about Next.js for Drupal Next.js for Drupal works requires Drupal 9.3+ and Drupal 10. +## Which Next.js versions are supported? + +Next.js for Drupal supports both Next.js 14 and Next.js 15. This includes support for both the App Router and the Pages Router. + ## Does it work without authentication? Yes. Authentication is only required for previewing unpublished entities and revisions. diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index 2102032cd..144831fb5 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -3,23 +3,10 @@ title: Building Pages excerpt: How to build pages using JSON:API resources from Drupal. --- -In Next.js V14, data fetching has evolved significantly from previous versions. Instead of using `getStaticProps` and `getServerSideProps` you now use the native `fetch` function enhanced by Next.js to handle server-side data fetching. - -The `NextDrupal` client provides several functions to help you query JSON:API resources from Drupal. - - - --- ## Basic Example @@ -53,6 +40,18 @@ export default function AboutPage() { } ``` +### Time-based Revalidation + +To use time-based revalidation, you can pass a `revalidate` option to the `getResource` function. This will set the cache lifetime of a resource (in seconds). + +```tsx +const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8", + { next: { revalidate: 3600 } } +) +``` + --- ## Dynamic pages @@ -156,6 +155,151 @@ export default function Page({ params }) { } ``` +### Tag-based Revalidation + +In addition to revalidating based on time, it is also possible to revalidate +based on cache tag values. This is useful when you want to revalidate a resource +based on changes to other resources. + +Below we've adapted the Page function from the example above to include +tag-based revalidation. + +```tsx title=app/[...slug]/page.tsx +import { DrupalJsonApiParams } from "drupal-jsonapi-params" + +... + +export default function Page({ params }) { + const {slug} = params; + + const path = drupal.translatePath(slug) + + // Get the resource type. + const type = path.jsonapi.resourceName + const tag = `${path.entity.type}:${path.entity.id}` + + const params = new DrupalJsonApiParams() + + // Fetch the title, path and body field for pages. + if (type === "node--page") { + params.addFields("node--page", ["title", "path", "body"]) + } + + // Fetch additional fields for articles. + if (type === "node--article") { + params.addFields("node--article", ["title", "path", "body", "uid"]) + } + + const node = await drupal.getResource(path, path.entity.uuid { + params: params.getQueryObject(), + tags: [tag] + }) + + // Render different Components based on Node type. + if (node.type === "node--page") { + return + } + + if (node.type === "node--article") { + return + } + + return null +} +``` + ## Reference See the [fetching JSON:API resources](/docs/fetching-resources) section for more examples of fetching resources and collection of resources. + +## Pages Router + +When using the Pages Router, you will use the `getStaticProps` and `getServerSideProps` methods to fetch data for your pages. + +### Basic Example + +Here's an example which uses `getResource` to fetch a `page` node by ID: + +```tsx +const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8" +) +``` + +A full page would look like this: + +```tsx title=pages/about.tsx +// node will be populated at build time by getStaticProps +export default function AboutPage({ node }) { + return ( +
+

{node.title}

+ // ... +
+ ) +} + +export async function getStaticProps() { + // Fetch the node from Drupal. + const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8" + ) + + // Pass the node as props to the AboutPage. + return { + props: { + node, + }, + } +} +``` + +--- + +### Dynamic pages + +You can use Next.js [dynamic route](https://nextjs.org/docs/basic-features/pages#pages-with-dynamic-routes) to build static pages for Drupal entity types. + +Start by creating a page at `/pages/[...slug].tsx`, where `[...slug]` maps to the **path alias** for an entity type (or content type) in Drupal. + +This means `/pages/[...slug].tsx` will handle all pages with the following aliases: `/about`, `/team`, `/another/path` ...etc. + +To build static pages, there are two functions we need to implement: + +1. `getStaticPaths`: to tell Next.js all the routes that we want to be rendered. +2. `getStaticProps`: to fetch data for pages. + +```tsx title=pages/[...slug].tsx +export default function Page({ node }) { + return ( +
+

{node.title}

+ // ... +
+ ) +} + +export async function getStaticPaths(context) { + // Build paths for all `node--page`. + return { + paths: await drupal.getStaticPathsFromContext("node--page", context), + fallback: false, + } +} + +export async function getStaticProps(context) { + // Fetch the node based on the context. + // next-drupal automatically handles the slug value. + const node = await drupal.getResourceFromContext("node--page", context) + + return { + props: { + node, + }, + } +} +``` + +--- diff --git a/www/content/docs/reference/buildendpoint.mdx b/www/content/docs/reference/buildendpoint.mdx deleted file mode 100644 index bf051289c..000000000 --- a/www/content/docs/reference/buildendpoint.mdx +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: buildEndpoint -excerpt: Get the JSON:API entry for a resource type. ---- - -```ts -const url = await drupal.buildEndpoint({ - locale, - resourceType, - path, - searchParams -}): Promise -``` - -- `type: string` - - Optional - - The resource type. Example: `node--article`. -- `locale: string` - - Optional - - The locale to fetch the index. Example: `es` or `fr`. -- `path: string` - - Optional - - The path to fetch. Example: `test`. -- `searchParams: string | Record | URLSearchParams | JsonApiParams` - - Optional - - Search params. Example: `{ bar: "baz" }`. - ---- - -## Notes - -By default, when retrieving resources in `getResource` or `getResourceCollection`, the `NextDrupal` client make a request to Drupal to fetch the JSON:API resource entry. - -Example: if you provide `node--article`, `NextDrupal` will make a request to `http://example.com/jsonapi/node/article`. - -If you would like to infer the entry from the resource type, use the [`useDefaultResourceTypeEntry`](/docs/configuration#usedefaultresourcetypeentry) option. - -```ts -const drupal = new DrupalClient(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { - useDefaultResourceTypeEntry: true, -}) -``` - ---- - -## Examples - -```ts -// https://example.com/jsonapi/node/article. -const url = await drupal.buildEndpoint({ resourceType: `node--article` }) - -// https://example.com/jsonapi/en/node/article. -const url = await drupal.buildEndpoint({ - locale: `en`, - resourceType: `node--article`, -}) - -// https://example.com/jsonapi/en/node/article?include=field_image. -const url = await drupal.buildEndpoint({ - locale: `en`, - resourceType: `node--article`, - searchParams: `include=field_image`, -}) - -// https://example.com/jsonapi/en/views/articles/page_1. -const url = await drupal.buildEndpoint({ - locale: `en`, - path: `/views/articles/page_1`, -}) -``` diff --git a/www/content/docs/reference/buildurl.mdx b/www/content/docs/reference/buildurl.mdx deleted file mode 100644 index 18d5a5983..000000000 --- a/www/content/docs/reference/buildurl.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: buildUrl -excerpt: A helper for building Url with query params. ---- - -```ts -const url = drupal.buildUrl(path, searchParams?): URL -``` - -- `path: string` - - **Required** - - The path for the url. Example: "/example" -- `searchParams: string | Record | URLSearchParams | JsonApiParams` - - Optional - ---- - -## Examples - -```ts -const drupal = new DrupalClient("https://example.com") - -// https://drupal.org -drupal.buildUrl("https://drupal.org").toString() - -// https://example.com/foo -drupal.buildUrl("/foo").toString() - -// https://example.com/foo?bar=baz -client.buildUrl("/foo", { bar: "baz" }).toString() -``` - -- Build a URL from `DrupalJsonApiParams` - -```ts -const params = { - getQueryObject: () => ({ - sort: "-created", - "fields[node--article]": "title,path", - }), -} - -// https://example.com/jsonapi/node/article?sort=-created&fields%5Bnode--article%5D=title%2Cpath -drupal.buildUrl("/jsonapi/node/article", params).toString() -``` diff --git a/www/content/docs/reference/constructpathfromsegment.mdx b/www/content/docs/reference/constructpathfromsegment.mdx deleted file mode 100644 index b744484d3..000000000 --- a/www/content/docs/reference/constructpathfromsegment.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: constructPathFromSegment -excerpt: Returns a Drupal path given an array of Next.js path segments, path prefix and locale. ---- - -```ts -const slug = drupal.constructPathFromSegment( - segment, - options?: { - pathPrefix, - locale, - defaultLocale - } -): string -``` - -- `segment: string | string[]` -- `options` - - Optional - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Examples - -- Get the path (slug) from `en/news/article-1`. - -```ts - -const path = drupal.constructPathFromSegment('article-1', { - locale: 'en', - pathPrefix: '/news' -}) - -`path` will result in: `/en/news/article-1` - -``` - -This might be useful when trying to get the full path of the `params` object coming from `generateStaticParams`. -Ex: `app/[lang]/[...slug]/page.tsx` - -```tsx -export async function generateStaticParams() { - const apiParams = new DrupalJsonApiParams() - const params = apiParams - .addFields("node--landing_page", ["status", "path", "changed"]) - .addSort("changed", "DESC") - .addFilter("status", "1") - - const paths = await drupalClient.getResourceCollectionPathSegments( - ["node--landing_page"], - { - params: params.getQueryObject(), - defaultLocale: "en", - locales: ["en", "es"], - } - ) - - return paths.map((path) => { - return { - slug: path.segments, - lang: path.locale, - } - }) -} - -export default async function LandingPage({ params }) { - const { slug, lang } = params - - const path = drupal.constructPathFromSegment(slug, { locale: lang }) - - // If slug = ['segment-1', 'segment-2'] and lang = 'en' - // path will result in '/en/segment-1/segment-2' - - // ... -} -``` diff --git a/www/content/docs/reference/createfileresource.mdx b/www/content/docs/reference/createfileresource.mdx deleted file mode 100644 index f321df656..000000000 --- a/www/content/docs/reference/createfileresource.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: createFileResource -excerpt: Create a file resource for a parent entity. ---- - -```ts -const resource = await drupal.createFileResource( - type, - body, - options?: { - params, - withAuth, - deserialize, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. In most cases this is `file--file`. -- `body: JsonApiCreateFileResourceBody` - - **Required** - - The body payload with `data`. - - `type: string`: The resource type of the host entity. Example: `media--image`. - - `field: string`: The name of the file field on the host entity: Example: `field_media_image`. - - `filename: string`: The name of the file with extension: Example: `avatar.jpg`. - - `file: Buffer`: The file. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - ---- - -## Examples - -To create a file resource, you use the name of the file field on the parent entity. - -Example: A `media--image` with a file field called `field_media_image`. - -```ts -const file = await drupal.createFileResource("file--file", { - data: { - attributes: { - type: "media--image", // <-- The type of the parent resource. - field: "field_media_image", // <-- The name of the field on the parent resource. - filename: "filename.jpg", - file: await fs.readFile("/path/to/file.jpg"), - }, - }, -}) -``` - -This will create a `file--file` resource. - -You can then use this to create a new `media--image` with a relationship to the `file--file`. - -```ts -const media = await drupal.createResource("media--image", { - data: { - attributes: { - name: "Name for the media", - }, - relationships: { - field_media_image: { - data: { - type: "file--file", - id: file.id, - }, - }, - }, - }, -}) -``` diff --git a/www/content/docs/reference/createresource.mdx b/www/content/docs/reference/createresource.mdx deleted file mode 100644 index 49cfd475a..000000000 --- a/www/content/docs/reference/createresource.mdx +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: createResource -excerpt: Create a resource. ---- - -```ts -const resource = await drupal.createResource( - type, - body, - options?: { - params, - withAuth, - deserialize, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article`, `taxonomy_term--tags`, or `block_content--basic`. -- `body: JsonApiCreateResourceBody` - - **Required** - - The body payload with `data`. - - ```ts - interface JsonApiCreateResourceBody { - data: { - type?: string - attributes?: Record - relationships?: Record - } - } - ``` -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - ---- - -## Examples - -- Create a `node--page` resource. - -```ts -const page = await drupal.createResource("node--page", { - data: { - attributes: { - title: "Page Title", - body: { - value: "

Content of body field

", - format: "full_html", - }, - }, - }, -}) -``` - -- Get a `node--article` resource with a taxonomy term. - -```ts -const article = await drupal.createResource("node--article", { - data: { - attributes: { - title: "Title of Article", - body: { - value: "

Content of body field

", - format: "full_html", - }, - }, - relationships: { - field_category: { - data: { - type: "taxonomy_term--category", - id: "28ab9f26-927d-4e33-9510-b59a7ccdafe6", - }, - }, - }, - }, -}) -``` - -- Using filters. - -```ts -const page = await drupal.createResource( - "node--page", - { - data: { - attributes: { - title: "Page Title", - body: { - value: "

Content of body field

", - format: "full_html", - }, - }, - }, - }, - { - params: { - "fields[node--page]": "title,path", - }, - } -) -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const page = await drupal.createResource("node--page", { - data: { - attributes: { - title: "Page Title", - body: { - value: "

Content of body field

", - format: "full_html", - }, - }, - }, -}) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/deleteresource.mdx b/www/content/docs/reference/deleteresource.mdx deleted file mode 100644 index 22520bda9..000000000 --- a/www/content/docs/reference/deleteresource.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: deleteResource -excerpt: Delete a resource. ---- - -```ts -const deleted = await drupal.deleteResource( - type, - uuid, - options?: { - withAuth, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article`, `taxonomy_term--tags`, or `block_content--basic`. -- `uuid: string` - - **Required** - - The resource id. Example `a50ffee7-ba94-46c9-9705-f9f8f440db94`. -- `options` - - Optional - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - ---- - -## Examples - -- Delete a `node--page` resource. - -```ts -const isDeleted = await drupal.deleteResource( - "node--page", - "a50ffee7-ba94-46c9-9705-f9f8f440db94" -) -``` diff --git a/www/content/docs/reference/deserialize.mdx b/www/content/docs/reference/deserialize.mdx deleted file mode 100644 index 5613f4e48..000000000 --- a/www/content/docs/reference/deserialize.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: deserialize -excerpt: A helper for deserializing JSON:API resources. ---- - -```ts -const resource = drupal.deserialize(body, options?) -``` - -- `body` - - **Required** - - The raw JSON:API response. -- `options` - - Optional - - Options for the deserializer. - ---- - -## Notes - -- To provide your own custom deserializer, see the [deserializer docs](/docs/deserializer). - ---- - -## Examples - -```ts -const url = drupal.buildUrl("/jsonapi/node/article", { - sort: "-created", - "fields[node--article]": "title,path", -}) - -const response = await drupal.fetch(url.toString()) - -const json = await response.json() - -const resource = drupal.deserialize(json) -``` diff --git a/www/content/docs/reference/fetch.mdx b/www/content/docs/reference/fetch.mdx deleted file mode 100644 index ae73e567b..000000000 --- a/www/content/docs/reference/fetch.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: fetch -excerpt: A wrapper for the polyfilled fetch. ---- - -```ts -const response = await drupal.fetch(input, init?): Promise -``` - -- `input: RequestInfo` - - **Required** - - The url to fetch from. -- `init: FetchOptions` - - Optional - - The fetch options with `withAuth`. - - If `withAuth` is set, `fetch` will fetch an `Authorization` header before making the request. - - - ---- - -## Notes - -- To provide your own custom fetcher, see the [fetcher docs](/docs/fetcher). - ---- - -## Examples - -```ts -const url = drupal.buildUrl("/jsonapi/node/article", { - sort: "-created", - "fields[node--article]": "title,path", -}) - -const response = await drupal.fetch(url.toString()) -``` diff --git a/www/content/docs/reference/getaccesstoken.mdx b/www/content/docs/reference/getaccesstoken.mdx deleted file mode 100644 index 8e7773a12..000000000 --- a/www/content/docs/reference/getaccesstoken.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: getAccessToken -excerpt: Retrieve an access token. ---- - -```ts -const token = await drupal.getAccessToken(clientIdSecret?: { - clientId, - clientSecret, - url?, - scope? -}): Promise -``` - -- `clientIdSecret` - - Optional - - If `clientIdSecret` is not provided, `NextDrupal` will use the `clientId` and `clientSecret` configured in `auth`. - - `clientId: string`: The oauth client id. - - `clientSecret: string`: The oauth client secret. - - `url: string`: The oauth url. Default: `/oauth/token`. - - `scope: string` - ---- - -## Examples - -```ts -const accessToken = await drupal.getAccessToken({ - clientId: "7034f4db-7151-466f-a711-8384bddb9e60", - clientSecret: "d92Fm^ds", -}) -``` diff --git a/www/content/docs/reference/getauthorizationheader.mdx b/www/content/docs/reference/getauthorizationheader.mdx deleted file mode 100644 index a776e06d1..000000000 --- a/www/content/docs/reference/getauthorizationheader.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: getAuthorizationHeader -excerpt: Returns an authorization header based on an authentication method. ---- - -```ts -const paths = await drupal.getAuthorizationHeader( - auth -): Promise -``` - -- `auth: NextDrupalAuth` - - **Required** - - A supported authentication method. See [auth](/docs/authentication) - ---- - -## Examples - -- Auth method: `Bearer`. For this, you need a `clientId` and `clientSecret` - -```ts -const auth = { - clientId: process.env.DRUPAL_CLIENT_ID, - clientSecret: process.env.DRUPAL_CLIENT_SECRET, -} - -const authorizationHeader = await drupal.getAuthorizationHeader(auth) - -// authorizationHeader = Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjM ......... -``` diff --git a/www/content/docs/reference/getentryforresourcetype.mdx b/www/content/docs/reference/getentryforresourcetype.mdx deleted file mode 100644 index b9110c0fa..000000000 --- a/www/content/docs/reference/getentryforresourcetype.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: getEntryForResourceType -excerpt: Get the JSON:API entry for a resource type. ---- - -```ts -const url = await drupal.getEntryForResourceType(type, locale?): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article`. -- `locale: string` - - Optional - - The locale to fetch the index. Example: `es` or `fr`. - ---- - -## Notes - -By default, when retrieving resources in `getResource` or `getResourceCollection`, the `NextDrupalPages` make a request to Drupal to fetch the JSON:API resource entry.. - -Example: if you provide `node--article`, `NextDrupalPages` will make a request to `http://example.com/jsonapi/node/article`. - -If you would like to infer the entry from the resource type, use the [`useDefaultResourceTypeEntry`](/docs/configuration#usedefaultresourcetypeentry) option. - -```ts -const drupal = new NextDrupalPages(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { - useDefaultResourceTypeEntry: true, -}) -``` - ---- - -## Examples - -```ts -// https://example.com/jsonapi/node/article. -const url = await drupal.getEntryForResourceType(`node--article`) -``` diff --git a/www/content/docs/reference/getmenu.mdx b/www/content/docs/reference/getmenu.mdx deleted file mode 100644 index 521347953..000000000 --- a/www/content/docs/reference/getmenu.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: getMenu -excerpt: Fetch a menu from Drupal. ---- - - - -You need to install the [JSON:API Menu Items](https://www.drupal.org/project/jsonapi_menu_items) module to use `getMenu`. - - - -```ts -const resource = await drupal.getMenu( - menuName, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - withCache, - cacheKey, - } -): Promise<{ - items: T[] - tree: T[] -}> -``` - -- `menuName: string` - - **Required** - - The name of the menu. Example: `main` or `footer`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - - `withCache: boolean`: Set `withCache` if you want to store and retrieve the menu from cache. - - `cacheKey: string`: The cache key to use. - ---- - -## Notes - -- `getMenu` returns: - - `items`: An array of `DrupalMenuItem`. - - `tree`: An array of `DrupalMenuItem` with children nested to match the hierarchy from Drupal. - ---- - -## Examples - -- Get the `main` menu. - -```ts -const { menu, items } = await drupal.getMenu("main") -``` - -- Get the `main` menu using cache. - -```ts -const menu = await drupal.getMenu("main", { - withCache: true, - cacheKey: "menu--main", -}) -``` - - diff --git a/www/content/docs/reference/getpathfromcontext.mdx b/www/content/docs/reference/getpathfromcontext.mdx deleted file mode 100644 index cb847abd4..000000000 --- a/www/content/docs/reference/getpathfromcontext.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: getPathFromContext -excerpt: Return the path (slug) from getStaticProps or getServerSideProps context. ---- - -```ts -const slug = drupal.getPathFromContext( - context, - options?: { - pathPrefix, - } -): string -``` - -- `context: GetStaticPropsContext | GetServerSidePropsContext` - - **Required** - - The context from `getStaticProps` or `getServerSideProps`. -- `options` - - Optional - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - ---- - -## Examples - -- Get the path (slug) from `getStaticProps` context. - -```ts -export async function getStaticProps(context) { - const slug = await drupal.getPathFromContext(context) -} -``` diff --git a/www/content/docs/reference/getresource.mdx b/www/content/docs/reference/getresource.mdx deleted file mode 100644 index edc0d1da4..000000000 --- a/www/content/docs/reference/getresource.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: getResource -excerpt: Fetch a resource by id. ---- - -```ts -const resource = await drupal.getResource( - type, - uuid, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - withCache, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article`, `taxonomy_term--tags`, or `block_content--basic`. -- `uuid: string` - - **Required** - - The id of the resource. Example: `15486935-24bf-4be7-b858-a5b2de78d09d`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - - `withCache: boolean`: Set `withCache` if you want to store and retrieve the resource from cache. - - `cacheKey: string`: The cache key to use. - ---- - -## Examples - -- Get a page by uuid. - -```ts -const node = await drupal.getResource( - "node--page", - "07464e9f-9221-4a4f-b7f2-01389408e6c8" -) -``` - -- Get the `es` translation for a page by uuid. - -```ts -const node = await drupal.getResource( - "node--page", - "07464e9f-9221-4a4f-b7f2-01389408e6c8", - { - locale: "es", - defaultLocale: "en", - } -) -``` - -- Get the raw JSON:API response. - -```ts -const { data, meta, links } = await drupal.getResource( - "node--page", - "07464e9f-9221-4a4f-b7f2-01389408e6c8", - { - deserialize: false, - } -) -``` - -- Get a `node--article` resource using cache. - -```ts -const id = "07464e9f-9221-4a4f-b7f2-01389408e6c8" - -const article = await drupal.getResource("node--article", id, { - withCache: true, - cacheKey: `node--article:${id}`, -}) -``` - - - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const node = await drupal.getResource( - "node--page", - "07464e9f-9221-4a4f-b7f2-01389408e6c8" -) -``` - -- Using `DrupalTaxonomyTerm` for a taxonomy term entity type. - -```ts -import { DrupalTaxonomyTerm } from "next-drupal" - -const term = await drupal.getResource( - "taxonomy_term--tags", - "7b47d7cc-9b1b-4867-a909-75dc1d61dfd3" -) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getresourcebypath.mdx b/www/content/docs/reference/getresourcebypath.mdx deleted file mode 100644 index 828033d9a..000000000 --- a/www/content/docs/reference/getresourcebypath.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: getResourceByPath -excerpt: Fetch a resource by path. ---- - -```ts -const resource = await drupal.getResourceByPath( - path, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - isVersionable, - } -): Promise -``` - -- `path: string` - - **Required** - - The resource path. Example: `/blog/slug-for-article`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - - `isVersionable: boolean`: Set to true if you're fetching the revision for a resource. _Automatically set to true for node entity types_. - ---- - -## Notes - -- The [Decoupled Router](https://drupal.org/project/decoupled_router) module is required. - ---- - -## Examples - -- Get a page by path. - -```ts -const node = await drupal.getResourceByPath("/blog/slug-for-article") -``` - -- Get the raw JSON:API response. - -```ts -const { data, meta, links } = await drupal.getResourceByPath( - "/blog/slug-for-article", - { - deserialize: false, - } -) -``` - -- Get the `es` translation for a page. - -```ts -const node = await drupal.getResourceByPath("/blog/slug-for-article", { - locale: "es", - defaultLocale: "en", -}) -``` - - - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const node = await drupal.getResourceByPath( - "/blog/slug-for-article" -) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getresourcecollection.mdx b/www/content/docs/reference/getresourcecollection.mdx deleted file mode 100644 index a73f557ad..000000000 --- a/www/content/docs/reference/getresourcecollection.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: getResourceCollection -excerpt: Fetch a collection of resources. ---- - -```ts -const resource = await drupal.getResourceCollection( - type, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article` or `user--user`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | DrupalClientAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Examples - -- Get all articles. - -```ts -const articles = await drupal.getResourceCollection("node--article") -``` - -- Using filters. - -```ts -const publishedArticles = await drupal.getResourceCollection("node--article", { - params: { - "filter[status]": "1", - }, -}) -``` - -- Get the raw JSON:API response. - -```ts -const { data, meta, links } = await drupal.getResourceCollection("node--page", { - deserialize: false, -}) -``` - - - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const nodes = await drupal.getResourceCollection("node--article") -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getresourcecollectionfromcontext.mdx b/www/content/docs/reference/getresourcecollectionfromcontext.mdx deleted file mode 100644 index da579209c..000000000 --- a/www/content/docs/reference/getresourcecollectionfromcontext.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: getResourceCollectionFromContext -excerpt: Fetch a collection of resources from getStaticProps or getServerSideProps context. ---- - -```ts -const resource = await drupal.getResourceCollectionFromContext( - type, - context, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article` or `user--user`. -- `context: GetStaticPropsContext | GetServerSidePropsContext` - - **Required** - - The context from `getStaticProps` or `getServerSideProps`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Notes - -- The localized resources will be fetched based on the `locale` and `defaultLocale` values from `context`. - ---- - -## Examples - -- Get all articles from context. - -```ts title=pages/[[...slug]].tsx -export async function getStaticProps(context) { - const articles = await drupal.getResourceCollectionFromContext( - "node--article", - context - ) - - return { - props: { - articles, - }, - } -} -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const nodes = await drupal.getResourceCollectionFromContext( - "node--article", - context -) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getresourcecollectionpathsegments.mdx b/www/content/docs/reference/getresourcecollectionpathsegments.mdx deleted file mode 100644 index 2c0e51b2a..000000000 --- a/www/content/docs/reference/getresourcecollectionpathsegments.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: getResourceCollectionPathSegments -excerpt: Returns an array of Drupal path-related data that can be used to provide a static list for generateStaticParams. ---- - -```ts -const paths = await drupal.getResourceCollectionPathSegments( - types, - options?: { - params, - withAuth, - pathPrefix, - locale, - defaultLocale, - } -): Promise<{ - path: string - type: string - locale: Locale - segments: string[] - }[]> -``` - -- `types: string | string[]` - - **Required** - - The resource types. Example: `node--article` or `["taxonomy_term--tags", "user--user"]`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Examples - -- When used in `app/[...slug]/page.tsx` the `[...slug]` Dynamic Segment indicates that - `generateStaticParams()` is expected to return an array of objects with a `slug` property - containing an array of strings. - -```ts -export async function generateStaticParams(): Promise { - const resources = await drupal.getResourceCollectionPathSegments([ - "node--page", - "node--article", - ]) - - return resources.map((resource) => { - return { - slug: resource.segments, - } - }) -} -``` - -- When used in `app/[slug]/blog/[category]/[...slug]/page.tsx` file, `generateStaticParams()` - is expected to return an array of objects with a `lang` string, a `category` string and a `slug` array of strings. - -````ts -export async function generateStaticParams(): Promise { - const resources = await drupal.getResourceCollectionPathSegments( - ["node--article"], - { - // The pathPrefix will be removed from the returned path segments array. - pathPrefix: "/blog", - // The list of locales to return. - locales: ["en", "es"], - // The default locale. - defaultLocale: "en", - } - ); - - return resources.map((resource) => { - // NOTE: Because `pathPrefix` was set to "/blog", - // "blog" will not be included in the segments array. - - // Grab the first item from the segments array to get - // the needed "category" param. - const category = resource.segments.unshift(); - - return { - lang: resource.locale, - category, - slug: resource.segments, - }; - }) -}``` -```` diff --git a/www/content/docs/reference/getresourcefromcontext.mdx b/www/content/docs/reference/getresourcefromcontext.mdx deleted file mode 100644 index f317f7016..000000000 --- a/www/content/docs/reference/getresourcefromcontext.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: getResourceFromContext -excerpt: Fetch a resource from getStaticProps or getServerSideProps context. ---- - -```ts -const resource = await drupal.getResourceFromContext( - input, - context, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - pathPrefix, - isVersionable, - } -): Promise -``` - -- `input: string | DrupalTranslatedPath` - - **Required** - - The resource type `node--article` or the translated path from `translatePathFromContext`. -- `context: GetStaticPropsContext | GetServerSidePropsContext` - - **Required** - - The context from `getStaticProps` or `getServerSideProps`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the resource in. - - `defaultLocale: string`: The default locale of the site. - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - - `isVersionable: boolean`: Set to true if you're fetching the revision for a resource. _Automatically set to true for node entity types_. - ---- - -## Notes - -- The localized resource will be fetched based on the `locale` and `defaultLocale` values from `context`. - -- If you pass in a `DrupalTranslatedPath` for input, `getResourceFromContext` will take the `type` and `id` from the path and make a `getResource` call to Drupal. - -```ts -export async function getStaticProps(context) { - const path = await drupal.translatePathFromContext(context) - - const node = await drupal.getResourceFromContext(path, context) - - return { - props: { - node, - }, - } -} -``` - -- If you pass in a `string` inout, such as `node--article`, `getResourceFromContext` will make a subrequest call to Drupal to translate the path and then fetch the resource. - - You will need both the [Subrequests](https://drupal.org/project/subrequests) and [Decoupled Router](https://drupal.org/project/decoupled_router) modules. - -```ts -export async function getStaticProps(context) { - const node = await drupal.getResourceFromContext("node--article", context) - - return { - props: { - node, - }, - } -} -``` - ---- - -## Examples - -- Fetch a resource from context. - -```ts title=pages/[[...slug]].tsx -export async function getStaticProps(context) { - const node = await drupal.getResourceFromContext("node--page", context) - - return { - props: { - node, - }, - } -} -``` - -- Fetch a resource from context in a sub directory. - -```ts title=pages/articles/[[...slug]].tsx -export async function getStaticProps(context) { - const node = await drupal.getResourceFromContext("node--page", context, { - pathPrefix: "/articles", - }) - - return { - props: { - node, - }, - } -} -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const node = await drupal.getResourceFromContext( - "node--page", - context -) -``` - -- Using `DrupalTaxonomyTerm` for a taxonomy term entity type. - -```ts -import { DrupalTaxonomyTerm } from "next-drupal" - -const term = await drupal.getResourceFromContext( - "taxonomy_term--tags", - context -) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getsearch.mdx b/www/content/docs/reference/getsearch.mdx deleted file mode 100644 index 4eef21ad3..000000000 --- a/www/content/docs/reference/getsearch.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: getSearchIndex -excerpt: Fetch a Search API index. ---- - - - -You need to install the [JSON:API Search API](https://www.drupal.org/project/jsonapi_search_api) module to use `getSearchIndex`. - - - -```ts -const resources = await drupal.getSearchIndex( - name, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - } -): Promise -``` - -- `name: string` - - **Required** - - The name of the search index. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | DrupalClientAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the view in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Examples - -- Get search results from an index named `articles`. - -```ts -const results = await drupal.getSearchIndex("articles") -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const results = await drupal.getSearchIndex("articles") -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/getstaticpathsfromcontext.mdx b/www/content/docs/reference/getstaticpathsfromcontext.mdx deleted file mode 100644 index a0b7d4af7..000000000 --- a/www/content/docs/reference/getstaticpathsfromcontext.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: getStaticPathsFromContext -excerpt: Return paths for getStaticPaths. ---- - -```ts -const paths = await drupal.getStaticPathsFromContext( - types, - context, - options?: { - params, - withAuth, - pathPrefix, - } -): Promise["paths"]> -``` - -- `types: string | string[]` - - **Required** - - The resource types. Example: `node--article` or `["taxonomy_term--tags", "user--user"]`. -- `context: GetStaticPathsContext` - - **Required** - - The context from `getStaticPaths`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - ---- - -## Examples - -- Return static paths for `node--page` resources. - -```ts -export async function getStaticPaths(context) { - return { - paths: await drupal.getStaticPathsFromContext("node--page", context), - fallback: "blocking", - } -} -``` - -- Return static paths for `node--page` and `node--article` resources. - -```ts -export async function getStaticPaths(context) { - return { - paths: await drupal.getStaticPathsFromContext( - ["node--page", "node--article"], - context - ), - fallback: "blocking", - } -} -``` diff --git a/www/content/docs/reference/getview.mdx b/www/content/docs/reference/getview.mdx deleted file mode 100644 index f16eddeb6..000000000 --- a/www/content/docs/reference/getview.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: getView -excerpt: Fetch a view from Drupal. ---- - - - -You need to install the [JSON:API Views](https://www.drupal.org/project/jsonapi_views) module to use `getView`. - - - -```ts -const view = await drupal.getView( - name, - options?: { - params, - withAuth, - deserialize, - locale, - defaultLocale, - } -): Promise> -``` - -- `name: string` - - **Required** - - The name of the view and the display id. Example: "articles--promoted". -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | DrupalClientAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - - `locale: string`: The locale to fetch the view in. - - `defaultLocale: string`: The default locale of the site. - ---- - -## Examples - -- Get a view named `articles` and display id `promoted`. - -```ts -const view = await drupal.getView("articles--promoted") -``` - -- Using sparse fieldsets to only fetch the title and body fields. - -```ts -const view = await drupal.getView("articles--promoted", { - params: { - fields: { - "node--article": "title,body", - }, - }, -}) -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const view = await drupal.getView("articles--promoted") -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/reference/preview.mdx b/www/content/docs/reference/preview.mdx deleted file mode 100644 index f5ef8b677..000000000 --- a/www/content/docs/reference/preview.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: preview -excerpt: Handle preview mode for resources. ---- - -```ts -await drupal.preview(request, response, options?: { - errorMessages?: { - secret, - slug, - } -}): Promise -``` - -- `request: NextApiRequest` - - **Required** - - The `request` from an API route. -- `response: NextApiResponse` - - **Required** - - The `response` from an API route. -- `options` - - Optional - - `errorMessages`: - - `secret`: The error message to display for an invalid secret. - - `slug`: The error message to display for an invalid slug. - ---- - -## Notes - -- The `preview` method should be called in an API route. - ---- - -## Examples - -```ts title=pages/api/preview.ts -import { drupal } from "lib/drupal" - -export default async function handler(req, res) { - return await drupal.preview(req, res) -} -``` diff --git a/www/content/docs/reference/translatepath.mdx b/www/content/docs/reference/translatepath.mdx deleted file mode 100644 index 38d5b43ed..000000000 --- a/www/content/docs/reference/translatepath.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: translatePath -excerpt: Fetch info about a Drupal path. ---- - -```ts -const path = await drupal.translatePath( - path, - options?: { - withAuth, - } -): Promise -``` - -- `path: string` - - **Required** - - The resource path. Example: `/blog/slug-for-article`. -- `options` - - Optional - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - ---- - -## Notes - -- The [Decoupled Router](https://drupal.org/project/decoupled_router) module is required. - ---- - -## Examples - -- Get info about a `/blog/slug-for-article` path. - -```ts -const path = await drupal.translatePath("/blog/slug-for-article") -``` diff --git a/www/content/docs/reference/translatepathfromcontext.mdx b/www/content/docs/reference/translatepathfromcontext.mdx deleted file mode 100644 index 3d8d4c9e8..000000000 --- a/www/content/docs/reference/translatepathfromcontext.mdx +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: translatePathFromContext -excerpt: Fetch info about a Drupal path from getStaticProps or getServerSideProps context. ---- - -```ts -const path = await drupal.translatePathFromContext( - context, - options?: { - withAuth, - pathPrefix, - } -): Promise -``` - -- `context: GetStaticPropsContext | GetServerSidePropsContext` - - **Required** - - The context from `getStaticProps` or `getServerSideProps`. -- `options` - - Optional - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `pathPrefix: string`: Set the pathPrefix if you're calling from a subdir. Example: for `/articles/[...slug].tsx`, use `pathPrefix: "/articles"`. - ---- - -## Notes - -- The [Decoupled Router](https://drupal.org/project/decoupled_router) module is required. - ---- - -## Examples - -- Get info about a path from `getStaticProps` context. - -```ts -export async function getStaticProps(context) { - const path = await drupal.translatePathFromContext(context) -} -``` diff --git a/www/content/docs/reference/updateresource.mdx b/www/content/docs/reference/updateresource.mdx deleted file mode 100644 index e46cf8be8..000000000 --- a/www/content/docs/reference/updateresource.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: updateResource -excerpt: Update a resource. ---- - -```ts -const resource = await drupal.updateResource( - type, - uuid, - body, - options?: { - params, - withAuth, - deserialize, - } -): Promise -``` - -- `type: string` - - **Required** - - The resource type. Example: `node--article`, `taxonomy_term--tags`, or `block_content--basic`. -- `uuid: string` - - **Required** - - The resource id. Example `a50ffee7-ba94-46c9-9705-f9f8f440db94`. -- `body: JsonApiUpdateResourceBody` - - **Required** - - The body payload with `data`. -- `options` - - Optional - - `params: JsonApiParams`: JSON:API params such as `filter`, `fields`, `include` or `sort`. - - `withAuth: boolean | NextDrupalAuth`: - - Set the authentication method to use. See the [authentication docs](/docs/authentication). - - Set to `true` to use the authentication method configured on the client. - - `deserialize: boolean`: Set to false to return the raw JSON:API response. - ---- - -## Examples - -- Update a `node--page` resource. - -```ts -const page = await drupal.updateResource( - "node--page", - "a50ffee7-ba94-46c9-9705-f9f8f440db94", - { - data: { - attributes: { - title: "Updated Title", - }, - }, - } -) -``` - ---- - -## TypeScript - -- Using `DrupalNode` for a node entity type. - -```ts -import { DrupalNode } from "next-drupal" - -const page = await drupal.updateResource( - "node--page", - "a50ffee7-ba94-46c9-9705-f9f8f440db94", - { - data: { - attributes: { - title: "Updated Title", - }, - }, - } -) -``` - -See the [TypeScript docs](/docs/typescript) for more built-in types. diff --git a/www/content/docs/upgrade-guide.mdx b/www/content/docs/upgrade-guide.mdx index 49b2c9bf8..89a3a271c 100644 --- a/www/content/docs/upgrade-guide.mdx +++ b/www/content/docs/upgrade-guide.mdx @@ -1,8 +1,78 @@ --- title: Upgrade Guide -excerpt: Guide for upgrading your site to use DrupalClient. +excerpt: Upgrading your site to use the latest version of Next.js for Drupal. --- +## next-drupal 2.0.0 + +1. Upgrade to the latest version + +```sh +yarn add next-drupal@latest +``` + + + +`next-drupal 2.0.0` adds support for the App Router and maintains support for the Pages Router. For information on transitioning from the Pages Router to the App Router, see the [App Router Incremental Adoption Guide](https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration) guide on the Next.js site. + + + +Breaking changes: + +- When Drupal uses the Preview Url and the Revalidate Url, the "slug" search param + has been renamed to "path". +- The JsonApiWithAuthOptions type has been renamed to JsonApiWithAuthOption. The + JsonApiWithLocaleOptions type is now considered deprecated for DrupalClient + usage and can be replaced with the JsonApiOptions type. +- The forceIframeSameSiteCookie option to DrupalClient has been removed. The + DrupalClient now automatically makes this change when the Next.js website is + running in development mode (and the change is not needed for production sites). +- The options to the DrupalClient.preview() method were previously ignored. The + options are now passed to the NextApiResponse.setDraftMode() method and their + TypeScript definition have now changed to match the options parameter of the + setDraftMode method. +- DrupalClient previously had a debug property that indicated if debugging was + enabled. This property has been removed and replaced with a debug() method that + accepts a string and logs it as a debug message if debugging is enabled. +- The useMenu() client hook has moved out of the main entry point and into its own + entry point. Any import or require of that function needs to be updated: + +Old usage: + +```js +import { useMenu } from "next-drupal" +``` + +New usage: + +```js +import { useMenu } from "next-drupal/navigation" +``` + +## next 2.0.0 + +1. Upgrade your Drupal site to use `next 2.0.0` + +```sh +composer require drupal/next +``` + +2. Run updates: + +```sh +drush updb +``` + + + +Notable changes: + +- A new 'tag' On-demand validation plugin is available to configure for use with Next.js entity types. +- When Drupal uses the Preview Url and the Revalidate Url, the "slug" search param + has been renamed to "path". + + + ## next-drupal 1.6.0 1. Upgrade to the latest version diff --git a/www/content/features/authentication.mdx b/www/content/features/authentication.mdx index e1da32495..c3042f4b2 100644 --- a/www/content/features/authentication.mdx +++ b/www/content/features/authentication.mdx @@ -6,38 +6,29 @@ weight: 4 ```ts // Bearer token. -export const drupal = new DrupalClient( - process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, - { - auth: { - clientId: process.env.DRUPAL_CLIENT_ID, - clientSecret: process.env.DRUPAL_CLIENT_SECRET, - }, - } -) +export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { + auth: { + clientId: process.env.DRUPAL_CLIENT_ID, + clientSecret: process.env.DRUPAL_CLIENT_SECRET, + }, +}) ``` ```ts // Basic. -export const drupal = new DrupalClient( - process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, - { - auth: { - username: process.env.DRUPAL_USERNAME, - password: process.env.DRUPAL_PASSWORD, - }, - } -) +export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { + auth: { + username: process.env.DRUPAL_USERNAME, + password: process.env.DRUPAL_PASSWORD, + }, +}) ``` ```ts // Bring your own. -export const drupal = new DrupalClient( - process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, - { - auth: () => { - // Do something and return an Authorization header. - }, - } -) +export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { + auth: () => { + // Do something and return an Authorization header. + }, +}) ``` diff --git a/www/content/features/drupal-client.mdx b/www/content/features/drupal-client.mdx index 7f595f43e..bdd4dab56 100644 --- a/www/content/features/drupal-client.mdx +++ b/www/content/features/drupal-client.mdx @@ -5,12 +5,12 @@ weight: 0 --- ```ts -// Create a DrupalClient. -const drupal = new DrupalClient("http://drupal.org", { +// Create a NextDrupal Client. +const drupal = new NextDrupal("http://drupal.org", { auth: {} // Authentication fetcher: {} // Custom fetcher cache: {} // Cache support - serializer: {} // Custom serializer + deserializer: {} // Custom deserializer }) ``` diff --git a/www/content/tutorials/graphql/apply-patches.mdx b/www/content/tutorials/graphql/apply-patches.mdx index 43ad17a81..e75f0d00f 100644 --- a/www/content/tutorials/graphql/apply-patches.mdx +++ b/www/content/tutorials/graphql/apply-patches.mdx @@ -5,45 +5,35 @@ weight: 30 group: Quick Start (GraphQL) --- +At the time of this writing, a patch is required in order to use multilingual features such as translation. + -In a future release, these patches will NOT be required for using Next.js and GraphQL. +**Note:** If you're not using multilingual features such as translation, you can skip this step. -At the time of this writing, there are two patches we need to wire everything together. - 1. Open your Drupal `composer.json` file. -2. Add the following patches under `"extra"`. +2. Add the following patch under `"extra"`. ```json title=composer.json "extra": { // highlight-start "patches": { - "drupal/subrequests": { - "#3049395-47 - Page Cache causes different subrequests to return the same responses": "https://www.drupal.org/files/issues/2022-12-06/subrequests-3049395-chnage-request-type-47.patch" - }, "drupal/decoupled_router": { - "#3111456-59 - Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2022-12-01/decouple_router-3111456-resolve-language-issue-58--get-translation.patch" + "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2024-10-22/decoupled_router-3111456-resolve-langcode-issue-78--external-redirects.patch" } }, // highlight-end } ``` - - -**Note:** If you're not using multilingual features such as translation, you can skip the second patch. - - - 3. Run the following command: ```sh composer require cweagans/composer-patches ``` -You can read more about the patches here: +You can read more about the patch here: -1. https://www.drupal.org/project/subrequests/issues/3049395 2. https://www.drupal.org/project/decoupled_router/issues/3111456 diff --git a/www/content/tutorials/on-demand-revalidation/index.mdx b/www/content/tutorials/on-demand-revalidation/index.mdx index b6966acf1..9165758af 100644 --- a/www/content/tutorials/on-demand-revalidation/index.mdx +++ b/www/content/tutorials/on-demand-revalidation/index.mdx @@ -8,3 +8,7 @@ group: On-demand Revalidation [On-demand Revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation) makes it easy to update your Next.js site when content on Drupal is created, updated or deleted. Starting with `v1.6.0`, Next.js for Drupal supports On-demand Revalidation configurable per entity types. + +TODO - in v2, Next.js for Drupal supports... +TODO - maybe this section should just be renamed to validation? +TODO - maybe move revalidation examples from Building Pages to this section... diff --git a/www/content/tutorials/quick-start/apply-patches.mdx b/www/content/tutorials/quick-start/apply-patches.mdx index 94f384f37..8b836c1eb 100644 --- a/www/content/tutorials/quick-start/apply-patches.mdx +++ b/www/content/tutorials/quick-start/apply-patches.mdx @@ -5,39 +5,37 @@ weight: 30 group: Quick Start --- -At the time of this writing, there are two patches we need to wire everything together. +At the time of this writing, a patch is required in order to use multilingual features such as translation. + + + +**Note:** If you're not using multilingual features such as translation, you can skip this step. + + 1. Open your Drupal `composer.json` file. -2. Add the following patches under `"extra"`. +2. Add the following patch under `"extra"`. + +TODO - validate that these patches are necessary and correct. ```json title=composer.json "extra": { // highlight-start "patches": { - "drupal/subrequests": { - "Get same results on different request": "https://www.drupal.org/files/issues/2019-07-18/change_request_type-63049395-09.patch" - }, "drupal/decoupled_router": { - "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2022-11-30/decouple_router-3111456-resolve-language-issue-58.patch" + "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2024-10-22/decoupled_router-3111456-resolve-langcode-issue-78--external-redirects.patch" } }, // highlight-end } ``` - - -**Note:** If you're not using multilingual features such as translation, you can skip the second patch. - - - 3. Run the following command: ```sh composer require cweagans/composer-patches ``` -You can read more about the patches here: +You can read more about the patch here: -1. https://www.drupal.org/project/subrequests/issues/3049395 2. https://www.drupal.org/project/decoupled_router/issues/3111456 diff --git a/www/next.config.js b/www/next.config.js index 47b3c7b7f..3d9e3363b 100644 --- a/www/next.config.js +++ b/www/next.config.js @@ -102,6 +102,11 @@ module.exports = { destination: "/learn/on-demand-revalidation", permanent: true, }, + { + source: "/docs/api/:path*.mdx", + destination: "/docs/api/:path*", + permanent: true, + }, ] }, } diff --git a/www/package.json b/www/package.json index 6834ba166..1b11886a2 100644 --- a/www/package.json +++ b/www/package.json @@ -6,9 +6,10 @@ "main": "index.js", "license": "MIT", "scripts": { - "dev": "next dev -p 4444", - "build": "next build", - "preview": "next build && next start -p 4444" + "dev": "typedoc --tsconfig tsconfig.docs.json && next dev -p 4444", + "build": "typedoc --tsconfig tsconfig.docs.json && next build", + "preview": "typedoc --tsconfig tsconfig.docs.json && next build && next start -p 4444", + "generate:api-docs": "typedoc --tsconfig tsconfig.docs.json" }, "dependencies": { "@docsearch/react": "^3.6.0", @@ -23,7 +24,9 @@ "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "sharp": "^0.30.7" + "sharp": "^0.30.7", + "typedoc": "^0.27", + "typedoc-plugin-markdown": "^4.4" }, "devDependencies": { "@mapbox/rehype-prism": "^0.8.0", @@ -39,4 +42,4 @@ "tailwindcss": "^3.4.3", "typescript": "^5.4.5" } -} +} \ No newline at end of file diff --git a/www/pages/index.tsx b/www/pages/index.tsx index eacdb322f..7977838be 100644 --- a/www/pages/index.tsx +++ b/www/pages/index.tsx @@ -37,19 +37,15 @@ export default function IndexPage({ features }: IndexPageProps) {
- Next.js 13, Drupal 10 and Stable On-demand Revalidation + Next.js 15, Drupal 11 and App Router Support

- The future of Drupal is headless + Next.js for Drupal

-

- Next.js for Drupal has everything you need to build a - next-generation front-end for your Drupal site. -

-
-