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

Nuxt 3 Instantsearch SSR #132

Closed
kreejzak opened this issue Jan 17, 2023 · 28 comments · Fixed by #143
Closed

Nuxt 3 Instantsearch SSR #132

kreejzak opened this issue Jan 17, 2023 · 28 comments · Fixed by #143
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@kreejzak
Copy link

Hello,
I just wanted to ask if it is possible to use this module for Nuxt 3 Instantsearch with SSR?
I tried using <ais-instant-search-ssr> component, but it throws:

[nuxt] [request error] [unhandled] [500] createServerRootMixin is required when using SSR.                                                             

Also the Algolia's official Instantsearch example for Nuxt with SSR is only for Nuxt 2. So not much to learn there.

Is there any way to use Instantsearch with SSR, or should I use useAlgoliaFacetedSearch inside of useAsyncData for obtaining results and handle filtering manually with factes?

Thanks for your time!

@kreejzak kreejzak added the question Further information is requested label Jan 17, 2023
@Baroshem
Copy link
Collaborator

Hey,

Thanks for rising this question. Vue Instantsearch does not work with SSR by default. In order to make it a mixin (or something similar for Vue 3) would be needed.

The official docs are using Nuxt 2 as the module is maintained by me and help of contributors, not by Algolia itself. Thats is why the examples there are not up to date but I already contacted Algolia team to add my docs there.

For now, I would recommend you to use useFacetedSearch or regular useAsyncAlgoliaSearch to get the data in SSR while we can think about supporting SSR with Vue Instantsearch in future releases :)

@Baroshem
Copy link
Collaborator

For that, you can use https://algolia.nuxtjs.org/getting-started/usage#useasyncalgoliasearch and pass as a third property of the param object the requestOptions filters https://www.algolia.com/doc/api-reference/api-methods/search/#method-param-requestoptions

@kreejzak
Copy link
Author

Thank you for pointing me in the right direction! 🙏

@Baroshem Baroshem reopened this Jan 17, 2023
@Baroshem
Copy link
Collaborator

Baroshem commented Jan 17, 2023

Glad that I could help!

I will reopen the issue so that I could make some research about implementing the mixin to make Vue Instantsearch work with SSR :)

After all, it should work for SSR too so it is a valid feature request.

@Baroshem
Copy link
Collaborator

Hey @Haroenv

Long time no see 😃

Have you maybe found a way to implement instantsearch for Vue 3 with SSR? 🙂

@Haroenv
Copy link

Haroenv commented Jan 17, 2023

I tried this recently, but didn't manage to get reference to the component's render as you can do in non-setup style scripts. My attempt is here: algolia/doc-code-samples#430

@Baroshem
Copy link
Collaborator

Thanks @Haroenv I will take a look at it in a few days

@Baroshem
Copy link
Collaborator

Baroshem commented Mar 1, 2023

Havent managed to make it work either unfortunately. I think this can be an opportunity for some community contribution to this module as it is not very requested feature for all users but rather a nice to have

@Baroshem Baroshem added enhancement New feature or request help wanted Extra attention is needed and removed question Further information is requested labels Mar 1, 2023
@Strift
Copy link

Strift commented Mar 1, 2023

Hello there 👋

First, thanks a lot for your module and for sharing code samples. It was useful to look at the code, even though I'm not using Algolia. For the context, I'm using Meilisearch, which is an open source alternative. Meilisearch also uses InstantSearch components to implement the front-end.

I haven't yet come to a solution, but I've got a partially working sample. It might or might not be useful but I figured I would share it as your contributions helped me a lot in my work, so I hope I can return the favor.

Basically, my implementation works for server-side rendering the results, but not the other components that depends on the state (ie. filters, etc.) At least, that's my understanding of the behavior. Feel free to let me know if I'm wrong.

I made a local Nuxt module in one of the projects I'm working on, you can take a look here: https://github.com/meilisearch/ecommerce-demo/blob/main/components/organisms/MeiliSearchProvider.vue

