---
title: "Connect your app code to API"
section: "frontend/data"
platforms: ["android", "angular", "flutter", "javascript", "nextjs", "react", "react-native", "swift", "vue"]
gen: 2
last-updated: "2026-03-25T17:40:00.000Z"
url: "https://docs.amplify.aws/react/frontend/data/connect-to-API/"
---

In this guide, you will connect your application code to the backend API using the Amplify Libraries. Before you begin, you will need:

- Your cloud sandbox with an Amplify Data resource up and running (`npx ampx sandbox`)
- A frontend application set up with the Amplify library installed
- [npm installed](https://docs.npmjs.com/getting-started)

## Configure the Amplify Library

When you deploy you're iterating on your backend (`npx ampx sandbox`), an **amplify_outputs.json** file is generated for you. This file contains your API's endpoint information and auth configurations. Add the following code to your app's entrypoint to initialize and configure the Amplify client library:

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);
```

## Generate the Amplify Data client

Once the Amplify library is configured, you can generate a "Data client" for your frontend code to make fully-typed API requests to your backend.

> **Info:** **If you're using Amplify with a JavaScript-only frontend (i.e. not TypeScript), then you can still get a fully-typed data fetching experience by annotating the generated client with a JSDoc comment**. Select the **JavaScript** in the code block below to see how.

To generate a new Data client, use the following code:

#### [TypeScript]
```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

// Now you should be able to make CRUDL operations with the
// Data client
const fetchTodos = async () => {
  const { data: todos, errors } = await client.models.Todo.list();
};
```

#### [JavaScript]
```js
import { generateClient } from 'aws-amplify/data';

/**
 * @type {import('aws-amplify/data').Client<import('../amplify/data/resource').Schema>}
 */
const client = generateClient();

// Now you should be able to make CRUDL operations with the
// Data client
const fetchTodos = async () => {
  const { data: todos, errors } = await client.models.Todo.list();
};
```

<!-- /Platform -->

## Configure authorization mode

The **Authorization Mode** determines how a request should be authorized with the backend. By default, Amplify Data uses the "userPool" authorization which uses the signed-in user credentials to sign an API request. If you use a `allow.publicApiKey()` authorization rules for your data models, you need to use "apiKey" as an authorization mode. Review [Customize your auth rules](/[platform]/build-a-backend/data/customize-authz) to learn more about which authorization modes to choose for which type of request. A **Default Authorization Mode** is provided as part of the **amplify_outputs.json** that is generated upon a successful deployment.

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
You can generate different Data clients with different authorization modes or pass in the authorization mode at the request time.

### Set authorization mode on a per-client basis

To apply the same authorization mode on all requests from a Data client, specify the `authMode` parameter on the `generateClient` function.

#### [API Key]

Use "API Key" as your authorization mode when if defined the `allow.publicApiKey()` authorization rule.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>({
  authMode: 'apiKey',
});
```

#### [Amazon Cognito user pool]

Use "userPool" as your authorization mode when using Amazon Cognito user pool-based authorization rules, such as `allow.authenticated()`, `allow.owner()`, `allow.ownerDefinedIn()`, `allow.groupsDefinedIn()`, or `allow.groups()`.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>({
  authMode: 'userPool',
});
```

#### [AWS IAM (including Amazon Cognito identity pool roles)]

Use "identityPool" as your authorization mode when using Amazon Cognito identity pool-based authorization rules, such as `allow.guest()` or `allow.authenticated('identityPool')`.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>({
  authMode: 'identityPool',
});
```

#### [OpenID Connect (OIDC)]
Use "oidc" as your authorization mode when connecting applications to a trusted identity provider. Private, owner, and group authorization can be configured with an OIDC authorization mode. Review the [OIDC authorization docs](/[platform]/build-a-backend/data/customize-authz/using-oidc-authorization-provider) to learn more.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>({
  authMode: 'oidc',
});
```

#### [Lambda Authorizer]

Use "Lambda Authorizer" when using your own custom authorization logic via `allow.custom()`. Review [Customize your auth rules](/[platform]/build-a-backend/data/customize-authz) to learn more about how to implement your authorization protocol.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const getAuthToken = () => 'myAuthToken';
const lambdaAuthToken = getAuthToken();

const client = generateClient<Schema>({
  authMode: 'lambda',
  authToken: lambdaAuthToken,
});
```

<!-- /Platform -->

### Set authorization mode on the request-level

You can also specify the authorization mode on each individual API request. This is useful if your application typically only uses one authorization mode with a small number of exceptions.

#### [API Key]

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
const { data: todos, errors } = await client.models.Todo.list({
  authMode: 'apiKey',
});
```
<!-- /Platform -->

<!-- Platform: android -->
```kt
val query = ModelQuery.list(Todo::class.java) as AppSyncGraphQLRequest<PaginatedResult<Todo>>
val apiKeyQuery = query
    .newBuilder()
    .authorizationType(AuthorizationType.API_KEY)
    .build<PaginatedResult<Todo>>()
Amplify.API.query(apiKeyQuery,
    { Log.i("MyAmplifyApp", "Queried with API key ${it.data}")},
    { Log.e("MyAmplifyApp", "Error querying with API Key")})
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
let result = try await Amplify.API.query(
    request: .list(
        Todo.self,
        authMode: .apiKey))
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
final apiKeyRequest = ModelQueries.list(Todo.classType, authorizationMode: APIAuthorizationType.apiKey);
final apiKeyResponse = await Amplify.API.query(request: apiKeyRequest).response;
```
<!-- /Platform -->

#### [Amazon Cognito user pool]

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
const { data: todos, errors } = await client.models.Todo.list({
  authMode: 'userPool',
});
```
<!-- /Platform -->

<!-- Platform: android -->
```kt
val query = ModelQuery.list(Todo::class.java) as AppSyncGraphQLRequest<PaginatedResult<Todo>>
val userPoolQuery = query
  .newBuilder()
  .authorizationType(AuthorizationType.AMAZON_COGNITO_USER_POOLS)
  .build<PaginatedResult<Todo>>()
Amplify.API.query(userPoolQuery,
  { Log.i("MyAmplifyApp", "Queried with Cognito user pool ${it.data}")},
  { Log.e("MyAmplifyApp", "Error querying with Cognito user pool")})
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
let result = try await Amplify.API.query(
    request: .list(
        Todo.self,
        authMode: .amazonCognitoUserPools))
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
final userPoolRequest = ModelQueries.list(Todo.classType, authorizationMode: APIAuthorizationType.userPools);
final userPoolResponse = await Amplify.API.query(request: userPoolRequest).response;
```
<!-- /Platform -->

#### [AWS IAM (including Amazon Cognito identity pool roles)]

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
const { data: todos, errors } = await client.models.Todo.list({
  authMode: 'identityPool',
});
```
<!-- /Platform -->

<!-- Platform: android -->
```kt
val query = ModelQuery.list(Todo::class.java) as AppSyncGraphQLRequest<PaginatedResult<Todo>>
val iamQuery = query
    .newBuilder()
    .authorizationType(AuthorizationType.AWS_IAM)
    .build<PaginatedResult<Todo>>()
Amplify.API.query(iamQuery,
    { Log.i("MyAmplifyApp", "Queried with AWS IAM ${it.data}")},
    { Log.e("MyAmplifyApp", "Error querying with AWS IAM")})
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
let result = try await Amplify.API.query(
    request: .list(
        Todo.self,
        authMode: .awsIAM))
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
final iamRequest = ModelQueries.list(Todo.classType, authorizationMode: APIAuthorizationType.iam);
final iamResponse = await Amplify.API.query(request: iamRequest).response;
```
<!-- /Platform -->

#### [OpenID Connect (OIDC)]

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
const { data: todos, errors } = await client.models.Todo.list({
  authMode: 'oidc',
});
```
<!-- /Platform -->

<!-- Platform: android -->
```kt
val query = ModelQuery.list(Todo::class.java) as AppSyncGraphQLRequest<PaginatedResult<Todo>>
val oidcQuery = query
    .newBuilder()
    .authorizationType(AuthorizationType.OPENID_CONNECT)
    .build<PaginatedResult<Todo>>()
Amplify.API.query(oidcQuery,
    { Log.i("MyAmplifyApp", "Queried with OIDC authorization mode ${it.data}")},
    { Log.e("MyAmplifyApp", "Error querying with OIDC authorization mode")})
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
let result = try await Amplify.API.query(
    request: .list(
        Todo.self,
        authMode: .openIDConnect))
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
final oidcRequest = ModelQueries.list(Todo.classType, authorizationMode: APIAuthorizationType.oidc);
final oidcResponse = await Amplify.API.query(request: oidcRequest).response;
```
<!-- /Platform -->

#### [Lambda Authorizer]

You can implement your own custom API authorization logic using a AWS Lambda function. Review [Customize your auth rules](/[platform]/build-a-backend/data/customize-authz) to learn more about how to implement your authorization protocol with AWS Lambda.

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
```ts
const getAuthToken = () => 'myAuthToken';
const lambdaAuthToken = getAuthToken();

const { data: todos, errors } = await client.models.Todo.list({
  authMode: 'lambda',
  authToken: lambdaAuthToken,
});
```
<!-- /Platform -->

<!-- Platform: android -->
```kt
val query = ModelQuery.list(Todo::class.java) as AppSyncGraphQLRequest<PaginatedResult<Todo>>
val lambdaQuery = query
    .newBuilder()
    .authorizationType(AuthorizationType.AWS_LAMBDA)
    .build<PaginatedResult<Todo>>()
Amplify.API.query(lambdaQuery,
    { Log.i("MyAmplifyApp", "Queried with AWS Lambda authorizer ${it.data}")},
    { Log.e("MyAmplifyApp", "Error querying with AWS Lambda authorizer")})
```
<!-- /Platform -->

<!-- Platform: swift -->
```swift
let result = try await Amplify.API.query(
    request: .list(
        Todo.self,
        authMode: .function))
```
<!-- /Platform -->

<!-- Platform: flutter -->
```dart
final lambdaRequest = ModelQueries.list(Todo.classType, authorizationMode: APIAuthorizationType.function);
final lambdaResponse = await Amplify.API.query(request: lambdaRequest).response;
```
<!-- /Platform -->

## Set custom request headers

When working with the Amplify Data endpoint, you may need to set request headers for authorization purposes or to pass additional metadata from your frontend to the backend API.

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
This is done by specifying a `headers` parameter into the configuration. You can define headers either on a per Data client-level or on a per-request level:

#### [Custom headers per Data client]

```ts
import type { Schema } from '../amplify/data/resource';
import { generateClient } from 'aws-amplify/data';

const client = generateClient<Schema>({
  headers: {
    'My-Custom-Header': 'my value',
  },
});
```

#### [Custom headers per request]

```ts
// same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery
const { data: blog, errors } = await client.models.Blog.get(
  { id: 'myBlogId' },
  {
    headers: {
      'My-Custom-Header': 'my value',
    },
  }
);
```

The examples above show you how to set static headers but you can also programmatically set headers by specifying an async function for `headers`:

#### [Custom headers per Data client]

```ts
import type { Schema } from '../amplify/data/resource';
import { generateClient } from 'aws-amplify/data';

const client = generateClient<Schema>({
  headers: async (requestOptions) => {
    console.log(requestOptions);
    /* The request options allow you to customize your headers based on the request options such
       as http method, headers, request URI, and query string. These options are typically used
       to create a request signature.
    {
      method: '...',
      headers: { },
      uri: '/',
      queryString: ""
    }
    */
    return {
      'My-Custom-Header': 'my value',
    };
  },
});
```

#### [Custom headers per request]

```ts
// same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery
const res = await client.models.Blog.get(
  { id: 'myBlogId' },
  {
    headers: async (requestOptions) => {
      console.log(requestOptions);
      /* The request options allow you to customize your headers based on the request options such
        as http method, headers, request URI, and query string. These options are typically used
        to create a request signature.
      {
        method: '...',
        headers: { },
        uri: '/',
        queryString: ""
      }
      */
      return {
        'My-Custom-Header': 'my value',
      };
    },
  }
);
```

<!-- /Platform -->

<!-- Platform: android -->
To specify your own headers, use the `configureClient()` configuration option on the `AWSApiPlugin`'s builder. Specify the name of one of the configured APIs in your **amplify_outputs.json**. Apply customizations to the underlying OkHttp instance by providing a lambda expression as below.

#### [Java]

```java
AWSApiPlugin plugin = AWSApiPlugin.builder()
    .configureClient(AWSApiPlugin.DEFAULT_GRAPHQL_API, okHttpBuilder -> {
        okHttpBuilder.addInterceptor(chain -> {
            Request originalRequest = chain.request();
            Request updatedRequest = originalRequest.newBuilder()
                .addHeader("customHeader", "someValue")
                .build();
            return chain.proceed(updatedRequest);
        });
    })
    .build();
Amplify.addPlugin(plugin);
```

#### [Kotlin - Callbacks]

```kotlin
val plugin = AWSApiPlugin.builder()
    .configureClient(AWSApiPlugin.DEFAULT_GRAPHQL_API) { okHttpBuilder ->
        okHttpBuilder.addInterceptor { chain ->
            val originalRequest = chain.request()
            val updatedRequest = originalRequest.newBuilder()
                .addHeader("customHeader", "someValue")
                .build()
            chain.proceed(updatedRequest)
        }
    }
    .build()
Amplify.addPlugin(plugin)
```

