Closed demo-Ghost closed 2 years ago
Can you post the code or part of for the blog post page file?
I haven't changed anything there ([...slug].tsx), but let me get it for you.
import * as React from "react"
import Head from "next/head"
import {
getPathsFromContext,
getResourceFromContext,
getResourceTypeFromContext,
} from "next-drupal"
import { GetStaticPropsContext } from "next"
import { NodeArticle } from "@/nodes/node-article"
import { NodeBasicPage } from "@/components/nodes/node-basic-page"
/* eslint-disable @typescript-eslint/no-explicit-any */
interface NodePageProps {
preview: GetStaticPropsContext["preview"]
node: Record<string, any>
}
export default function NodePage({ node, preview }: NodePageProps) {
const [showPreviewAlert, setShowPreviewAlert] = React.useState<boolean>(false)
if (!node) return null
React.useEffect(() => {
setShowPreviewAlert(preview && window.top === window.self)
}, [])
return (
<>
<Head>
<title>{node.title}</title>
<meta
name="description"
content="A Next.js site powered by a Drupal backend."
/>
</Head>
{showPreviewAlert && (
<div className="fixed top-4 right-4">
<a
href="/api/exit-preview"
className="bg-black text-white rounded-md px-4 py-2 text-sm"
>
Exit preview
</a>
</div>
)}
{node.type === "node--page" && <NodeBasicPage node={node} />}
{node.type === "node--article" && <NodeArticle node={node} />}
</>
)
}
export async function getStaticPaths(context) {
const output = {
paths: await getPathsFromContext(["node--article", "node--page"], context),
fallback: true,
};
return output;
}
export async function getStaticProps(context) {
const type = await getResourceTypeFromContext(context);
if (!type) {
return {
notFound: true,
}
}
let params = {}
if (type === "node--article") {
params = {
include: "field_image,uid",
}
}
const node = await getResourceFromContext(type, context, {
params,
})
if (!node?.status) {
return {
notFound: true,
}
}
return {
props: {
preview: context.preview || false,
node,
},
revalidate: 60,
}
}
node
Is this happening in the Drupal preview only? What happens if you visit the post directly?
You mean from Next.js or in Drupal?
If I go to my Drupal site at localhost and click on the post on Homepage, I get this
I mean if you visit the page on the Next site
On the Next site, homepage for the default-starter works great. Plus, I see all the data of the collection.
but if I click on one of the posts, I get this
Just to add to this, although it might not be relevant, but my env variable for DRUPAL_FRONT_PAGE(which I really don't know what it does exactly) is /node.
I'm trying to reproduce this. Can you delete the .next
directory and try restarting the server again yarn dev
?
Let's also add a console.log(node)
in getStaticProps
to confirm if Next is able to pull the blog node:
export async function getStaticProps(context) {
const type = await getResourceTypeFromContext(context);
if (!type) {
return {
notFound: true,
}
}
let params = {}
if (type === "node--article") {
params = {
include: "field_image,uid",
}
}
const node = await getResourceFromContext(type, context, {
params,
})
console.log(node) // <----------- π
if (!node?.status) {
return {
notFound: true,
}
}
return {
props: {
preview: context.preview || false,
node,
},
revalidate: 60,
}
}
I deleted .next and restarted. The node is always null in the console and problem still persists.
I also deleted the next.js sites and pathauto patterns in Drupal yesterday and re-created them in case something was wrong there, but I think the peoblem is on the Next side of things.
Two more things to mention:
Do you think this could be an issue related to decoupled routing?
I did some debugging in your source code, adding some console logs in buildUrl and getResourceByPath functions, because I think this has to do with the Url Aliases and Routing. As I mentioned before, the issue is not when fetching a collection of posts, but an individual post, at [...slug].jsx (check basic-starter source code).
What I get is this (sorry for this awful spaghetti sequence :) )
event - build page: /[...slug]
wait - compiling...
event - compiled successfully
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/jsonapi',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/jsonapi',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/jsonapi',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/jsonapi',
search: '',
searchParams: URLSearchParams {},
hash: ''
}
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/jsonapi/node/page?fields%5Bnode--page%5D=path&fields%5Bnode--article%5D=path',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/jsonapi/node/page',
search: '?fields%5Bnode--page%5D=path&fields%5Bnode--article%5D=path',
searchParams: URLSearchParams { 'fields[node--page]' => 'path', 'fields[node--article]' => 'path' },
hash: ''
}
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/jsonapi/node/article?fields%5Bnode--article%5D=path',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/jsonapi/node/article',
search: '?fields%5Bnode--article%5D=path',
searchParams: URLSearchParams { 'fields[node--article]' => 'path' },
hash: ''
}
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/router/translate-path?path=blog%2Fsecond-article',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/router/translate-path',
search: '?path=blog%2Fsecond-article',
searchParams: URLSearchParams { 'path' => 'blog/second-article' },
hash: ''
}
-
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/subrequests?_format=json',
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/subrequests',
search: '?_format=json',
searchParams: URLSearchParams { '_format' => 'json' },
hash: ''
}
-
url in getResourceByPath http://localhost/drupal-next/web/subrequests?_format=json
-
response data in getResourceByPath {
resolved: 'http://localhost/blog/second-article',
isHomePath: false,
entity: {
canonical: 'http://localhost/blog/second-article',
type: 'node',
bundle: 'article',
id: '2',
uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
},
label: 'Second Article',
jsonapi: {
individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
resourceName: 'node--article',
pathPrefix: 'jsonapi',
basePath: '/jsonapi',
entryPoint: 'http://localhost/jsonapi'
},
meta: {
deprecated: {
'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
}
}
}
Everything looks good, until you check the last console output
response data in getResourceByPath {
resolved: 'http://localhost/blog/second-article',
isHomePath: false,
entity: {
canonical: 'http://localhost/blog/second-article',
type: 'node',
bundle: 'article',
id: '2',
uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
},
label: 'Second Article',
jsonapi: {
individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
resourceName: 'node--article',
pathPrefix: 'jsonapi',
basePath: '/jsonapi',
entryPoint: 'http://localhost/jsonapi'
},
meta: {
deprecated: {
'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
}
}
}
The URL is wrong there for jsonapi.individual, it shouldn't be http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857.
I'm not sure if this is some config thing in Drupal, or some config in Next.js. Plus, I also configured my url to be localhost/drupal-next/web after suspecting that it could be an issue with path aliases and also did a clean Drupal install from scratch. Initially, I had Drupal configured at plain http://localhost with some Apache settings (no drupal-next/web), but removed those settings, just to make sure I didn't mess with the paths.
The error in this Github Issue title,
TypeError: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object
comes from giving the wrong input to a function, it expects a string or buffer, but instead gets an object
{ message: "Invalid slug" }
from within next-drupal source code.
Few things I've found that may help guide you @shadcn.
I managed to reproduce the issue of a 404 appearing on individual slugs. I was able to rectify the issue by adding 'deserialize: false' to the options parameter of 'getResourceFromContext'.
In the getResourceByPath function, if data is serialised it will return undefined. I have not been able to determine why.
This then returns the data back to the getStaticProps function, however the data does not contain a node.status (edit: I imagine this is likely due to deserialize not being run), so it will return notFound: true
Removing this check will create a successful page load.
Obviously this is not ideal and the checks are there for a reason but hopefully this will provide some context in order for you to further investigate.
Edit: Apologies for all the edits.
Seems there's one more thing I forgot to mention. Setting deserialize to false is not enough, this line here also seems to cause null to be returned.
@demo-Ghost @rilrom Thanks for the detailed info. Is there anyway you can help me reproduce this (minimal reproducible repo)?
@shadcn In my case, this is a local environment in Windows, with Xampp. Other than that, just follow the exact instructions on Quick Start guide https://next-drupal.org/docs/quick-start. That's about it I think, the repo is the same as the basic starter.
I am experiencing the same phenomenon.
TypeError [ERR_INVALID_ARG_TYPE]: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object
at new NodeError (node:internal/errors:371:5)
at Function.byteLength (node:buffer:733:11)
at ServerResponse.apiRes.end (/opt/preview/.docker/preview/node_modules/next/dist/server/api-utils.js:72:41)
at /opt/preview/.docker/preview/node_modules/next-drupal/dist/index.js:593:30
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Object.apiResolver (/opt/preview/.docker/preview/node_modules/next/dist/server/api-utils.js:101:9)
at async Server.handleApiRequest (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:770:9)
at async Object.fn (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:661:37)
at async Router.execute (/opt/preview/.docker/preview/node_modules/next/dist/server/router.js:205:32)
at async Server.run (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:841:29) {
code: 'ERR_INVALID_ARG_TYPE'
The version I am using is as follows.
next-drupal: 0.16.2
next.js
(Drupal Module): 1.0.0-beta5I refer to this code, On the Drupal side, the page loaded successfully by applying the following patch to the subrequests module.
"patches": {
+ "drupal/subrequests": {
+ "Subrequest failed validation": "https://www.drupal.org/files/issues/2019-05-27/3029570-array-not-object.patch",
+ "Get same results on different request": "https://www.drupal.org/files/issues/2019-07-18/change_request_type-63049395-09.patch"
+ }
},
Just thinking out loud here.
It looks like buildUrl
is correctly returning the url but the response from decoupled router has the wrong base url.
url in buildUrl URL {
href: 'http://localhost/drupal-next/web/subrequests?_format=json', <----------------- β
origin: 'http://localhost',
protocol: 'http:',
username: '',
password: '',
host: 'localhost',
hostname: 'localhost',
port: '',
pathname: '/drupal-next/web/subrequests',
search: '?_format=json',
searchParams: URLSearchParams { '_format' => 'json' },
hash: ''
}
-
url in getResourceByPath http://localhost/drupal-next/web/subrequests?_format=json
-
response data in getResourceByPath {
resolved: 'http://localhost/blog/second-article', <----------------- β
isHomePath: false,
entity: {
canonical: 'http://localhost/blog/second-article', <----------------- β
type: 'node',
bundle: 'article',
id: '2',
uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
},
label: 'Second Article',
jsonapi: {
individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
resourceName: 'node--article',
pathPrefix: 'jsonapi',
basePath: '/jsonapi',
entryPoint: 'http://localhost/jsonapi' <----------------- β
},
meta: {
deprecated: {
'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
}
}
}
I'll looking into the Drupal/Decoupled Router end.
Aha
From \Drupal\decoupled_router\EventSubscriber\RouterPathTranslatorSubscriber::onPathTranslation
:
$path = $event->getPath();
$path = $this->cleanSubdirInPath($path, $event->getRequest());
/**
* Removes the subdir prefix from the path.
*
* @param string $path
* The path that can contain the subdir prefix.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request to extract the path prefix from.
*
* @return string
* The clean path.
*/
protected function cleanSubdirInPath($path, Request $request) {
// Remove any possible leading subdir information in case Drupal is
// installed under http://example.com/d8/index.php
$regexp = preg_quote($request->getBasePath(), '/');
return preg_replace(sprintf('/^%s/', $regexp), '', $path);
}
Trying to figure out why we're doing this now.
Hello again @shadcn . I'm trying to think why you could have done this. Something that seemed weird to me, is that URL Aliases at '/admin/config/search/path', would always show the full url path, regardless of where Drupal is served from. Even if served directly at http://localhost, the URL Aliases would include /drupal/web in the url.
This is coming from Drupal. Been a while since I used Drupal in a subdir path. I can't remember if there was a config somewhere to overwrite this.
You're saying that you're running the site at http://localhost
and Drupal is still showing http://localhost/drupal/web
in the admin?
No, it shows http://localhost/drupal/web
only in the URL aliases screen, the one in the screenshot. The url in the browser is correct, http://localhost
.
My Drupal installation is in a subfolder ([web server root]/drupal/web) and then I configure Apache .htaccess files to serve it from http://localhost
, using this doc https://www.drupal.org/forum/support/post-installation/2016-06-22/d8-how-to-use-drupal-8-in-a-subfolder (check IntraFusion's comment).
I faced the same issue (Drupal 9.2). As per @taku3202 comment, applying Drupal Subrequests patches solved it :
@demo-Ghost Did you figure this out?
I'm getting the same error in preview. My Drupal site base URL is http://localhost:8080/blog/drupal/web
EDIT: I've followed exactly the quick start https://next-drupal.org/learn/quick-start path
Also, applying the patches from https://github.com/chapter-three/next-drupal/issues/26#issuecomment-945509653 didn't help
Just for debugging I've replaced the RouterPathTranslatorSubscriber::cleanSubdirInPath()
method (see https://github.com/chapter-three/next-drupal/issues/26#issuecomment-950711871) with:
protected function cleanSubdirInPath($path, Request $request) {
// Remove any possible leading subdir information in case Drupal is
// installed under http://example.com/d8/index.php
if (strpos($path, '/blog/drupal/web/') === 0) {
$path = substr($path, strlen('/blog/drupal/web/'));
}
return $path;
}
...and it fixes the issue.
Note that I've hardcoded my base path (/blog/drupal/web/
) because it seems that I couldn't get it from request. What comes from $request->getBasePath()
is an empty string. Same is stored in $GLOBALS['base_path']
.
However, we need a permanent fix. I see there is https://www.drupal.org/project/decoupled_router/issues/3133873 but the patch over there is not resolving the issue.
So this works but don't ask my why :) The Symfony request stored in $GLOBALS['request']
contains the correct base path information. But I have no idea why they are different...
protected function cleanSubdirInPath($path, Request $request) {
// Remove any possible leading subdir information in case Drupal is
// installed under http://example.com/d8/index.php
$regexp = preg_quote($GLOBALS['request']->getBasePath(), '/');
return preg_replace(sprintf('/^%s/', $regexp), '', $path);
}
Ok, so I've reworked https://www.drupal.org/project/decoupled_router/issues/3133873. @demo-Ghost could you try to apply also the https://git.drupalcode.org/project/decoupled_router/-/merge_requests/3.diff patch to decoupled_router
module and see if fixes the issue? It does for me.
(However, still don't understand why, in cleanSubdirInPath()
, $request
and $GLOBALS['request']
are diverged)
I'll give it a shot @claudiu-cristea and I'll let you know.
@claudiu-cristea hi! I had the same issue described here, and your last patch fixed it!
Hi! Thanks for all the work you've put into this project. I'm following the quick start tutorial and have managed to set things up, after a some setbacks with simple_oauth library.
But, currently, I'm having an issue viewing my blog posts. I can see them at the home page and can also see that all their content is fetched in home page, as a collection. But once I visit a specific one (blog/first-post, as in blog/[node:title]), I get a 404 in the Network tab in Google Chrome.
And also this error.
I guess the following error is due to not getting any data in the response.