EDIT: The link is now public as intended

Have a good day,

@Baroshem
Copy link
Collaborator

Baroshem commented Mar 2, 2023

Hey @Strift

Thank you for this message! I am glad that you liked the module :)

Also thanks for the research on how to implement this using SSR.

I am affraid that I do not have access to this project.

Would you be interested in contributing to Algolia module and adding such provider for it to make VueInstantsearch work with SSR? :)

@Bcavez
Copy link

Bcavez commented Mar 9, 2023

I tried this recently, but didn't manage to get reference to the component's render as you can do in non-setup style scripts. My attempt is here: algolia/doc-code-samples#430

Have you tried the getCurrentInstance function ?

EDIT:
Made it kinda work doing this:

Using the code from your pull request, I removed the search.vue component and put everything in the page.
Then I did this modification:

        app.ssrContext.payload.algolia = await instantsearch.findResultsState({
          // todo: what is the component? i don't see how to access the instance we currently are in
          component: getCurrentInstance(),
          // this relies on a patch in createServerRootMixin, as the component would be a child likely
          renderToString,
        }); 

The issue next was that the finderResultsState function (https://github.com/algolia/instantsearch/blob/master/packages/vue-instantsearch/src/util/createServerRootMixin.js) tries to access the instantsearch instance by doing this.instantsearch which doesn't work.
If I modify the file by doing this

created() {
                instance = component.provides.$_ais_ssrInstantSearchInstance;

                instance.start();

Then it works (I still get some hydration mismatch on the widgets, but the search results is good).
I just don't know how to make the finderResultsState function work properly.

@Strift
Copy link

Strift commented Mar 13, 2023

Hey there,

Sorry for the link to private repository earlier. 😓 It's now public.

I'm using a solution similar to Bcavez's. The search results are SSR'd, but not the rest of the UI (facets, etc.)

I'm guessing this is related to me not using instantsearch.findResultsState().

@Baroshem Unfortunately, I currently do not have the time to contribute. But I will make sure to share what I learn here!

@Baroshem
Copy link
Collaborator

Hey @Strift

Sorry for no contact from my side. I was off due to sick leave.

Thanks for that! I am currently working on other modules as this one is quite stable now and does not require that many new features. So, I am looking at this feature as an opportunity for an open source activity for contributors to implement.

@Laruxo
Copy link
Contributor

Laruxo commented Apr 3, 2023

After a long debugging session I managed to make finderResultsState work. I hope this helps anyone struggling with this.

  onBeforeMount(() => {
    // Use data loaded on the server
    if (data.value) {
      instantsearch.hydrate(data.value.algoliaState)
    }
  })

  const { data } = await useAsyncData('algolia-state', async () => {
    const algoliaState = await instantsearch.findResultsState({
      // IMPORTANT: a component with access to `this.instantsearch` to be used by the createServerRootMixin code
      component: {
        $options: {
          data() {
            return { instantsearch }
          },
          render() {
            h('div')
          },
        },
      },
      renderToString,
    })

    return { algoliaState }
  })

@Baroshem
Copy link
Collaborator

Baroshem commented Apr 3, 2023

Hey @Laruxo

Thanks for this piece of code. Would you be interested in contributing to the module by writing a docs section with this code sample? I am sure that several people could benefit from that :)

@Laruxo
Copy link
Contributor

Laruxo commented Apr 3, 2023

Hey @Baroshem, honestly, I am not using this module right now 😅 I think we should contribute to vue-instantsearch instead, it really needs to move away from that mixin. I will try to find some time for any of this a bit later. First, I really need to finish my migration to Nuxt 3 🙂

@Baroshem
Copy link
Collaborator

Baroshem commented Apr 4, 2023

@Laruxo

I see but you just provided a way how users can have instantsearch in Nuxt 3 with SSR.

If you don't want to, I can add this code snippet to the docs so the future users could more easily find the answer for that :)

Fixing this in instantsearch is a good idea as well!

@Laruxo
Copy link
Contributor

Laruxo commented Apr 4, 2023

@Baroshem got inspired this morning, so I created a PR for the docs 🙂

@Baroshem
Copy link
Collaborator

Baroshem commented Apr 4, 2023

Awesome!

I will review it in a few hours :)

