Invoke Forge Remote from the backend with all the auth bells and whistles the frontend has

Hi,

I’m working on a Forge app that uses a Forge Remote to provide data.

In the UI and through (scheduled) triggers you can make authenticated calls to the Forge Remote, but what if I need to make an authenticated call from within the Compass data-provider module?

In testing I found that just using the Fetch API results in 401 errors.

Looking at the Forge Remote Contract and RFC8 Forge Remote I don’t see anything related to this.

Is this something that can be added?
Or is there a workaround where I can call a method to add the authentication details to the Fetch API from the backend?

@AdamMoore I’m mentioned you as I connected with you at Team24 and because you are the author of the RFC

Some additional context: The app is for Compass and in the data-provider module you can opt to backfill data for a component. In my case the data needs to come from the Forge Remote, and since it is setup to support the Forge Remote authentication from the contract it would be nice to reuse this for the backfill of data as well.

3 Likes

Hey @markrekveld,

It wasn’t included in the original RFC, but it did come up in the comments and in many discussions with developers since.

So we have been working on Forge Functions to Remotes, and we’re just working on the final testing, docs, etc. It should be going out to Preview in the next couple of weeks.

There will be a new invokeRemote function added to @forge/api that you’ll be able to use from any backend function much like you can from the UI currently.

6 Likes

Hi @AdamMoore great thanks cannot wait.
Looking at the Trello board I saw a EAP release. Any chance I can be included in that?

Hey @markrekveld,

This is now available in preview, please refer to these docs for details on how to utilise Forge Remote within a backend function.

Please feel free to reach out to share any feedback (positive, negative or constructive) you have with this.

2 Likes

Thanks @SeanBourke I’ll be sure to take it for a spin and let you know how it goes.

1 Like

For those also giving this a try.
If you see this error:

INVALID_REMOTE_ERR: Invalid remote key provided: "remote-backend"
at handleResponseErrors (webpack://my-app/node_modules/@forge/api/out/api/remote.js:27:1)
at invokeRemote (webpack://my-app/node_modules/@forge/api/out/api/remote.js:20:1)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
status: 400,
remoteKey: 'remote-backend'
}

Add the compute operation to your remote for the function to work.

@SeanBourke the context object in the ForgeInvocationToken is empty. In other invocation the context look more like this:

{
  "localId": "",
  "cloudId": "",
  "environmentId": "",
  "environmentType": "",
  "moduleKey": "",
  "siteUrl": "",
  "extension": {
    "type": "",
    "componentId": ""
  }
}

Can the context be populated as well?

It would also be cool if the body element of the invoke option could take an object that would be auto mapped to json and have the content-type header also applied to the request to the remote. This works for the frontend to remote invocation.

2 Likes

Hi @SeanBourke

I’ve been playing around with the new feature and love it so far, and I only have 3 small feedback points:

  1. It was not clear to me that I needed to add the compute operation to my remote, it took me a bit of time to figure this out.
  2. The context of the ForgeInvocationToken is not populated like the other invocation to a remote are. I require this context so I can match the request to a tenant on the backend. I found a workaround, to pass the cloudId as a custom request header, but for consistency it would be great if this context matched the context given in other invocations
  3. The body element of the options given to tine invokeRemote function is currently limited to ArrayBuffer, ArrayBufferView, NodeJS.ReadableStream, string, URLSearchParams and FormData. It would save us on coding if this element would also accept any object that can be parsed to JSON. Now I need to also set the header next to the body like this to have my remote accept the call:
const res = await invokeRemote('remote-backend', {
    method: 'POST',
    path: '/compass/component/links',
    headers: {
        'x-cloud-id': context.cloudId,
        'content-type': 'application/json',
    },
    body: JSON.stringify({
        "id": component.id,
        "name": component.name,
    }),
});

Thanks again for the implementation, it made the code for my app a lot simpler.

1 Like

Hey @markrekveld,

Thanks for the feedback.

We hear you, we’ll explore ways to make this clearer within the documentation. We’ve included it in the example, but if you’re working with existing remotes, appreciate this is something which may be missed.

The backend should have access to that context and be able to pass it through, however appreciate this deviates somewhat from the front-end remote invocation experience. We’ll explore this further.

I’ve passed along this feedback as well.

Really glad to hear this and a big thank you for the thorough and thoughtful feedback.

2 Likes

Hey @SeanBourke

I have been keeping an eye on this because being able to invoke remote from the forge backend (eg. in an install event trigger or webtrigger) will also be very helpful for us too. Thank you for rolling out this feature so quickly and for keeping us updated!

I was initially having trouble getting this working, but it turns out I just needed to update the @forge/api dependency version. Everything is now working as expected :slight_smile:

1 Like