Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEXT-1204] opengraph-image can't be used to accept params in catch-all routes (Error: Catch-all must be the last part of the URL.) #49630

Closed
1 task done
timfee opened this issue May 10, 2023 · 10 comments
Assignees
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked Metadata Related to Next.js' Metadata API. Runtime Related to Node.js or Edge Runtime with Next.js.

Comments

@timfee
Copy link
Contributor

timfee commented May 10, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.4.0: Mon Mar  6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000
    Binaries:
      Node: 20.1.0
      npm: 9.6.4
      Yarn: N/A
      pnpm: 8.5.0
    Relevant packages:
      next: 13.4.1
      eslint-config-next: 13.4.1
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Metadata (metadata, generateMetadata, next/head, head.js), Middleware / Edge (API routes, runtime)

Link to the code that reproduces this issue

#48162

To Reproduce

I am trying to use opengraph-image.tsx inside of ./app/[[...path]] but get the Error: Catch-all must be the last part of the URL. message.

I saw a similar issue that was recently closed: #48162 - however I am trying to use the params to construct the image:

export default async function og({ params }: { params: { path: string[] } }) {
  console.log("Generating for", params)
  return new ImageResponse(...)
}

I wasn't sure based on the discussion in the related issue if this was WAI or slated for a future fix, so wanted to surface it in my case, where I needed to accept params. Thanks!

Describe the Bug

Cannot use opengraph-image inside of a [[...catchAll]] route; error is: Error: Catch-all must be the last part of the URL.

Expected Behavior

This functionality is allowed

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1204

@timfee timfee added the bug Issue was opened via the bug report template. label May 10, 2023
@github-actions github-actions bot added area: app App directory (appDir: true) Runtime Related to Node.js or Edge Runtime with Next.js. Metadata Related to Next.js' Metadata API. labels May 10, 2023
@timfee
Copy link
Contributor Author

timfee commented May 11, 2023

I was able to accomplish this with a workaround, albeit a bit hacky.

My directory format was ./[slug]/[password] - but I wanted to get away from having the params enumerated that way for greater flexibility (e.g. ./[slug]/[version]/[password])

So, I moved from
./app/[slug]/[password]/page.tsx to ./app/[[...path]]/page.tsx which caused this bug.

