GraphQL image paths

Enonic version : 7.7.1
Mac Catalina: 10.15.7

Hello, i’m trying to do a headless implementation with NextJS and Enonic.
Which is working well, the only problem I have with fetching page content.
Is that images are returned as ID’s when i call:

query($path:ID){
guillotine{
get(key:$path) {
pageAsJson
}
}
}

Is there a way to retrieve the content of a page ( regions, components like a tree )
And with the image path’s included? So I won’t get a uuid as an image, but a path.
If anyone has an example query on how to do that, that would be great.

Kind regards,
Mark van der Steenhoven

Hi Mark.

Why would you want the image paths rather than IDs?
You should rather access the components directly. That way you can use the fully typed schema, and and generate image URLs directly… Image urls != internal image paths.

1 Like

Hey, thanks for responding!

The reason I want path’s instead of ID’s is that I don’t want to do a call per image to translate an ID to a path.

Pages have a layout, a layout has regions and regions contain parts.
I’d like this tree so I can render it with react, but i’ve been unable to find a way to write query in guillotine that does that.

When i access the components directly in guillotine, i get a flat array of the components it contains.
But not the structure of the page, this causes me to now know which part is in which region.
When I call pageAsJson, I get the structure of the page but not the image paths.

Also, before I retrieve a page, I know nothing about it, except that it’s a page since we use a router in nextjs that matches it’s path, with that of a page in enonic.

I hope you understand my problem. Thanks again for responding.

1 Like

The page tree can be composed from the flat list by using the component path field (which can also be used as an ID).

Still don’t understand what you need the image paths for? :thinking:

I’m guessing that the term “image path” as used in the original post refers to the URL to the rendered image after scaling and other processing by the image service, not the path in the content repo?

Hi, Marcel here. I’m a colleague of Mark and Mark has just gone on holiday, so I’ll respond.
I think we have a little communication error here, as we are trying to render the page in Next.js. We need the url’s to the images for that, to work completely. Our website is quite image heavy ( about 80%), as we have to make it attractive for people to play and show the charity work that is being done.

We currently have the components rendering, the text and layout blocks, based on the pageAsJson or the “content object json” shown in the Content Viewer. Unfortunately the image objects in that json only contain the Uuid’s not the url’s.

We want to avoid querying for the images by Uuid, because it would create a rather “chatty” application. The "chatty"ness could possibly be made worse by the multiple images for different screen sizes.

The situation is this. The site gets hit by millions of users, so there might be an impact if we get it wrong. There is also no leeway in the design, as the design is done by a pro design department and a content management team. In short, we have to get it pixel perfect and performing. That’s something that Enonic should be great at, so we are a bit puzzled.

Theoretically, I can think of 3 ways to get a json that can be used to render the site.
1 Write a GraphQL query that joins the components with the image urls. That would be a recursive as images can be in layouts, parts in layouts etc.
2 Change the GraphQL schema to automaticly do the join with the image url
3 Change the Enonic image components to have the url, but that would probably create caching problems.

We have used the GitHub - navikt/nav-enonicxp-frontend as an inspiration. Their Image component has an image url, but we suspect they did some magic on the Enonic back-end. We are sure willing to do that too, but need a little pointer in the right direction.

Edit: On further research I see that the Content View json and the pageAsJson are not exactly the same. Both are also missing the __typename property available in the GraphQL api and used to determine the layout in the nav app. These 3 things look similar, but are different.

1 Like

Looking at Site engine (enonic.com) the pageAsJson is probably the way it is because of the page renderer.
I can find the image objects matching the Uuid’s used in the pageAsJson in

  • references
  • children
  • components

Don’t know witch is the best one, but one of them must be good. (Please tell if one is better as the others)

I can get the imageUrl by using the

mediaUrl(download:false, type:absolute)

found here Accessing images - scaling - Help - Enonic Discuss

I can can convert the mediaUrl to an imageUrl by combining the xAsJson.media.imageInfo and the image scaling functions explained here Image service (enonic.com) .

Converting the mediaUrl to an imageUrl by hand seems brittle.
Is there an imageUrl version that automatically applies the scaling from the xAsJson?
Alternatively could there be a npm lib function that does that and we can include in Next.js?

Hi Marcel.

In general, pageAsJson is essentially “unstructured” and “dumb”. It creates resemblance with the JavaScript API, and a quick overview of the page setup, but for the headless usecase I would recommend using components instead. The reason is that the components are fully typed, and you can query to follow relationships, generate image urls, perform macro and image processing of rich text fields etc etc.

So, by specifying all components in your query, you simply have to stich them together in your rendering using the component paths, vs the recursive approach of pageAsJson.

The reason why components are flat like this is that we cannot provide a GraphQL schema for unstructured items - also this structure mimics what is actually stored and possible to query directly.

The only downside is that you must be careful when updating your components (schema) to avoid breaking your graphQL API - until clients (your nextJS app) are updated ofcourse.

If you would still want to use pageAsJson, you would have to perform additional queries to process your rich text, generate image urls and possibly fetch more data from references.

I guess there is a potential to offer generic fields for this in Guillotine, i.e. imageUrl(ids, params). processHtml(ids, params) etc in addition to the typed fields we use today. You can even extend the graphQL API with any such functionality yourself. In that case, you could for instance generate a buch of imageUrls with one request. But, again, this is not the recommended approach as using components directly is both more powerful, fast and flexible.

Hope this helps?

Thanks, this helps a lot.

I too have a preference for typed data, as I come from typed languages and it gives some guarantees. Unfortunately JavaScript is not strong on giving guarantees, although Typescript helps.

Recursion for SQL is well known and often used Recursion in SQL Explained Visually | Medium , but GraphQL doesn’t seem to like it How to model recursive data structures in GraphQL - Stack Overflow

The pageAsJson is probably created in a similar way, internally. Extending the GraphQL api sounds like an option we might consider. Although, wouldn’t extending the GraphQL api or Guillotine create the problem that we can’t upgrade anymore without losing our changes?

The API can safely be extended, you are not modifying the Guillotine library, just adding your own fields. Customizing the schema

Thanks. Good to know.
We’ll look into that.

Hey thanks for the response,

If we have to query all components, with all their config for every page.
Isn’t the query going be very big and allot of null values? I guess it’s doable, but it sounds like allot of work. That’s the thing I liked about pageAsJson, but I get that there is no way for enonic to get all extra data.

I do see a way to use /header/0/region1/0 as a tree structure.
Zo we could use these paths as a tree to know which components goes where.
This is what you are suggesting right?

Just a recap:

to get all components define all components and their entire schema for every page request and use the paths in those components to determine where they go.

Again thanks for responding!

Kind regards,
Mark van der Steenhoven

1 Like

Hi

I might have had the same “problem” as you, and I just want to mention that it is possible to get the image url by using the function imageUrl in the content of an image. Something like this:

query($path: ID!) {
  guillotine {
    get(key: $path) {
      _id
      type
     // pageAsJson
     // ...
      ... on no_myapp_app_Article {
        displayName
        article: data {
          topImage { // an "imageSelector" input type
            ... on media_Image {
              src: imageUrl(type: absolute, scale: "block(459,295)")
            }
          }
          text {
            processedHtml
          }
        }
      }
    }
  }
}
1 Like