Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(function): add custom functions documentation for AWS Amplify-backend #8176

Merged
17 changes: 14 additions & 3 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -1618,14 +1618,25 @@
"knowledgebases",
"rehype",
"assetlinks",
"AMPLIFYRULES"
"AMPLIFYRULES",
"manylinux",
"GOARCH",
"norpc"
],
"flagWords": [
"hte",
"full-stack",
"Full-stack",
"Full-Stack",
"sudo"
],
"flagWords": ["hte", "full-stack", "Full-stack", "Full-Stack", "sudo"],
"patterns": [
{
"name": "youtube-embed-ids",
"pattern": "/embedId=\".*\" /"
}
],
"ignoreRegExpList": ["youtube-embed-ids"]
"ignoreRegExpList": [
"youtube-embed-ids"
]
}
3 changes: 3 additions & 0 deletions src/directory/directory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,9 @@ export const directory = {
},
{
path: 'src/pages/[platform]/build-a-backend/functions/modify-resources-with-cdk/index.mdx'
},
{
path: 'src/pages/[platform]/build-a-backend/functions/custom-functions/index.mdx'
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import { getCustomStaticPath } from '@/utils/getCustomStaticPath';

export const meta = {
title: 'Custom functions',
description:
'Use another AWS Lambda runtimes like python, golang to perform tasks and customize workflows.',
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
platforms: [
'android',
'angular',
'flutter',
'javascript',
'nextjs',
'react',
'react-native',
'swift',
'vue'
]
};

export function getStaticPaths() {
return getCustomStaticPath(meta.platforms);
}

export function getStaticProps(context) {
return {
props: {
platform: context.params.platform,
meta
}
};
}

AWS Amplify Gen 2 Functions are AWS Lambda functions that can be used to perform tasks and customize workflows in your Amplify app.Functions can be written in Node.js, Python, Go, or any other language supported by AWS Lambda:
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

<Callout warning>

**Note:** Custom runtimes are not supported in Amplify Functions directly. If you need docker support to use a custom runtime for example in Python, Docker is not supported in Amplify Hosting and Amplify backend auto build. You shouldn't use docker build in your function.There is an example of how to use python in a lambda function in the without Docker section below.

</Callout>
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

Technically, you can use any language supported by AWS Lambda. These are NodeJS, Python, Java, .NET, Ruby.
To use other languages in Lambda, such as Go or Rust, use an OS-only runtime.

MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
In this guide, you will learn how to create python and golang functions in Amplify Functions.
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

# Python functions
To get started, create a new directory and a resource file, `amplify/functions/say-hello/resource.ts`. Then, define the Function with `defineFunction`:
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

```ts title="amplify/functions/say-hello/resource.ts"
import { defineFunction } from "@aws-amplify/backend";
import { DockerImage, Duration } from "aws-cdk-lib";
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
import { execSync } from "child_process";
import * as path from "path";
import { fileURLToPath } from "url";
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

const functionDir = path.dirname(fileURLToPath(import.meta.url));

export const sayHelloFunctionHandler = defineFunction(
(scope) =>
new Function(scope, "say-hello", {
handler: "index.handler",
runtime: Runtime.PYTHON_3_9, // or any other python version
timeout: Duration.seconds(20), // default is 3 seconds
code: Code.fromAsset(functionDir, {
bundling: {
image: DockerImage.fromRegistry("dummy"),
local: {
tryBundle(outputDir: string) {
execSync(
`python3 -m pip install -r ${path.join(functionDir, "requirements.txt")} -t ${path.join(outputDir)} --platform manylinux2014_x86_64 --only-binary=:all:`
);
execSync(`rsync -rLv ${functionDir}/* ${path.join(outputDir)}`);
return true;
},
},
},
}),
})
);
```

Next, create the corresponding handler file at `amplify/functions/say-hello/index.py`. This is where your function code will go.

```ts title="amplify/functions/say-hello/index.py"
import json

def handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({
"message": "Hello World",
}),
}
```

The handler file _must_ export a function named "handler". This is the entry point to your function. For more information on writing functions, refer to the [AWS documentation for Lambda function handlers using python](https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html).
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

If you need python packages, you can add them to a `requirements.txt` file in the same directory as your handler file. The `bundling` option in the `Code.fromAsset` method will install these packages for you.
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
Create a `requirements.txt` file in the same directory as your handler file. This file should contain the names of the packages you want to install. For example:

```txt title="amplify/functions/say-hello/requirements.txt"
request==2.25.1
some-other-package>=1.0.0
```

You're now ready to deploy your python function. Next is the same process as the Node.js/TypeScript function. Go to [Common steps for all languages](#common-steps-for-all-languages) to continue.

# Go functions
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
To get started, install go package from npm. go to your backend directory, you should see amplify folder inside it. and run the following command:
```npm i @aws-cdk/aws-lambda-go-alpha```
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

create a new directory and a resource file, `amplify/functions/say-hello/resource.ts`. Then, define the Function with `defineFunction`:
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

```ts title="amplify/functions/say-hello/resource.ts"
import { defineFunction } from "@aws-amplify/backend";
import { DockerImage, Duration } from "aws-cdk-lib";
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
import { execSync } from "child_process";
import * as path from "path";
import { fileURLToPath } from "url";
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

const functionDir = path.dirname(fileURLToPath(import.meta.url));

export const sayHelloFunctionHandler = defineFunction(
(scope) =>
new Function(scope, "say-hello", {
handler: "bootstrap",
runtime: Runtime.PROVIDED_AL2023, // or any other python version
josefaidt marked this conversation as resolved.
Show resolved Hide resolved
timeout: Duration.seconds(3), // default is 3 seconds
jordanvn marked this conversation as resolved.
Show resolved Hide resolved
code: Code.fromAsset(functionDir, {
bundling: {
image: DockerImage.fromRegistry("dummy"),
local: {
tryBundle(outputDir: string) {
execSync(`rsync -rLv ${functionDir}/* ${path.join(outputDir)}`);
execSync(
`cd ${path.join(outputDir)} && GOARCH=amd64 GOOS=linux go build -tags lambda.norpc -o ${path.join(outputDir)}/bootstrap ${functionDir}/main.go`
);
return true;
},
},
},
}),
}),
);
```

Next, create the corresponding handler file at `amplify/functions/say-hello/main.go`. This is where your function code will go.

```go title="amplify/functions/say-hello/main.go"
package main

import (
"context"
"fmt"

"github.com/aws/aws-lambda-go/lambda"
)

type Event struct {
Arguments Arguments `json:"arguments"`
}

type Arguments struct {
Title string `json:"phone"`
Msg string `json:"msg"`
}

func HandleRequest(ctx context.Context, event Event) (string, error) {
fmt.Println("Received event: ", event)

// fmt.Println("Message sent to: ", event.Arguments.Msg)
// You can use lambda arguments in your code

return "Hello World!", nil
}

func main() {
lambda.Start(HandleRequest)
}
```

Then you should run the following command to build the go function:
```bash title="terminal"
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
go mod init lambda
```
then run to install the dependencies.

```bash title="terminal"
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
go mod tidy
```

You're now ready to deploy your golang function. Next is the same process as the Node.js/TypeScript function.

# Common steps for all languages
Lastly, this function needs to be added to your backend.
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

```ts title="amplify/backend.ts"
import { defineBackend } from '@aws-amplify/backend';
// highlight-next-line
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
import { sayHelloFunctionHandler } from './functions/say-hello/resource';

defineBackend({
// highlight-next-line
sayHelloFunctionHandler,
});
```

Now when you run `npx ampx sandbox` or deploy your app on Amplify, it will include your Function.
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

To invoke your Function, we recommend adding your [Function as a handler for a custom query with your Amplify Data resource](/[platform]/build-a-backend/data/custom-business-logic/). This will enable you to strongly type Function arguments and the return statement, and use this to author your Function's business logic. To get started, open your `amplify/data/resource.ts` file and specify a new query in your schema:
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved
MarlonJD marked this conversation as resolved.
Show resolved Hide resolved

```ts title="amplify/data/resource.ts"
import { type ClientSchema, a, defineData } from "@aws-amplify/backend"
import { sayHelloFunctionHandler } from "../functions/say-hello/resource"

const schema = a.schema({
// highlight-start
sayHello: a
.query()
.arguments({
name: a.string(),
})
.returns(a.string())
.handler(a.handler.function(sayHelloFunctionHandler)),
// highlight-end
})

export type Schema = ClientSchema<typeof schema>

export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "iam",
},
})
```