Skip to content

Commit

Permalink
feat: Fix examples to use lambda data client (#8096)
Browse files Browse the repository at this point in the history
* feat: Fix examples to use lambda data client

* fix: Schema import pattern

* Update src/pages/[platform]/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx

Co-authored-by: josef <[email protected]>

* Add warning after data client handler examples

* Update src/pages/[platform]/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx

Co-authored-by: josef <[email protected]>

* Update src/pages/[platform]/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx

Co-authored-by: josef <[email protected]>

* Update src/pages/[platform]/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx

Co-authored-by: josef <[email protected]>

* Apply suggestions from code review

Co-authored-by: josef <[email protected]>

---------

Co-authored-by: josef <[email protected]>
  • Loading branch information
stocaaro and josefaidt authored Dec 9, 2024
1 parent b362e1b commit 0dfff00
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,16 @@ Function access to `defineData` can be configured using an authorization rule on
import {
a,
defineData,
defineFunction,
type ClientSchema
} from '@aws-amplify/backend';

const functionWithDataAccess = defineFunction({
entry: '../functions/data-access.ts'
});
import { functionWithDataAccess } from '../function/data-access/resource';

const schema = a
.schema({
Todo: a.model({
name: a.string(),
description: a.string()
description: a.string(),
isDone: a.boolean()
})
})
// highlight-next-line
Expand All @@ -60,14 +57,25 @@ export const data = defineData({
});
```

Create a new directory and a resource file, `amplify/functions/data-access/resource.ts`. Then, define the Function with `defineFunction`:

```ts title="amplify/functions/data-access/resource.ts"
import { defineFunction } from '@aws-amplify/backend';

export const functionWithDataAccess = defineFunction({
name: 'data-access',
});
```

The object returned from `defineFunction` can be passed directly to `allow.resource()` in the schema authorization rules. This will grant the function the ability to execute Query, Mutation, and Subscription operations against the GraphQL API. Use the `.to()` method to narrow down access to one or more operations.

```ts
```ts title="amplify/data/resource.ts"
const schema = a
.schema({
Todo: a.model({
name: a.string(),
description: a.string()
description: a.string(),
isDone: a.boolean()
})
})
// highlight-start
Expand All @@ -77,8 +85,6 @@ const schema = a
// highlight-end
```

When configuring function access, the function will be provided the API endpoint as an environment variable named `<defineDataName>_GRAPHQL_ENDPOINT`, where `defineDataName` is transformed to SCREAMING_SNAKE_CASE. The default name is `AMPLIFY_DATA_GRAPHQL_ENDPOINT` unless you have specified a different name in `defineData`.

<Callout info>

Function access can only be configured on the schema object. It cannot be configured on individual models or fields.
Expand All @@ -89,86 +95,43 @@ Function access can only be configured on the schema object. It cannot be config

In the handler file for your function, configure the Amplify data client

```ts title="amplify/functions/data-access.ts"
```ts title="amplify/functions/data-access/handler.ts"
import type { Handler } from 'aws-lambda';
import type { Schema } from '../../data/resource';
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/data';
import { Schema } from '../data/resource';
import { getAmplifyDataClientConfig } from '@aws-amplify/backend/function/runtime';
import { env } from '$amplify/env/<function-name>'; // replace with your function name

const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig(env);

Amplify.configure(
{
API: {
GraphQL: {
endpoint: env.<amplifyData>_GRAPHQL_ENDPOINT, // replace with your defineData name
region: env.AWS_REGION,
defaultAuthMode: 'identityPool'
}
}
},
{
Auth: {
credentialsProvider: {
getCredentialsAndIdentityId: async () => ({
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
sessionToken: env.AWS_SESSION_TOKEN,
},
}),
clearCredentialsAndIdentityId: () => {
/* noop */
},
},
},
}
);

const dataClient = generateClient<Schema>();
Amplify.configure(resourceConfig, libraryOptions);

const client = generateClient<Schema>();

