Documentation
Feedback
Guides
App Development

App Development
App Development

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:

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:


_10
git 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.

node/clients/translation-graphql.ts

_21
import { AppGraphQLClient, InstanceOptions, IOContext } from '@vtex/api'
_21
_21
interface TranslatePayload {
_21
indexedByFrom: { from: string; messages: object[] }[];
_21
to: string;
_21
}
_21
_21
export default class TranslationGraphQL extends AppGraphQLClient {
_21
constructor(ctx: IOContext, options?: InstanceOptions) {
_21
super('vtex.messages@1', ctx, options)
_21
}
_21
_21
public translate = (args: TranslatePayload) => {
_21
return this.graphql.query({
_21
query: `query GetTranslation($args: TranslateArgs!) {
_21
translate(args: $args)
_21
}`,
_21
variables: { args },
_21
})
_21
}
_21
}

Some details about this implementation:

  1. 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 use vtex.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 the options parameter, such as authentication tokens in the headers for authenticated queries.
  2. Client functions: Create functions for each query and mutation you need. This example shows a simplified implementation to call the translate query. To make the query, use the function this.graphql.query with 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 object GetTranslation and use the $args variable 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 the translate query and the TranslateArgs! 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.args is used as the variable $args in the query.

If you want to use mutations, use the function this.graphql.mutate and insert the template string as the mutate argument'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:

node/clients/index.ts

_10
import { IOClients } from '@vtex/api'
_10
_10
import TranslationGraphQL from './translation-graphql'
_10
_10
export class Clients extends IOClients {
_10
public get translationGraphQL() {
_10
return this.getOrSet('translationGraphQL', TranslationGraphQL)
_10
}
_10
}

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.

node/middlewares/translateMessages.ts

_27
import { json } from 'co-body'
_27
_27
export async function translateMessages(ctx: Context, next: () => Promise<any>) {
_27
const {
_27
clients: { translationGraphQL }
_27
} = ctx
_27
_27
const requestBody = await json(ctx.req)
_27
const { from, messages, to } = requestBody
_27
_27
if (!from || !messages || !to) {
_27
ctx.status = 400
_27
ctx.body = { error: 'Missing required fields: from, messages, to' }
_27
return
_27
}
_27
_27
const translateArgs = {
_27
indexedByFrom: [{ from, messages }],
_27
to,
_27
}
_27
_27
const response = await translationGraphQL.translate(translateArgs)
_27
ctx.status = 200
_27
ctx.body = response
_27
_27
await next()
_27
}

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:

node/service.json

_10
{
_10
...
_10
"routes": {
_10
"translateMessages": {
_10
"path": "/_v/graphql-integration/translateMessages",
_10
"public": true
_10
}
_10
}
_10
}

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.

node/index.ts

_10
import { translateMessages } from './middlewares/translateMessages'
_10
_10
export default new Service({
_10
clients,
_10
routes: {
_10
translateMessages: method({
_10
POST: translateMessages,
_10
})
_10
},
_10
})

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:

vtex.messages/policies.json

_17
[
_17
...
_17
{
_17
"name": "graphql-translate-messages",
_17
"description": "Allows access the translation API",
_17
"statements": [
_17
{
_17
"actions": ["get", "post"],
_17
"effect": "allow",
_17
"resources": [
_17
"vrn:vtex.messages:{{region}}:{{account}}:{{workspace}}:/_v/graphql"
_17
]
_17
}
_17
]
_17
},
_17
...
_17
]

Add the policy to your app using the format "name": "{vendor}.{app-name}:{policy-name}". For the Messages app example, we use:

manifest.json

_10
"policies": [
_10
{
_10
"name": "vtex.messages:graphql-translate-messages"
_10
}
_10
]

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:


_10
curl --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
}

Contributors
1
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
Was this helpful?
Suggest edits (GitHub)
On this page