From 715f1ea93d5bc0eacda85e032aa9037ec582ef44 Mon Sep 17 00:00:00 2001 From: Ivan Artemiev <29709626+iartemiev@users.noreply.github.com> Date: Wed, 25 Jan 2023 17:26:03 -0500 Subject: [PATCH 1/3] feat: add TS examples to API GQL docs --- src/fragments/lib/graphqlapi/js/authz.mdx | 82 ++++++- .../lib/graphqlapi/js/cancel-request.mdx | 16 +- .../lib/graphqlapi/js/getting-started.mdx | 82 ++++++- .../lib/graphqlapi/js/js-configure.mdx | 2 +- .../lib/graphqlapi/js/mutate-data.mdx | 165 ++++++++++++- .../lib/graphqlapi/js/query-data.mdx | 228 ++++++++++++++++-- .../lib/graphqlapi/js/subscribe-data.mdx | 177 ++++++++++++-- 7 files changed, 675 insertions(+), 77 deletions(-) diff --git a/src/fragments/lib/graphqlapi/js/authz.mdx b/src/fragments/lib/graphqlapi/js/authz.mdx index 6d824128425..46ff21ca170 100644 --- a/src/fragments/lib/graphqlapi/js/authz.mdx +++ b/src/fragments/lib/graphqlapi/js/authz.mdx @@ -16,15 +16,38 @@ In order to use this feature with the Amplify GraphQL Client the `API.graphql({. This is an example of using `AWS_IAM` as an authorization mode: + + + +```ts +import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import { CreateTodoMutation } from './API'; + +// Creating a post is restricted to IAM +const createdTodo = await API.graphql>({ + query: queries.createTodo, + variables: { input: todoDetails }, + authMode: GRAPHQL_AUTH_MODE.AWS_IAM +}); +``` + + + + ```js +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; + // Creating a post is restricted to IAM const createdTodo = await API.graphql({ query: queries.createTodo, variables: {input: todoDetails}, - authMode: 'AWS_IAM' + authMode: GRAPHQL_AUTH_MODE.AWS_IAM }); ``` + + + Previous examples uses `graphqlOperation` function. That function only creates an object with two attributes `query` and `variables`. In order to use `authMode` you need to pass this object as is mentioned on the previous example. @@ -40,6 +63,28 @@ You can implement your own custom API authorization logic using an AWS Lambda fu If you are using a Lambda function as an authorization mode with your AppSync API, you will need to pass an authentication token with each API request and will need to manage token refresh in your application. The following example assumes `AWS_LAMBDA` is configured as the default authentication type for your API: + + + + +```ts +import { GraphQLQuery } from '@aws-amplify/api'; +import { CreateTodoMutation } from './API'; + +const getAuthToken = () => 'myAuthToken'; +const lambdaAuthToken = getAuthToken(); + +const createdTodo = await API.graphql>({ + query: queries.createTodo, + variables: {input: todoDetails}, + authToken: lambdaAuthToken +}); +``` + + + + + ```js const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); @@ -51,16 +96,47 @@ const createdTodo = await API.graphql({ }); ``` + + + If you have a different default authentication type and would like to use `AWS_LAMBDA` with a request: -```javascript + + + + +```ts +import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import { CreateTodoMutation } from './API'; + +const getAuthToken = () => 'myAuthToken'; +const lambdaAuthToken = getAuthToken(); + +const createdTodo = await API.graphql>({ + query: queries.createTodo, + variables: {input: todoDetails}, + authMode: GRAPHQL_AUTH_MODE.AWS_LAMBDA, + authToken: lambdaAuthToken +}); +``` + + + + + +```js +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; + const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const createdTodo = await API.graphql({ query: queries.createTodo, variables: {input: todoDetails}, - authMode: 'AWS_LAMBDA', + authMode: GRAPHQL_AUTH_MODE.AWS_LAMBDA, authToken: lambdaAuthToken }); ``` + + + diff --git a/src/fragments/lib/graphqlapi/js/cancel-request.mdx b/src/fragments/lib/graphqlapi/js/cancel-request.mdx index 36198a48ce5..932e63d1e7b 100644 --- a/src/fragments/lib/graphqlapi/js/cancel-request.mdx +++ b/src/fragments/lib/graphqlapi/js/cancel-request.mdx @@ -6,14 +6,14 @@ You may cancel any query or mutation request made through API category by keepin const promise = API.graphql(graphqlOperation(...)); try { - await promise; + await promise; } catch (error) { - console.log(error); - // If the error is because the request was cancelled you can confirm here. - if(API.isCancel(error)) { - console.log(error.message); // "my message for cancellation" - // handle user cancellation logic - } + console.log(error); + // If the error is because the request was cancelled you can confirm here. + if(API.isCancel(error)) { + console.log(error.message); // "my message for cancellation" + // handle user cancellation logic + } } ... @@ -26,7 +26,7 @@ You need to ensure that the promise returned from `API.graphql()` has not been m ```javascript async function makeAPICall() { - return API.graphql(graphqlOperation(...)); + return API.graphql(graphqlOperation(...)); } const promise = makeAPICall(); diff --git a/src/fragments/lib/graphqlapi/js/getting-started.mdx b/src/fragments/lib/graphqlapi/js/getting-started.mdx index 0ae2792aa26..7302b190c1f 100644 --- a/src/fragments/lib/graphqlapi/js/getting-started.mdx +++ b/src/fragments/lib/graphqlapi/js/getting-started.mdx @@ -73,6 +73,30 @@ Now that the GraphQL API has deployed, it’s time to learn how to interact with - __Mutations__ - write data to the API (create, update, delete operations) + + + +```ts +import { API, graphqlOperation } from 'aws-amplify'; +import { createTodo, updateTodo, deleteTodo } from './graphql/mutations'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { CreateTodoInput, CreateTodoMutation, UpdateTodoMutation, DeleteTodoMutation } from './API'; + +const todo: CreateTodoInput = { name: "My first todo", description: "Hello world!" }; + +/* create a todo */ +await API.graphql>(graphqlOperation(createTodo, {input: todo})); + +/* update a todo */ +await API.graphql>(graphqlOperation(updateTodo, { input: { id: todoId, name: "Updated todo info" }})); + +/* delete a todo */ +await API.graphql>(graphqlOperation(deleteTodo, { input: { id: todoId }})); +``` + + + + ```js import { API, graphqlOperation } from 'aws-amplify'; import { createTodo, updateTodo, deleteTodo } from './graphql/mutations'; @@ -88,8 +112,29 @@ await API.graphql(graphqlOperation(updateTodo, { input: { id: todoId, name: "Upd /* delete a todo */ await API.graphql(graphqlOperation(deleteTodo, { input: { id: todoId }})); ``` + + + + - __Queries__ - read data from the API (list, get operations) + + + +```ts +import { API, graphqlOperation } from 'aws-amplify'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { listTodos } from './graphql/queries'; +import { ListTodosQuery } from './API'; + +const todos = await API.graphql>(graphqlOperation(listTodos)); +``` + + + + + + ```js import { API, graphqlOperation } from 'aws-amplify'; import { listTodos } from './graphql/queries'; @@ -97,14 +142,44 @@ import { listTodos } from './graphql/queries'; const todos = await API.graphql(graphqlOperation(listTodos)); ``` + + + - __Subscriptions__ - subscribe to changes in data for real-time functionality (onCreate, onUpdate, onDelete) + + + +```ts +import { API, graphqlOperation } from 'aws-amplify'; +import { GraphQLSubscription } from '@aws-amplify/api'; +import { onCreateTodo } from './graphql/subscriptions'; +import { OnCreateTodoSubscription } from './API'; + +// Subscribe to creation of Todo +const sub = API.graphql>( + graphqlOperation(onCreateTodo) +).subscribe({ + next: (todoData) => { + console.log(todoData); + // Do something with the data + } +}); + +// Stop receiving data updates from the subscription +sub.unsubscribe(); +``` + + + + + ```js import { API, graphqlOperation } from 'aws-amplify'; import { onCreateTodo } from './graphql/subscriptions'; // Subscribe to creation of Todo -const subscription = API.graphql( +const sub = API.graphql( graphqlOperation(onCreateTodo) ).subscribe({ next: (todoData) => { @@ -114,9 +189,12 @@ const subscription = API.graphql( }); // Stop receiving data updates from the subscription -subscription.unsubscribe(); +sub.unsubscribe(); ``` + + + ### Updating Your GraphQL Schema When you create a GraphQL backend with the CLI, the schema definition for your backend data structure is saved in one of two places: diff --git a/src/fragments/lib/graphqlapi/js/js-configure.mdx b/src/fragments/lib/graphqlapi/js/js-configure.mdx index bc58f59084e..cfc63c07946 100644 --- a/src/fragments/lib/graphqlapi/js/js-configure.mdx +++ b/src/fragments/lib/graphqlapi/js/js-configure.mdx @@ -6,7 +6,7 @@ Add Amplify to your app with `yarn` or `npm`: npm install aws-amplify ``` -In your app's entry point i.e. App.js, import and load the configuration file: +In your app's entry point i.e. App.ts or App.js, import and load the configuration file: ```javascript import { Amplify, API, graphqlOperation } from 'aws-amplify'; diff --git a/src/fragments/lib/graphqlapi/js/mutate-data.mdx b/src/fragments/lib/graphqlapi/js/mutate-data.mdx index 480d9979803..cb5734e4af6 100644 --- a/src/fragments/lib/graphqlapi/js/mutate-data.mdx +++ b/src/fragments/lib/graphqlapi/js/mutate-data.mdx @@ -6,7 +6,31 @@ In GraphQL, mutations are used to create, update, or delete data. Here are some #### Creating an item -```javascript + + + +```ts +import { API } from "aws-amplify"; +import * as mutations from './graphql/mutations'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { CreateTodoInput, CreateTodoMutation } from './API'; + +const todoDetails: CreateTodoInput = { + name: 'Todo 1', + description: 'Learn AWS AppSync' +}; + +const newTodo = await API.graphql>({ + query: mutations.createTodo, + variables: { input: todoDetails } +}); +``` + + + + + +```js import { API } from "aws-amplify"; import * as mutations from './graphql/mutations'; @@ -15,33 +39,93 @@ const todoDetails = { description: 'Learn AWS AppSync' }; -const newTodo = await API.graphql({ query: mutations.createTodo, variables: {input: todoDetails}}); +const newTodo = await API.graphql({ + query: mutations.createTodo, + variables: { input: todoDetails } +}); ``` + + + You do not have to pass in `createdAt` and `updatedAt` fields, AppSync manages this for you. You can optionally import the `graphqlOperation` helper function to help you construct the argument object: -```javascript + + + +```ts import { API, graphqlOperation } from 'aws-amplify'; // ... -const newTodo = await API.graphql(graphqlOperation(mutations.createTodo, {input: todoDetails})); // equivalent to above example + +// equivalent to above example +const newTodo = await API.graphql>( + graphqlOperation(mutations.createTodo, { input: todoDetails }) +); ``` + + + + +```js +import { API, graphqlOperation } from 'aws-amplify'; +// ... + +// equivalent to above example +const newTodo = await API.graphql( + graphqlOperation(mutations.createTodo, { input: todoDetails }) +); +``` + + + + #### Updating an item -```javascript + + + +```ts +import { API } from "aws-amplify"; +import * as mutations from './graphql/mutations'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { UpdateTodoInput, UpdateTodoMutation } from './API'; + +const todoDetails: UpdateTodoInput = { + id: 'some_id', + description: 'Updated description' +}; + +const updatedTodo = await API.graphql>({ + query: mutations.updateTodo, + variables: { input: todoDetails } +}); +``` + + + + + +```js import { API } from "aws-amplify"; import * as mutations from './graphql/mutations'; const todoDetails = { id: 'some_id', - description: 'My updated description!' + description: 'Updated description' }; -const updatedTodo = await API.graphql({ query: mutations.updateTodo, variables: {input: todoDetails}}); +const updatedTodo = await API.graphql({ + query: mutations.updateTodo, + variables: { input: todoDetails } +}); ``` + + + Notes: - You do not have to pass in `createdAt` and `updatedAt` fields, AppSync manages this for you. @@ -49,7 +133,30 @@ Notes: #### Deleting an item -```javascript + + + +```ts +import { API } from "aws-amplify"; +import * as mutations from './graphql/mutations'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { DeleteTodoInput, DeleteTodoMutation } from './API'; + +const todoDetails: DeleteTodoInput = { + id: 'some_id', +}; + +const deletedTodo = await API.graphql>({ + query: mutations.deleteTodo, + variables: { input: todoDetails } +}); +``` + + + + + +```js import { API } from "aws-amplify"; import * as mutations from './graphql/mutations'; @@ -57,9 +164,15 @@ const todoDetails = { id: 'some_id', }; -const deletedTodo = await API.graphql({ query: mutations.deleteTodo, variables: {input: todoDetails}}); +const deletedTodo = await API.graphql({ + query: mutations.deleteTodo, + variables: { input: todoDetails } +}); ``` + + + Only an `id` is needed. @@ -72,8 +185,35 @@ By default, each AppSync API will be set with a default authorization mode when #### Mutation with custom authorization mode + + + +```ts +import { API } from "aws-amplify"; +import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import * as mutations from './graphql/mutations'; +import { CreateTodoInput, CreateTodoMutation } from './API'; + +const todoDetails: CreateTodoInput = { + id: 'some_id', + name: 'My todo!', + description: 'Hello world!' +}; + +const todo = await API.graphql>({ + query: mutations.createTodo, + variables: { input: todoDetails }, + authMode: GRAPHQL_AUTH_MODE.AWS_IAM +}); +``` + + + + + ```js import { API } from "aws-amplify"; +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import * as mutations from './graphql/mutations'; const todoDetails = { @@ -84,7 +224,10 @@ const todoDetails = { const todo = await API.graphql({ query: mutations.createTodo, - variables: {input: todoDetails}, - authMode: 'AWS_IAM' + variables: { input: todoDetails }, + authMode: GRAPHQL_AUTH_MODE.AWS_IAM }); ``` + + + diff --git a/src/fragments/lib/graphqlapi/js/query-data.mdx b/src/fragments/lib/graphqlapi/js/query-data.mdx index c5f5fedeafd..6e097bc3af8 100644 --- a/src/fragments/lib/graphqlapi/js/query-data.mdx +++ b/src/fragments/lib/graphqlapi/js/query-data.mdx @@ -16,7 +16,33 @@ import * as subscriptions from './graphql/subscriptions'; Running a GraphQL query is simple. Import the generated query and execute it with `API.graphql`: -```javascript + + + +```ts +import { API } from 'aws-amplify'; +import * as queries from './graphql/queries'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { ListTodosQuery, GetTodoQuery } from './API'; + +// Simple query +const allTodos = await API.graphql>( + { query: queries.listTodos } +); +console.log(allTodos); // result: { "data": { "listTodos": { "items": [/* ..... */] } } } + +// Fetch a single record by its identifier +const oneTodo = await API.graphql>({ + query: queries.getTodo, + variables: { id: 'some id' } +}); +``` + + + + + +```js import { API } from 'aws-amplify'; import * as queries from './graphql/queries'; @@ -24,49 +50,86 @@ import * as queries from './graphql/queries'; const allTodos = await API.graphql({ query: queries.listTodos }); console.log(allTodos); // result: { "data": { "listTodos": { "items": [/* ..... */] } } } -// Query using a parameter +Fetch a single record by its identifier const oneTodo = await API.graphql({ query: queries.getTodo, variables: { id: 'some id' } }); ``` -The TypeScript signature of API.graphql returns a `Promise | Observable`. [For now](https://github.com/aws-amplify/amplify-js/issues/6369), you need to assert the type before `await`ing: + + + +You can optionally import the `graphqlOperation` helper function to help you construct this argument object: + + + + +```ts +import { API, graphqlOperation } from 'aws-amplify'; +import { GraphQLQuery } from '@aws-amplify/api'; +import { GetTodoQuery } from './API'; -```typescript -const allTodos = await( - API.graphql({ query: queries.listTodos }) as Promise +const oneTodo = await API.graphql>( + graphqlOperation(queries.getTodo, { id: 'some id' }) ); ``` -You can optionally import the `graphqlOperation` helper function to help you construct this argument object: + -```javascript + + +```js import { API, graphqlOperation } from 'aws-amplify'; const oneTodo = await API.graphql( graphqlOperation(queries.getTodo, { id: 'some id' }) ); -// equivalent to -// const oneTodo = await API.graphql({ query: queries.getTodo, variables: { id: 'some id' }})); ``` + + + ### Custom authorization mode By default, each AppSync API will be set with a default authorization mode when you configure your app. If you would like to override the default authorization mode, you can do so by passing in an `authMode` property. For example, this is useful when you have public reads via API Key auth and authenticated reads via IAM auth. #### Query with custom authorization mode + + + +```ts +import { API } from 'aws-amplify'; +import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import * as queries from './graphql/queries'; +import { GetTodoQuery } from './API'; + +const todos = await API.graphql>({ + query: queries.listTodos, + authMode: GRAPHQL_AUTH_MODE.AWS_IAM +}); +``` + + + + + ```js import { API } from 'aws-amplify'; +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import * as queries from './graphql/queries'; + const todos = await API.graphql({ query: queries.listTodos, - authMode: 'AWS_IAM' + authMode: GRAPHQL_AUTH_MODE.AWS_IAM }); ``` + + + ## Filtered and Paginated Queries As your data grows, you will want to do pagination and filtering at the AppSync level instead of on the client. Fortunately, this is already built in to `API.graphql`, but you need to understand the schema of these queries. [This is explained in the AppSync docs](https://docs.aws.amazon.com/appsync/latest/devguide/using-your-api.html), but here you will translate them to the `API.graphql` equivalent. @@ -74,7 +137,7 @@ As your data grows, you will want to do pagination and filtering at the AppSync You can find the input schemas in the Docs pane of the GraphQL explorer or inside your autogenerated `/graphql` folder. They look like this: ```graphql -listProducts( +listTodos( filter: ModelTodoFilterInput limit: Int nextToken: String): ModelTodoConnection @@ -109,43 +172,156 @@ input ModelIntInput { These vary based on the type of the field, but are linked to corresponding [DynamoDB queries](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html). + + + +```ts +import { ListTodosQueryVariables, ListTodosQuery } from './API'; + +const variables: ListTodosQueryVariables = { + filter: { + priority: { + eq: 1 + } + } +}; + +await API.graphql>({ + query: listTodos, variables: variables +}); +``` + + + + + ```js -// Query with filters, limits, and pagination -let filter = { +const variables = { + filter: { priority: { - eq: 1 // filter priority = 1 + eq: 1 } + } }; -await API.graphql({ query: listProducts, variables: { filter: filter}}); + +await API.graphql({ + query: listTodos, variables: variables +});; ``` + + + ### Compound Filters You can combine filters with `and`, `or`, and `not` boolean logic. Observe, in the autogenerated schema above, that `ModelTodoFilterInput` is recursive in respect to those fields. So if, for example, you wanted to filter for `priority` values of 1 OR 2, you would do this: + + + +```ts +import { ListTodosQueryVariables, ListTodosQuery } from './API'; + +const variables: ListTodosQueryVariables = { + filter: { + or: [ + { priority: { eq:1 } }, + { priority: { eq:2 } } + ] + } +}; + +await API.graphql>({ + query: listTodos, variables: variables +}); +``` + + + + + ```js -let filter = { - or: [{ priority: {eq:1} }, - { priority: {eq:2} }] - }; -await API.graphql({ query: listProducts, variables: { filter: filter}}); +const variables = { + filter: { + or: [ + { priority: { eq:1 } }, + { priority: { eq:2 } } + ] + } +}; + +await API.graphql({ + query: listTodos, variables: variables +});; ``` + + + Note that querying for `priority` of 1 AND 2 would return no results, because this is boolean logic instead of natural language. ### Paginating Queries Pagination in AppSync is done by making a request with a `limit`, and getting back a `nextToken` in order to get a cursor for the next page in your query: + + + +```ts +import { ListTodosQueryVariables, ListTodosQuery } from './API'; + +// Fetch first 20 records +const variables: ListTodosQueryVariables = { + limit: 20, + // add filter as needed +}; + +const res = await API.graphql({ + query: listTodos, variables: variables +}); + +const { items: itemsPage1, nextToken } = res.data?.listTodos; + +// Fetch the next 20 records +variables.nextToken = nextToken; + +const res = await API.graphql({ + query: listTodos, variables: variables +}); + +const { items: itemsPage2 } = res.data?.listTodos; +``` + + + + + ```js -// page 1 of query -const { data: { listProducts: { items: itemsPage1, nextToken } } } = await API.graphql({ query: listProducts, variables: { limit: 20, /* add filter as needed */ }}); -// // You are assuming that `listProducts` includes a query for `nextToken`, which is the case for autogenerated GraphQL query strings. +// Fetch first 20 records +const variables = { + limit: 20, + // add filter as needed +}; + +const res = await API.graphql({ + query: listTodos, variables: variables +}); -// page 2 of query -const { data: { listProducts: { items: itemsPage2 } } } = await API.graphql({ query: listProducts, variables: { limit: 20, nextToken }}); +const { items: itemsPage1, nextToken } = res.data.listTodos; + +// Fetch the next 20 records +variables.nextToken = nextToken; + +const res = await API.graphql({ + query: listTodos, variables: variables +}); + +const { items: itemsPage2 } = res.data.listTodos; ``` + + + A `nextToken` is a very long string that looks like `"eyJ2ZXJzaW9uejE1a2RPanZPQzFCMlFTdGNxdUFBQUJxekNDQWFjR0NTcUdTSWIzRFFFSEJxQ0NBWmd3Z2dHVUFnRUFNSUlCalFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5eEtWQjUvTlJxS2o5ZHBYc0NBUkNBZ2dGZUZFNW1MbTRkb25BOUg0U0FpOGhSZ1lucmRndmQz"` which represents the cursor to the starting item of the next query made with these filters. ### Frequently Asked Questions diff --git a/src/fragments/lib/graphqlapi/js/subscribe-data.mdx b/src/fragments/lib/graphqlapi/js/subscribe-data.mdx index bb677d9198b..79589ad8d3b 100644 --- a/src/fragments/lib/graphqlapi/js/subscribe-data.mdx +++ b/src/fragments/lib/graphqlapi/js/subscribe-data.mdx @@ -4,12 +4,17 @@ Subscriptions is a GraphQL feature allowing the server to send data to its clients when a specific event happens. You can enable real-time data integration in your app with a subscription. -```javascript + + + +```ts import { Amplify, API, graphqlOperation } from 'aws-amplify'; +import { GraphQLSubscription } from '@aws-amplify/api'; import * as subscriptions from './graphql/subscriptions'; +import { OnCreateTodoSubscription } from './API'; // Subscribe to creation of Todo -const subscription = API.graphql( +const sub = API.graphql>( graphqlOperation(subscriptions.onCreateTodo) ).subscribe({ next: ({ provider, value }) => console.log({ provider, value }), @@ -17,9 +22,32 @@ const subscription = API.graphql( }); // Stop receiving data updates from the subscription -subscription.unsubscribe(); +sub.unsubscribe(); ``` + + + + +```js +import { Amplify, API, graphqlOperation } from 'aws-amplify'; +import * as subscriptions from './graphql/subscriptions'; + +// Subscribe to creation of Todo +const sub = API.graphql( + graphqlOperation(subscriptions.onCreateTodo) +).subscribe({ + next: ({ provider, value }) => console.log({ provider, value }), + error: (error) => console.warn(error) +}); + +// Stop receiving data updates from the subscription +sub.unsubscribe(); +``` + + + + When using **AWS AppSync** subscriptions, be sure that your AppSync configuration is at the root of the configuration object, instead of being under API: ```javascript @@ -42,20 +70,51 @@ Amplify.configure({ Subscriptions take an optional `filter` argument to define service-side subscription filters: -```graphql -// Only subscribe to creation of Todo, if the todo "type" is "Personal" -const subscription = API.graphql( - graphqlOperation(subscriptions.onCreateTodo, { - filter: { - type: { eq: "Personal" } - } - }) + + + +```ts +import { GraphQLSubscription } from '@aws-amplify/api'; +import { OnCreateTodoSubscriptionVariables, OnCreateTodoSubscription } from './API'; + +const variables: OnCreateTodoSubscriptionVariables = { + filter: { + // Only receive Todo messages where the "type" field is "Personal" + type: { eq: "Personal" } + } +} + +const sub = API.graphql>( + graphqlOperation(subscriptions.onCreateTodo, variables) +).subscribe({ + next: ({ provider, value }) => console.log({ provider, value }), + error: error => console.warn(error) +}); +``` + + + + + +```js +const variables = { + filter: { + // Only receive Todo messages where the "type" field is "Personal" + type: { eq: "Personal" } + } +} + +const sub = API.graphql( + graphqlOperation(subscriptions.onCreateTodo, variables) ).subscribe({ - next: ({ provider, value }) => console.log({ provider, value }), - error: error => console.warn(error) + next: ({ provider, value }) => console.log({ provider, value }), + error: error => console.warn(error) }); ``` + + + If you want to get all subscription events, don’t pass any `filter` parameters. @@ -68,7 +127,7 @@ If you want to get all subscription events, don’t pass any `filter` parameters Now that your application is setup and using subscriptions, you may want to know when the subscription is finally established, or reflect to your users when the subscription isn't healthy. You can monitor the connection state for changes via Hub. -```typescript +```ts import { CONNECTION_STATE_CHANGE, ConnectionState } from '@aws-amplify/pubsub'; import { Hub } from 'aws-amplify'; @@ -91,9 +150,72 @@ import jsReconnectDescription from '/src/fragments/lib/pubsub/js/reconnect-descr -```typescript + + + +```ts +import { GraphQLQuery, GraphQLSubscription } from '@aws-amplify/api'; +import { + ListTodosQuery + OnCreateTodoSubscription, + OnUpdateTodoSubscription, + OnDeleteTodoSubscription +} from './API'; + const fetchRecentData = () => { - // Retrieve some/all data from a appsync + // Retrieve some/all data from AppSync + const allTodos = await API.graphql>({ + query: queries.listTodos + }); +} + +let priorConnectionState: ConnectionState; + +Hub.listen("api", (data: any) => { + const { payload } = data; + if ( + payload.event === CONNECTION_STATE_CHANGE + ) { + + if (priorConnectionState === ConnectionState.Connecting && payload.data.connectionState === ConnectionState.Connected) { + fetchRecentData(); + } + priorConnectionState = payload.data.connectionState; + } +}); + +const createSub = API.graphql>( + graphqlOperation(subscriptions.onCreateTodo) +).subscribe({ + next: data => // Process incoming messages +}); + +const updateSub = API.graphql>( + graphqlOperation(subscriptions.onUpdateTodo) +).subscribe({ + next: data => // Process incoming messages +}); + +const deleteSub = API.graphql>( + graphqlOperation(subscriptions.onDeleteTodo) +).subscribe({ + next: data => // Process incoming messages +}); + +const cleanupSubscriptions = () => { + createSub.unsubscribe(); + updateSub.unsubscribe(); + deleteSub.unsubscribe(); +} +``` + + + + + +```js +const fetchRecentData = () => { + // Retrieve some/all data from AppSync const allTodos = await API.graphql({ query: queries.listTodos }); } @@ -112,27 +234,30 @@ Hub.listen("api", (data: any) => { } }); -const createSubscription = (API.graphql( +const createSub = API.graphql( graphqlOperation(subscriptions.onCreateTodo) -) as unknown as ZenObservable).subscribe({ +).subscribe({ next: data => // Process incoming messages }); -const updateSubscription = (API.graphql( +const updateSub = API.graphql( graphqlOperation(subscriptions.onUpdateTodo) -) as unknown as ZenObservable).subscribe({ +).subscribe({ next: data => // Process incoming messages }); -const deleteSubscription = (API.graphql( +const deleteSub = API.graphql( graphqlOperation(subscriptions.onDeleteTodo) -) as unknown as ZenObservable).subscribe({ +).subscribe({ next: data => // Process incoming messages }); const cleanupSubscriptions = () => { - createSubscription.unsubscribe(); - updateSubscription.unsubscribe(); - deleteSubscription.unsubscribe(); + createSub.unsubscribe(); + updateSub.unsubscribe(); + deleteSub.unsubscribe(); } -``` \ No newline at end of file +``` + + + \ No newline at end of file From 378c75b376d64293f2ee6d326fdf6f18b29b7b57 Mon Sep 17 00:00:00 2001 From: Ivan Artemiev <29709626+iartemiev@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:21:09 -0500 Subject: [PATCH 2/3] pr feedback --- src/fragments/lib/graphqlapi/js/cancel-request.mdx | 2 +- src/fragments/lib/graphqlapi/js/getting-started.mdx | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fragments/lib/graphqlapi/js/cancel-request.mdx b/src/fragments/lib/graphqlapi/js/cancel-request.mdx index 932e63d1e7b..71fdebc1151 100644 --- a/src/fragments/lib/graphqlapi/js/cancel-request.mdx +++ b/src/fragments/lib/graphqlapi/js/cancel-request.mdx @@ -10,7 +10,7 @@ try { } catch (error) { console.log(error); // If the error is because the request was cancelled you can confirm here. - if(API.isCancel(error)) { + if (API.isCancel(error)) { console.log(error.message); // "my message for cancellation" // handle user cancellation logic } diff --git a/src/fragments/lib/graphqlapi/js/getting-started.mdx b/src/fragments/lib/graphqlapi/js/getting-started.mdx index 7302b190c1f..d12bb43c88a 100644 --- a/src/fragments/lib/graphqlapi/js/getting-started.mdx +++ b/src/fragments/lib/graphqlapi/js/getting-started.mdx @@ -160,9 +160,9 @@ import { OnCreateTodoSubscription } from './API'; const sub = API.graphql>( graphqlOperation(onCreateTodo) ).subscribe({ - next: (todoData) => { - console.log(todoData); - // Do something with the data + next: (payload) => { + const createdTodo = payload.value.data?.onCreateTodo; + console.log(createdTodo); } }); @@ -182,9 +182,9 @@ import { onCreateTodo } from './graphql/subscriptions'; const sub = API.graphql( graphqlOperation(onCreateTodo) ).subscribe({ - next: (todoData) => { - console.log(todoData); - // Do something with the data + next: (payload) => { + const createdTodo = payload.value.data?.onCreateTodo; + console.log(createdTodo); } }); From 19281d03c060eb6aee0d1a67baadae70f9e0bd32 Mon Sep 17 00:00:00 2001 From: Ivan Artemiev <29709626+iartemiev@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:43:27 -0500 Subject: [PATCH 3/3] pr feedback 2 --- src/fragments/lib/graphqlapi/js/authz.mdx | 28 +++++++++++-------- .../lib/graphqlapi/js/mutate-data.mdx | 4 +-- .../lib/graphqlapi/js/query-data.mdx | 2 ++ .../lib/graphqlapi/js/subscribe-data.mdx | 6 ++++ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/fragments/lib/graphqlapi/js/authz.mdx b/src/fragments/lib/graphqlapi/js/authz.mdx index 46ff21ca170..8906d2ff308 100644 --- a/src/fragments/lib/graphqlapi/js/authz.mdx +++ b/src/fragments/lib/graphqlapi/js/authz.mdx @@ -20,12 +20,13 @@ This is an example of using `AWS_IAM` as an authorization mode: ```ts -import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import { API, GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import * as mutations from './graphql/mutations'; import { CreateTodoMutation } from './API'; // Creating a post is restricted to IAM const createdTodo = await API.graphql>({ - query: queries.createTodo, + query: mutations.createTodo, variables: { input: todoDetails }, authMode: GRAPHQL_AUTH_MODE.AWS_IAM }); @@ -35,11 +36,12 @@ const createdTodo = await API.graphql>({ ```js -import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import { API, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +import * as mutations from './graphql/mutations'; // Creating a post is restricted to IAM const createdTodo = await API.graphql({ - query: queries.createTodo, + query: mutations.createTodo, variables: {input: todoDetails}, authMode: GRAPHQL_AUTH_MODE.AWS_IAM }); @@ -68,14 +70,13 @@ The following example assumes `AWS_LAMBDA` is configured as the default authenti ```ts -import { GraphQLQuery } from '@aws-amplify/api'; -import { CreateTodoMutation } from './API'; +// ... const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const createdTodo = await API.graphql>({ - query: queries.createTodo, + query: mutations.createTodo, variables: {input: todoDetails}, authToken: lambdaAuthToken }); @@ -84,13 +85,14 @@ const createdTodo = await API.graphql>({ - ```js +// ... + const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const createdTodo = await API.graphql({ - query: queries.createTodo, + query: mutations.createTodo, variables: {input: todoDetails}, authToken: lambdaAuthToken }); @@ -105,14 +107,15 @@ If you have a different default authentication type and would like to use `AWS_L ```ts -import { GraphQLQuery, GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; +// ... +import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import { CreateTodoMutation } from './API'; const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const createdTodo = await API.graphql>({ - query: queries.createTodo, + query: mutations.createTodo, variables: {input: todoDetails}, authMode: GRAPHQL_AUTH_MODE.AWS_LAMBDA, authToken: lambdaAuthToken @@ -124,13 +127,14 @@ const createdTodo = await API.graphql>({ ```js +// ... import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const createdTodo = await API.graphql({ - query: queries.createTodo, + query: mutations.createTodo, variables: {input: todoDetails}, authMode: GRAPHQL_AUTH_MODE.AWS_LAMBDA, authToken: lambdaAuthToken diff --git a/src/fragments/lib/graphqlapi/js/mutate-data.mdx b/src/fragments/lib/graphqlapi/js/mutate-data.mdx index cb5734e4af6..23732c1c2e6 100644 --- a/src/fragments/lib/graphqlapi/js/mutate-data.mdx +++ b/src/fragments/lib/graphqlapi/js/mutate-data.mdx @@ -56,8 +56,8 @@ You can optionally import the `graphqlOperation` helper function to help you con ```ts -import { API, graphqlOperation } from 'aws-amplify'; // ... +import { API, graphqlOperation } from 'aws-amplify'; // equivalent to above example const newTodo = await API.graphql>( @@ -70,8 +70,8 @@ const newTodo = await API.graphql>( ```js -import { API, graphqlOperation } from 'aws-amplify'; // ... +import { API, graphqlOperation } from 'aws-amplify'; // equivalent to above example const newTodo = await API.graphql( diff --git a/src/fragments/lib/graphqlapi/js/query-data.mdx b/src/fragments/lib/graphqlapi/js/query-data.mdx index 6e097bc3af8..6b8f606a1ad 100644 --- a/src/fragments/lib/graphqlapi/js/query-data.mdx +++ b/src/fragments/lib/graphqlapi/js/query-data.mdx @@ -69,6 +69,7 @@ You can optionally import the `graphqlOperation` helper function to help you con import { API, graphqlOperation } from 'aws-amplify'; import { GraphQLQuery } from '@aws-amplify/api'; import { GetTodoQuery } from './API'; +// ... const oneTodo = await API.graphql>( graphqlOperation(queries.getTodo, { id: 'some id' }) @@ -81,6 +82,7 @@ const oneTodo = await API.graphql>( ```js import { API, graphqlOperation } from 'aws-amplify'; +// ... const oneTodo = await API.graphql( graphqlOperation(queries.getTodo, { id: 'some id' }) diff --git a/src/fragments/lib/graphqlapi/js/subscribe-data.mdx b/src/fragments/lib/graphqlapi/js/subscribe-data.mdx index 79589ad8d3b..80f244db52e 100644 --- a/src/fragments/lib/graphqlapi/js/subscribe-data.mdx +++ b/src/fragments/lib/graphqlapi/js/subscribe-data.mdx @@ -74,6 +74,7 @@ Subscriptions take an optional `filter` argument to define service-side subscrip ```ts +// ... import { GraphQLSubscription } from '@aws-amplify/api'; import { OnCreateTodoSubscriptionVariables, OnCreateTodoSubscription } from './API'; @@ -97,6 +98,8 @@ const sub = API.graphql>( ```js +// ... + const variables = { filter: { // Only receive Todo messages where the "type" field is "Personal" @@ -154,6 +157,7 @@ import jsReconnectDescription from '/src/fragments/lib/pubsub/js/reconnect-descr ```ts +// ... import { GraphQLQuery, GraphQLSubscription } from '@aws-amplify/api'; import { ListTodosQuery @@ -214,6 +218,8 @@ const cleanupSubscriptions = () => { ```js +// ... + const fetchRecentData = () => { // Retrieve some/all data from AppSync const allTodos = await API.graphql({ query: queries.listTodos });