#### [Kotlin - Coroutines]

```kotlin
val plugin = AWSApiPlugin.builder()
    .configureClient(AWSApiPlugin.DEFAULT_GRAPHQL_API) { okHttpBuilder ->
        okHttpBuilder.addInterceptor { chain ->
            val originalRequest = chain.request()
            val updatedRequest = originalRequest.newBuilder()
                .addHeader("customHeader", "someValue")
                .build()
            chain.proceed(updatedRequest)
        }
    }
    .build()
Amplify.addPlugin(plugin)
```

#### [RxJava]

```java
AWSApiPlugin plugin = AWSApiPlugin.builder()
    .configureClient(AWSApiPlugin.DEFAULT_GRAPHQL_API, okHttpBuilder -> {
        okHttpBuilder.addInterceptor(chain -> {
            Request originalRequest = chain.request();
            Request updatedRequest = originalRequest.newBuilder()
                .addHeader("customHeader", "someValue")
                .build();
            return chain.proceed(updatedRequest);
        });
    })
    .build();
RxAmplify.addPlugin(plugin);
```

<!-- /Platform -->

<!-- Platform: swift -->
To include custom headers in your outgoing requests, add an `URLRequestInterceptor` to the `AWSAPIPlugin`.

```swift
import Amplify
import AWSAPIPlugin

struct CustomInterceptor: URLRequestInterceptor {
    func intercept(_ request: URLRequest) throws -> URLRequest {
        var request = request
        request.setValue("headerValue", forHTTPHeaderField: "headerKey")
        return request
    }
}
let apiPlugin = AWSAPIPlugin(modelRegistration: AmplifyModels())
try apiPlugin.add(interceptor: CustomInterceptor(), for: AWSAPIPlugin.defaultGraphQLAPI)
try Amplify.add(plugin: apiPlugin)
try Amplify.configure(with: .amplifyOutputs)

```
<!-- /Platform -->

<!-- Platform: flutter -->
The simplest option for GraphQL requests is to use the `headers` property of a `GraphQLRequest`.

```dart
Future<void> queryWithCustomHeaders() async {
  final operation = Amplify.API.query<String>(
    request: GraphQLRequest(
      document: graphQLDocumentString,
      headers: {'customHeader': 'someValue'},
    ),
  );
  final response = await operation.response;
  final data = response.data;
  safePrint('data: $data');
}
```

Another option is to use the `baseHttpClient` property of the API plugin which can customize headers or otherwise alter HTTP functionality for all HTTP calls.

```dart
// First create a custom HTTP client implementation to extend HTTP functionality.
class MyHttpRequestInterceptor extends AWSBaseHttpClient {
  @override
  Future<AWSBaseHttpRequest> transformRequest(
    AWSBaseHttpRequest request,
  ) async {
    request.headers.putIfAbsent('customHeader', () => 'someValue');
    return request;
  }
}

// Then you can pass an instance of this client to `baseHttpClient` when you configure Amplify.
await Amplify.addPlugins([
  AmplifyAPI(baseHttpClient: MyHttpRequestInterceptor()),
]);
```
<!-- /Platform -->

<!-- Platform: javascript, angular, react-native, react, nextjs, vue -->
## Use an additional Data endpoint

If you have an additional Data endpoint that you're managing with a different Amplify project or through other means, this section will show you how to utilize that endpoint in your frontend code.

This is done by specifying the `endpoint` parameter on the `generateClient` function.

```ts
import { generateClient } from 'aws-amplify/data';

const client = generateClient({
  endpoint: 'https://my-other-endpoint.com/graphql',
});
```

If this Data endpoint shares its authorization configuration (for example, both endpoints share the same user pool and/or identity pool as the one in your `amplify_outputs.json` file), you can specify the `authMode` parameter on `generateClient`.

```ts
const client = generateClient({
  endpoint: 'https://my-other-endpoint.com/graphql',
  authMode: 'userPool',
});
```

If the endpoint uses API Key authorization, you can pass in the `apiKey` parameter on `generateClient`.

```ts
const client = generateClient({
  endpoint: 'https://my-other-endpoint.com/graphql',
  authMode: 'apiKey',
  apiKey: 'my-api-key',
});
``` 

If the endpoint uses a different authorization configuration, you can manually pass in the authorization header using the instructions in the [Set custom request headers](#set-custom-request-headers) section.
<!-- /Platform -->