export const handler = async (event) => {
// your function code goes here
}
```

Use the command below to generate GraphQL client code to call your data backend.
<Callout info>

**Note**: We are working on bringing the end-to-end typed experience to connect to your data from within function resources without needing this step. If you'd like to provide feedback the experience or have early access, join our [Discord community](https://discord.gg/amplify).

</Callout>

```sh title="Terminal" showLineNumbers={false}
npx ampx generate graphql-client-code --out <path-function-handler-dir>/graphql
```


<Callout warning>

**Note:** Whenever you update your data model, you will need to run the command above again.

When configuring Amplify with `getAmplifyDataClientConfig`, your function consumes schema information from an S3 bucket created during backend deployment with grants for the access your function need to use it. Any changes to this bucket outside of backend deployment may break your function.
</Callout>

Once you have generated the client code, update the function to access the data. The following code creates a todo and then lists all todos.

```ts title="amplify/functions/data-access.ts"
const client = generateClient<Schema>();

export const handler = async (event) => {
await client.graphql({
query: createTodo,
variables: {
input: {
name: "My first todo",
description: "This is my first todo",
},
},
});


await client.graphql({
query: listTodos,
});
export const handler: Handler = async (event) => {
const { errors: createErrors, data: newTodo } = await client.models.Todo.create({
name: "My new todo",
description: "Todo description",
isDone: false,
})


const { errors: listErrors, data: todos } = await client.models.Todo.list();

return event;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,75 +92,38 @@ export const postConfirmation = defineFunction({
});
```

Run the command `npx ampx sandbox` to create the backend, then use the command below to generate GraphQL client code to call your data backend.
<Callout info>

**Note**: We are working on bringing the end-to-end typed experience to connect to your data from within function resources without needing this step. If you'd like to provide feedback on the experience or want to have early access, join our [Discord community](https://discord.gg/amplify).

</Callout>

```sh title="Terminal" showLineNumbers={false}
npx ampx generate graphql-client-code --out <path-to-post-confirmation-handler-dir>/graphql
```

Then, create the corresponding handler file, `amplify/auth/post-confirmation/handler.ts`, file with the following contents:

```ts title="amplify/auth/post-confirmation/handler.ts"
import type { PostConfirmationTriggerHandler } from "aws-lambda";
import { type Schema } from "../../data/resource";
import { Amplify } from "aws-amplify";
import { generateClient } from "aws-amplify/data";
import { getAmplifyDataClientConfig } from '@aws-amplify/backend/function/runtime';
import { env } from "$amplify/env/post-confirmation";
import { createUserProfile } from "./graphql/mutations";

Amplify.configure(
{
API: {
GraphQL: {
endpoint: env.AMPLIFY_DATA_GRAPHQL_ENDPOINT,
region: env.AWS_REGION,
defaultAuthMode: "iam",
},
},
},
{
Auth: {
credentialsProvider: {
getCredentialsAndIdentityId: async () => ({
credentials: {
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
sessionToken: env.AWS_SESSION_TOKEN,
},
}),
clearCredentialsAndIdentityId: () => {
/* noop */
},
},
},
}

const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig(
env
);

const client = generateClient<Schema>({
authMode: "iam",
});
Amplify.configure(resourceConfig, libraryOptions);

const client = generateClient<Schema>();

export const handler: PostConfirmationTriggerHandler = async (event) => {
await client.graphql({
query: createUserProfile,
variables: {
input: {
email: event.request.userAttributes.email,
profileOwner: `${event.request.userAttributes.sub}::${event.userName}`,
},
},
await client.models.UserProfile.create({
email: event.request.userAttributes.email,
profileOwner: `${event.request.userAttributes.sub}::${event.userName}`,
});

return event;
};

```

<Callout warning>
When configuring Amplify with `getAmplifyDataClientConfig`, your function consumes schema information from an S3 bucket created during backend deployment with grants for the access your function need to use it. Any changes to this bucket outside of backend deployment may break your function.
</Callout>


Lastly, set the newly created Function resource on your auth resource:
Expand Down

0 comments on commit 0dfff00

Please sign in to comment.