(It also worked with [...path] (required catch-all, too).

I ended up with this workaround.

  1. I set up a rewrite to match any URL :path* (that was missing the path query param).
  2. I redirected it to a root ./page.tsx?path=:path* handler.
  3. This would rewrite path to replace / with -
  4. Then, it would redirect to ./company/[path]
  5. And then I’d just transform the [path] to drop -s.
const nextConfig = {
  async rewrites() {
    return {
      afterFiles: [
        {
          source: "/:path([^.|company]*)*",
          destination: "/?path=:path*",
          missing: [
            {
              type: "query",
              key: "path",
            },
          ],
        },
      ],
    }
  },
}

There are possibly two other bugs here - which I'll open separately.

  1. It seems to ignore the missing path configuration (which is why I had to add company as an explicit omission).
  2. This was matching favicon.ico - which I didn't expect it to (given it was afterFiles)**, hence my omitting any URL fragments with a .

@shuding shuding added the linear: next Confirmed issue that is tracked by the Next.js team. label May 18, 2023
@shuding shuding changed the title opengraph-image can't be used to accept params in catch-all routes (Error: Catch-all must be the last part of the URL.) [NEXT-1204] opengraph-image can't be used to accept params in catch-all routes (Error: Catch-all must be the last part of the URL.) May 18, 2023
@huozhi
Copy link
Member

huozhi commented May 18, 2023

Does the solution in #48162 (comment) work for you? For now as it has to work as a route and receive params, it can't stay under a catch all routes.

@timfee
Copy link
Contributor Author

timfee commented May 18, 2023

Does the solution in #48162 (comment) work for you? For now as it has to work as a route and receive params, it can't stay under a catch all routes.

With ./(group)/opengraph-image.tsx and ./(group)/[...params]/page.tsx, opengraph-image.tsx wasn't getting any params passed to it.

I ended up refactoring the code to split out the params individually to work around this for now:

import { ImageResponse } from "next/server"
import { getData } from "@/lib/queries" // uses vercel/postgres

export const alt = "Hi there!"
export const size = {
  width: 1200,
  height: 630,
}
export const contentType = "image/png"
export const runtime = "edge"

export default async function handler({
  params,
}: {
  params: { slug: string; }
}) {
  if (!params || !params.slug || !params.code) {
    return
  }
  const { slug, code } = params

  //
  // just a sql query under the hood
  // using vercel/postgres (Edge-capable)
  //
  // note:
  // `code` is a hash for protected posts. 
  //
  const post = await getData(slug, code)

  const fontBold = await fetch(
    new URL("sans-extrabold.ttf", import.meta.url)
  ).then((response) => response.arrayBuffer())
  const fontMedium = await fetch(
    new URL("sans-medium.ttf", import.meta.url)
  ).then((response) => response.arrayBuffer())

  return new ImageResponse(
    (
      <div
        style={{
          background: "#f1f5f9",
          width: "100%",
          height: "100%",
          display: "flex",
          alignItems: "stretch",
        }}>
        <div
          style={{
            flexGrow: 1,
            borderRadius: "1rem",
            padding: "2rem",
            background: "#ffffff",
            display: "flex",
            flexDirection: "column",

            margin: "4rem",
          }}>
          <h1 tw="text-7xl font-bold text-red-800 mb-8">{post?.title}</h1>
          <p tw="text-5xl font-bold text-slate-900 mb-12">
            {post?.excerpt}
          </p>
      </div>
    ),
    {
      ...size,
      fonts: [
        {
          name: "Bold",
          data: fontBold,
          style: "normal",
          weight: 700,
        },
        {
          name: "Bold",
          data: fontMedium,
          style: "normal",
          weight: 400,
        },
      ],
    }
  )
}

@colbyfayock
Copy link
Contributor

colbyfayock commented May 24, 2023

the (group) solution seems to allow it to render the page/image for me, but i dont receive any params (which makes sense, its not in the catch all, but defeats the purpose for me)

image

@mbahArip
Copy link

I'm currently using this workaround to generate the opengraph with params.
Create a new routes that returning ImageResponse.
Then set the opengraph & twitter images metadata using generateMetadata function.

my app dir structure:
image

metadata on [...path]/page.tsx:
image

here are the result:
image
image

@colbyfayock
Copy link
Contributor

nice, that seemed to work well for me!

@huozhi
Copy link
Member

huozhi commented Jul 18, 2023

Going to close now as the catch-all routes is catching all the sub routes including metadata ones so not possible to match them and forward the params, can look for the above workarounds for now.

@huozhi huozhi closed this as completed Jul 18, 2023
@fermin-alganaras-isair
Copy link

I'm currently using this workaround to generate the opengraph with params. Create a new routes that returning ImageResponse. Then set the opengraph & twitter images metadata using generateMetadata function.

my app dir structure: image

metadata on [...path]/page.tsx: image

here are the result: image image

@mbahArip do you mind sharing the code inside the opengraph/[...slug]/route.tsx, I did the implementation you suggested but for me is not reaching the return image code, probably doing something slightly different, just not sure what

@mbahArip
Copy link

@mbahArip do you mind sharing the code inside the opengraph/[...slug]/route.tsx, I did the implementation you suggested but for me is not reaching the return image code, probably doing something slightly different, just not sure what

you can check the code here
Opengraph route

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked Metadata Related to Next.js' Metadata API. Runtime Related to Node.js or Edge Runtime with Next.js.
Projects
None yet
Development

No branches or pull requests

6 participants