@Haroenv
Copy link

Haroenv commented Apr 4, 2023

@Laruxo, the component won't have access to the child widgets though, which is needed to have routing and state synced 🤔

@Laruxo
Copy link
Contributor

Laruxo commented Apr 5, 2023

@Haroenv you are right, I've just encountered that. Now, I am having some errors when I use initialUiState. Do you have any ideas how I could work around it?

@Haroenv
Copy link

Haroenv commented Apr 5, 2023

The component you're mounting needs to be the component with all widgets mounted too.

@Laruxo
Copy link
Contributor

Laruxo commented Apr 6, 2023

So I managed to make it work for my project. For that, I needed to render AisRefinementList and AisSortBy components. I don't persist the state to the route for now, so I did not add the router.

@Haroenv do you know other cases for which we would need to add additional components?

  const { data: algoliaState, error } = await useAsyncData('algolia-state', async () => {
    return instantsearch.findResultsState({
      // IMPORTANT: fake component used by the createServerRootMixin code
      component: {
        $options: {
          components: { AisInstantSearchSsr, AisRefinementList, AisSortBy },
          data() {
            return { instantsearch }
          },
          provide: { $_ais_ssrInstantSearchInstance: instantsearch },
          render() {
            return h(AisInstantSearchSsr, null, () => [
              h(AisRefinementList, { attribute: 'metadata.languages.value' }),
              h(AisSortBy, { items: [{ value: indexName, label: '' }] }),
            ])
          },
        },
      },
      renderToString,
    })
  })

@Haroenv
Copy link

Haroenv commented Apr 6, 2023

This would make sense, but if you're going that far, you can take the component, put it in a separate file, and use that to both render the html, as well as the server side rendering

@Laruxo
Copy link
Contributor

Laruxo commented Apr 6, 2023

I tried that, but my components use Nuxt composables (e.g. useNuxtApp) which causes the SSR to fail (specifically instantsearch.findResultsState). Maybe someone with a better understanding of Vue/Nuxt internals could work around that.

@Bcavez
Copy link

Bcavez commented Apr 20, 2023

Sorry for posting in a closed issue but I cannot seem to make your solution work. Is there something missing in the snippet you provided @Laruxo ?

I'm getting this error:

[nitro] [dev] [unhandledRejection] <ref *1> ReferenceError: XMLHttpRequest is not defined        
    at eval (file:///home/projects/github-vukisz-jvkzve/algoliasearch/dist/algoliasearch-lite.esm.browser:818:39)
    at new Promise (<anonymous>)
    at Object.send (file:///home/projects/github-vukisz-jvkzve/algoliasearch/dist/algoliasearch-lite.esm.browser:817:20)
    at retry (file:///home/projects/github-vukisz-jvkzve/algoliasearch/dist/algoliasearch-lite.esm.browser:419:38)
    at eval (file:///home/projects/github-vukisz-jvkzve/algoliasearch/dist/algoliasearch-lite.esm.browser:432:16) {
  error: [Circular *1]
}

Here is a minimal repro: https://stackblitz.com/edit/github-vukisz-jvkzve?file=pages/search.vue

@Laruxo
Copy link
Contributor

Laruxo commented May 4, 2023

Sorry for the late reply @Bcavez. Your implementation seems correct, so I don't know why it does not work. The only strange thing that I can see from the error is the use of a browser build (algoliasearch-lite.esm.browser). In my case, we use a different build.

@Dante3003
Copy link

@Bcavez have you found any solution to this problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants