Shopify / shopify-app-template-remix

327 stars 137 forks source link

"host did not expose RPC" - @shopify/app-bridge-react Modal with remix-template, Bug #541

Closed muchisx closed 6 months ago

muchisx commented 7 months ago

Issue summary

Relevant logs / Code

My routing seems fine server-side:

18:24:23 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:23 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:23 │ remix      │ GET                                                                                                                                                                                                      
/app?embedded=1&hmac=327dacb6aa16f488e75911094a772048ff95f0698642349261c22cd4573c66d5&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN
0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MDYyNDciLCJleHAiOjE3MDcyNjE5MTYsIm5i
ZiI6MTcwNzI2MTg1NiwiaWF0IjoxNzA3MjYxODU2LCJqdGkiOiJhNjJhNTEyMS01YzJmLTQyZTUtYTA3NC01ZGNlMDdkMTczNjIiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiI0NDU0OGUxMDYxYmVjOGE5ZDEyNjVlOWY0YTk4ZDUzOWI5NTc4Njg3Y
WMxYzY2NDJhMTJhYTM3NWEyNDc5ODRmIn0.LeoWnESOVRBxuWNK3HcMWNhTxTTV8GvyZSE7nGLxEOE&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.myshopify.com&timestamp=1707261856 
200 - - 9.459 ms                                                                                                                                                                                                                 
18:24:23 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:23 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:23 │ remix      │ GET                                                                                                                                                                                                      
/app?embedded=1&hmac=327dacb6aa16f488e75911094a772048ff95f0698642349261c22cd4573c66d5&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN
0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MDYyNDciLCJleHAiOjE3MDcyNjE5MTYsIm5i
ZiI6MTcwNzI2MTg1NiwiaWF0IjoxNzA3MjYxODU2LCJqdGkiOiJhNjJhNTEyMS01YzJmLTQyZTUtYTA3NC01ZGNlMDdkMTczNjIiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiI0NDU0OGUxMDYxYmVjOGE5ZDEyNjVlOWY0YTk4ZDUzOWI5NTc4Njg3Y
WMxYzY2NDJhMTJhYTM3NWEyNDc5ODRmIn0.LeoWnESOVRBxuWNK3HcMWNhTxTTV8GvyZSE7nGLxEOE&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.myshopify.com&timestamp=1707261856 
200 - - 6.565 ms                                                                                                                                                                                                                 
18:24:25 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:25 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:25 │ remix      │ GET /app/tests?_data=routes%2Fapp 200 - - 3.555 ms                                                                                                                                                       
18:24:25 │ remix      │ GET /app/tests?_data=routes%2Fapp.tests 200 - - 565.857 ms
18:24:27 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:27 │ remix      │ GET /app/modal/create_category?embedded=1&hmac=327dacb6aa16f488e75911094a772048ff95f0698642349261c22cd4573c66d5&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsIn
R5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdW
IiOiIxMDgyMDk0MDYyNDciLCJleHAiOjE3MDcyNjE5MTYsIm5iZiI6MTcwNzI2MTg1NiwiaWF0IjoxNzA3MjYxODU2LCJqdGkiOiJhNjJhNTEyMS01YzJmLTQyZTUtYTA3NC01ZGNlMDdkMTczNjIiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiI0NDU
0OGUxMDYxYmVjOGE5ZDEyNjVlOWY0YTk4ZDUzOWI5NTc4Njg3YWMxYzY2NDJhMTJhYTM3NWEyNDc5ODRmIn0.LeoWnESOVRBxuWNK3HcMWNhTxTTV8GvyZSE7nGLxEOE&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflo
rist-dev-delta.myshopify.com&timestamp=1707261856 200 - - 14.184 ms                                                                                                                                                              
18:24:34 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:34 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:34 │ remix      │ GET /app/tests?embedded=1&hmac=f761a306fc1a1bb965769c954a92b51d0eac64cfa8cba409da08462b1574a865&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e
yJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MD
YyNDciLCJleHAiOjE3MDcyNjE5MzQsIm5iZiI6MTcwNzI2MTg3NCwiaWF0IjoxNzA3MjYxODc0LCJqdGkiOiJhYTllMzBiNS03MjVkLTQyOTMtOTM2OC04MTE0MTBiYjcxYWQiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiJiZWFjOGIwMDkyYTkxOTk
1MDE3NmZjOWM3MDAwNTRkZDg3NTg2NTAyOTc2Y2YxMDkyNWNkMWNlNTBkNTcxOTk4In0.vpq0v-lrcfeU_tKTmgshFX13wj5Y0L7mCnG_DPUwXZQ&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.m
yshopify.com&timestamp=1707261874 200 - - 408.841 ms
18:24:34 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:34 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:35 │ remix      │ GET /app/tests?embedded=1&hmac=f761a306fc1a1bb965769c954a92b51d0eac64cfa8cba409da08462b1574a865&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e
yJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MD
YyNDciLCJleHAiOjE3MDcyNjE5MzQsIm5iZiI6MTcwNzI2MTg3NCwiaWF0IjoxNzA3MjYxODc0LCJqdGkiOiJhYTllMzBiNS03MjVkLTQyOTMtOTM2OC04MTE0MTBiYjcxYWQiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiJiZWFjOGIwMDkyYTkxOTk
1MDE3NmZjOWM3MDAwNTRkZDg3NTg2NTAyOTc2Y2YxMDkyNWNkMWNlNTBkNTcxOTk4In0.vpq0v-lrcfeU_tKTmgshFX13wj5Y0L7mCnG_DPUwXZQ&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.m
yshopify.com&timestamp=1707261874 200 - - 406.955 ms
18:24:35 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:35 │ remix      │ [shopify-app/INFO] Authenticating admin request                                                                                                                                                          
18:24:35 │ remix      │ GET /app/tests?embedded=1&hmac=f761a306fc1a1bb965769c954a92b51d0eac64cfa8cba409da08462b1574a865&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e
yJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MD
YyNDciLCJleHAiOjE3MDcyNjE5MzQsIm5iZiI6MTcwNzI2MTg3NCwiaWF0IjoxNzA3MjYxODc0LCJqdGkiOiJhYTllMzBiNS03MjVkLTQyOTMtOTM2OC04MTE0MTBiYjcxYWQiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiJiZWFjOGIwMDkyYTkxOTk
1MDE3NmZjOWM3MDAwNTRkZDg3NTg2NTAyOTc2Y2YxMDkyNWNkMWNlNTBkNTcxOTk4In0.vpq0v-lrcfeU_tKTmgshFX13wj5Y0L7mCnG_DPUwXZQ&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.m
yshopify.com&timestamp=1707261874 200 - - 401.844 ms
18:24:35 │ remix      │ [shopify-app/INFO] Authenticating admin request
18:24:35 │ remix      │ GET /app/modal/create_category?embedded=1&hmac=f761a306fc1a1bb965769c954a92b51d0eac64cfa8cba409da08462b1574a865&host=bnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tL2FkbWlu&id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tXC9hZG1pbiIsImRlc3QiOiJodHRwczpcL1wvbnVmbG9yaXN0LWRldi1kZWx0YS5teXNob3BpZnkuY29tIiwiYXVkIjoiNWUwMDNmYjcyNWQzOGU1NTNlOTE4ODdiYmJkZjRhM2YiLCJzdWIiOiIxMDgyMDk0MDYyNDciLCJleHAiOjE3MDcyNjE5MzQsIm5iZiI6MTcwNzI2MTg3NCwiaWF0IjoxNzA3MjYxODc0LCJqdGkiOiJhYTllMzBiNS03MjVkLTQyOTMtOTM2OC04MTE0MTBiYjcxYWQiLCJzaWQiOiI3YmFjZjc4Mi04OWQyLTQzMDMtYTBlOS1jZmIxYTdlMzczNWQiLCJzaWciOiJiZWFjOGIwMDkyYTkxOTk1MDE3NmZjOWM3MDAwNTRkZDg3NTg2NTAyOTc2Y2YxMDkyNWNkMWNlNTBkNTcxOTk4In0.vpq0v-lrcfeU_tKTmgshFX13wj5Y0L7mCnG_DPUwXZQ&locale=en-US&session=c3c76c68261268f46eb8e7466de324400b576c3fea4fa6a160468f322273739c&shop=nuflorist-dev-delta.myshopify.com&timestamp=1707261874 200 - - 18.135 ms

My app.tsx:

import type { HeadersFunction, LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import {
  Link,
  Outlet,
  useLoaderData,
  useLocation,
  useRouteError,
  useNavigate,
} from '@remix-run/react';
import { Provider } from '@shopify/app-bridge-react';
import polarisStyles from '@shopify/polaris/build/esm/styles.css';
import { AppProvider } from '@shopify/shopify-app-remix/react';
import { boundary } from '@shopify/shopify-app-remix/server';
import { useMemo } from 'react';
import { authenticate } from '../shopify.server';

export const links = () => [{ rel: 'stylesheet', href: polarisStyles }];

export const loader = async ({ request }: LoaderFunctionArgs) => {
  await authenticate.admin(request);

  const url = new URL(request.url);
  const host = url.searchParams.get('host');

  return json({ apiKey: process.env.SHOPIFY_API_KEY || '', host });
};
export default function App() {
  const { apiKey, host } = useLoaderData<typeof loader>();

  const location = useLocation();
  const navigate = useNavigate();
  const history = useMemo(
    () => ({ replace: (path: string) => navigate(path, { replace: true }) }),
    [navigate]
  );
  const router = useMemo(
    () => ({
      location,
      history,
    }),
    [location, history]
  );

  return (
    <AppProvider isEmbeddedApp apiKey={apiKey}>
      <Provider
        config={{
          apiKey,
          host: host!,
        }}
        router={router}
      >
        {location.pathname.includes('modal') || (
          <ui-nav-menu>
            <Link to="/app">Dashboard</Link>
            {process.env.NODE_ENV === 'production' || <Link to="/app/tests">Tests</Link>}
          </ui-nav-menu>
        )}

        <Outlet />
      </Provider>
    </AppProvider>
  );
}

// Shopify needs Remix to catch some thrown responses, so that their headers are included in the response.
export function ErrorBoundary() {
  return boundary.error(useRouteError());
}

export const headers: HeadersFunction = (headersArgs) => boundary.headers(headersArgs);

My call to the modal (handling opening and closing state)

        <Modal
          src="/app/modal/create_category"
          open={openCreateModal}
          onClose={() => setOpenCreateModal(false)}
          title="Create Addon Category"
        />

My route app.modal.create_category,tsx

import type { ActionFunctionArgs } from '@remix-run/node';
import { json, useSubmit } from '@remix-run/react';
import {
  Button,
  TextField,
  Form,
  FormLayout,
  Select,
  Thumbnail,
  Card,
  Box,
} from '@shopify/polaris';
import { createAddonCategory } from '~/models/addonCategory/addonCategory.server';
import { authenticate } from '~/shopify.server';
import { useField, useForm, notEmpty, lengthMoreThan } from '@shopify/react-form';
import { Availability } from '@prisma/client';
import { useState } from 'react';
import type { Collection } from '@shopify/app-bridge-core/actions/ResourcePicker';

export const action = async ({ request }: ActionFunctionArgs) => {
  const { admin, session } = await authenticate.admin(request);
  const data = await request.json();

  const addonCategory = await createAddonCategory(
    {
      data: {
        ...data,
        shop: session.shop,
      },
    },
    admin.graphql
  );

  return json({ addonCategory });
};

export default function CreateAddonCategoryModal() {
  // const actionData = useActionData<typeof action>();
  const submit = useSubmit();

  const [selectedCollection, setSelectedCollection] = useState<Collection | null>(null);

  const availabilityOptions = Object.values(Availability).map((value) => ({
    value,
    label: value.toLowerCase(),
  }));

  const {
    fields: { name, availability, collectionID },
    submit: handleSubmit,
    submitting,
    // dirty,
    // reset,
    submitErrors,
    // makeClean,
  } = useForm({
    fields: {
      name: useField({
        value: '',
        validates: [
          notEmpty('Name is required'),
          lengthMoreThan(2, 'Title must be more than 2 characters'),
        ],
      }),
      availability: useField(availabilityOptions[0].value as string),
      collectionID: useField(''),
    },
    onSubmit: async (fieldValues) => {
      submit(fieldValues, { method: 'POST', encType: 'application/json', replace: true });

      let errors = [];
      if (errors.length > 0) {
        return { status: 'fail', errors: [{ message: 'bad form data' }] };
      }

      return { status: 'success' };
    },
  });

  const Loading = submitting ? <p className="loading">loading...</p> : null;
  const Errors =
    submitErrors.length > 0
      ? submitErrors.map((error) => (
          <p className="error" key={error.message}>
            {error.message}
          </p>
        ))
      : null;

  const handleSelectCollection = async () => {
    const selected = await shopify.resourcePicker({ type: 'collection' });

    if (selected) {
      collectionID.value = selected[0].id;
      setSelectedCollection(selected[0]);
    }
  };

  return (
    <Box padding="600">
      <Card padding="600">
        <Form onSubmit={handleSubmit}>
          <FormLayout>
            {Loading}
            <TextField label="Name" autoComplete="off" {...name} />
            <Select label="Availability" options={availabilityOptions} {...availability} />
            {selectedCollection && (
              <>
                {selectedCollection.image?.originalSrc && (
                  <Thumbnail
                    size="medium"
                    source={selectedCollection.image?.originalSrc}
                    alt={selectedCollection.image?.altText || ''}
                  />
                )}
                {selectedCollection.title}
              </>
            )}

            <Button variant="secondary" onClick={handleSelectCollection}>
              Select Collection
            </Button>
            <Button variant="primary" tone="success" submit>
              Create
            </Button>
            {Errors}
          </FormLayout>
        </Form>
      </Card>
    </Box>
  );
}

Expected behavior

Opening the modal shouldn't break the other App-bridge components

Actual behavior

When opening the modal I get this error in the console: Host did not expose RPC and after that the other elements of App Bridge dissapear (like in the video, where the titlebar just goes away)

Now, the modal works and the content within it works aswell. But I am required to refresh the page to get the App bridge TitleBar to show-up again because it breaks.

I have been trying to debug this for a longtime but havent been able to find a solution, anyone knows how to fix it ?

Part of the app-bridge components dissapear and stop working when i open a modal. I'm using the remix-template with @Shopify/app-bridge-react

https://github.com/Shopify/shopify-app-template-remix/assets/34131801/5a38c365-c438-42c0-a4ef-5692876c81aa

Steps to reproduce the problem

  1. Install @shopify/app-bridge-react
  2. Follow instructions to set-up Provider at the top of the app and pass the host
  3. Open a modal and see the error
matteodepalo commented 7 months ago

Hi @muchisx , thank you for opening this issue, we're looking into it.

muchisx commented 7 months ago

thank you @matteodepalo !

I'm sorry if its not the right repository, I searched a lot for the @shopify/app-bridge-react repository but seems like it's private or I just couldn't find it, I am eager to make an issue there if I mistakenly did it here in case you can show me where!

lizkenyon commented 6 months ago

Hi there @muchisx 👋

I am going to close this issue here, as I believe you have seen the release regarding the new App Bridge Modal on the App Bridge issue you referenced.

That repository should be the best repository for future questions about App Bridge components. But feel free to reach out here as well with future issues about the template. Thank you! :)