Integrating an app with a GraphQL API
Learn how to query GraphQL APIs from an app.
When developing a VTEX IO app, you may need to interact with other apps that expose GraphQL APIs, such as retrieving data or triggering actions. To do this, you must implement a custom client that handles authenticated requests to the target app.
Once registered with the VTEX IO clients pattern (IOClients), the client becomes available throughout your app. You can then call it directly from resolvers or middleware functions to integrate external GraphQL capabilities into your service app.
Before you begin
Ensure you meet the technical requirements to follow this guide:
- VTEX IO CLI is installed on your computer.
- Access to a VTEX store with a development workspace and the Admin environment.
- Familiarity with GraphQL concepts, such as queries, schema, and resolvers.
- Experience developing in the TypeScript language.
Implementation
1. Starting with a template
We recommend cloning a VTEX IO app template to your computer if you are developing an app from scratch. In this tutorial, we will use the service-example template. Clone the repository using the following command in a terminal:
_10git clone https://github.com/vtex-apps/service-example
2. Implementing your custom client
VTEX IO provides AppGraphQLClient as a base class for making GraphQL requests between apps. Your custom client should extend this class, specifying the target app, which exposes the GraphQL API, in the super constructor. In this class, you must also define the methods you want to use in your middleware functions or resolvers.
In the following example, we define a client that uses the Messages app's translate query for string translations.
Some details about this implementation:
- Target app: In the client's constructor, define the app you want to query using the format
{vendor}.{app-name}@{version}. In this example, we usevtex.messages@1, specifying only the app's major version. You can also specify the exact version with minor and patch values (for example,vtex.messages@1.0.0). In this constructor, you can include other options in theoptionsparameter, such as authentication tokens in the headers for authenticated queries. - Client functions: Create functions for each query and mutation you need. This example shows a simplified implementation to call the
translatequery. To make the query, use the functionthis.graphql.querywith the following arguments:query: Insert the query here in a template string format, surrounded by backticks (`), which allows multi-line separation and usage of variables. In this example, we declare the GraphQL objectGetTranslationand use the$argsvariable as input. Notice that query variables start with the$symbol. You must use the same names for the queries and variables as in the schema definition. Here we use thetranslatequery and theTranslateArgs!type for the variable$args. You can find the type names in the app documentation (for example, Messages GraphQL API), using an Introspection Query, or, if you have access, in the app's source code.variables: Insert the query variables here as an object, where the property keys are the variable names in the query. In this example,variables.argsis used as the variable$argsin the query.
If you want to use mutations, use the function
this.graphql.mutateand insert the template string as themutateargument's value.
3. Registering the client
After you implement the client, register it in your app's list of clients. This step makes the client available in the context of your app's resolvers. To do this, open the node/clients/index.ts file, import your client, and add it to the Clients class, as shown in the following example:
4. Using the client
You can now use your custom client to request the target app from your app's handler functions by accessing it via context (ctx.clients).
In this example, we implement a REST API to access the translation query from the Messages app.
5. Defining the service route
We need to define a route, as we are implementing a REST API to access the middleware. Go to the node/service.json file, define a public route with the name of the middleware function, and set the path using the format "/_v/{app-name}/{middleware-name}". For this implementation, we use:
6. Relating a route to a middleware
Update the node/index.ts file to relate a route to a middleware. The routes section associates the translateMessages route with the translateMessages middleware function. The POST method is mapped to the translateMessages function. We use the POST method as we will send the content to be translated in the request body.
Therefore, when a POST request is made to the previously defined translateMessages route, it triggers the translateMessages function, which, in turn, executes the TranslationGraphQL client's translate method.
7. Add the policies
For your app to have permission to access any external resource, you must declare the related policies in your app's manifest.json file. In the case of a GraphQL API from another app, this is a role-based policy, so you must add the app name and policy name. You can find the policy name and description in the policies.json file from the app's source code.
Here is the role-based policy from the Messages app for its GraphQL API:
Add the policy to your app using the format "name": "{vendor}.{app-name}:{policy-name}". For the Messages app example, we use:
8. Link your app
To test your app, link it to the VTEX platform to enable it to run and accept requests.
Open a terminal and switch to a development workspace. Then, use the vtex link command.
9. Test your integration
With your app running, you can test the implemented integration. In our example, we implement a REST API in a service app linked to a middleware function. Therefore, we will call the endpoint that executes this function, which invokes the translation query.
Here is a cURL example to call the translation endpoint:
_10curl --request post \_10 --url 'https://{{workspace}}--{{accountname}}.myvtex.com/_v/graphql-integration/translateMessages' \_10 --header 'Accept: application/json' \_10 --header 'Content-Type: application/json' \_10 --header 'VtexIdclientAutCookie: {{vtex-cookie}}' \_10 --data '{"from": "en-US", "messages": [{"content": "shoe"}, {"content": "ball"}], "to": "pt-BR"}'
And we get the following response:
_10{_10 "data": {_10 "translate": [_10 "sapato",_10 "bola"_10 ]_10 }_10}