Hills 🏔 and Skills, What's Common?

They both need you to be on top.

Cohort-1 just ended. You will get:

All yours, just at:

$99

Invoke Lambda & other AWS Services in React Native without AWS SDK

Updated on
call aws apis from react native banner

React Native is awesome, just like you. We all love it, but building apps without calling a bunch of APIs is impossible. AWS provides you with all the services like AWS Lambda, S3, API Gateway, DynamoDB and everything which your app might need.

Building APIs in a backend to interact with every service you need might be just too much work - when you can just use IAM and Cognito to directly authorize AWS services.

Why not use AWS SDK in React Native?

You’ll be asking - why not just use AWS SDK for JavaScript, .NET and Java like we do in our backend?

You definitely can, if you want to:

Just @aws-sdk/client-lambda is of 2 MB alone, forget all the dependencies that it has.

If you’re looking for something which can be used in browser and modern JS runtime, check out aws4fetch.

A better solution using native crypto modules

Polyfills don’t always give us the best performance compared to built-in functions. So we can take advantage of newly-created crypto module for React Native which is react-native-quick-crypto.

⚡️ A fast implementation of Node’s crypto module written in C/C++ JSI Up to 58x faster than all other solutions

Create React Native Project

We’ll be using bun as a package manager. Let’s create a new React Native project from scratch.

bunx react-native init Call_AWS_Services

Install libraries

Along with react-native-quick-crypto, we will need react-native-quick-aws4, which will help us generate an AWS v4 signature.

bun add react-native-quick-crypto react-native-quick-aws4

Make sure to follow the instructions on react-native-quick-crypto’s README.

Build and run the React Native app

Start the metro bundler and hit a to build for Android and i to build for iOS.

bun run start

If you get any error while building the library, check out issues page.

Create Lambda Function & User

To test this, we will create a simple Lambda function which will echo the request from itself. You can create all these resources using AWS console, but it’s better to use AWS CDK which will make this 10x faster.

Create a CDK project with TypeScript

Let’s use bunx to initialize our CDK project. Feel free to cancel npm install, and manually run bun i.

mkdir echo-lambda-cdk
bunx cdk init app --language typescript

Make sure you have AWS CLI setup for CDK deployment.

If you’re deploying with CDK for the first time, you’ll need to run bunx cdk bootstrap.

To learn more about CDK, read our CDK tutorial for beginners.

CDK Stack for Echo Lambda, User & Access Keys

It creates the following resources:

It grants the IAM user permission to invoke the Lambda as well.

import * as cdk from "aws-cdk-lib";
import { CfnOutput } from "aws-cdk-lib";
import { Architecture, FunctionUrlAuthType } from "aws-cdk-lib/aws-lambda";
import { NodejsFunction, OutputFormat } from "aws-cdk-lib/aws-lambda-nodejs";
import { Construct } from "constructs";
import { AccessKey, User } from "aws-cdk-lib/aws-iam";

export class EchoLambdaCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const lambdaFn = new NodejsFunction(this, "echo-lambda-fn", {
      functionName: "echo-fn",
      entry: "./lib/echo-fn.ts",
      bundling: {
        format: OutputFormat.ESM,
      },
      architecture: Architecture.ARM_64,
      // prevent Lambda from DDoS
      reservedConcurrentExecutions: 2,
    });

    // create user with permission to invoke lambda and access key
    const echoLambdaUser = new User(this, "echo-fn-invoker-user");

    lambdaFn.grantInvoke(echoLambdaUser);

    const accessKey = new AccessKey(this, "echo-fn-invoker-user-access-key", {
      user: echoLambdaUser,
    });

    new CfnOutput(this, "AccessKeyId", { value: accessKey.accessKeyId });
    new CfnOutput(this, "AccessKeySecret", {
      value: accessKey.secretAccessKey.unsafeUnwrap(),
    });
  }
}

Echo Lambda Function Code

Put echo-fn.ts inside the lib directory.

The Lambda code simply returns the event:

// NOTE: Don't forget to make it async
export const handler = async (e: string) => {
  return e;
};

Get the complete CDK code on LearnAWS GitHub repo.

Setup AWS Client

Use secure react-native-keys instead of hard coding accessKeyId and secretAccessKey.

import {AwsClient} from 'react-native-quick-aws4';

const awsClient = new AwsClient({
  accessKeyId: 'xxx',
  secretAccessKey: 'xxx',
  region: 'ap-south-1', // replace it with your region
});

Code to invoke AWS Lambda Function

Now we define the payload for our API call where we pass these parameters:

Learn more about request parameter on API docs.

The endpoint for AWS Lambda is:

POST /2015-03-31/functions/FunctionName/invocations?Qualifier=Qualifier HTTP/1.1

Thus, we make a function which makes the POST request using fetch method from the client and sets the response into a state.

  const invokeLambda = async () => {
    const payload = {
      functionName: 'echo-fn',
      invocationType: 'RequestResponse',
      payload: JSON.stringify(
        `Hello from LearnAWS.io\nSent at: ${new Date().toTimeString()}`,
      ),
    };
    const lambdaRes = await awsClient.fetch(
      `https://lambda.${awsClient.region}.amazonaws.com/2015-03-31/functions/${payload.functionName}/invocations`,
      {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: payload.payload,
      },
    );
    setRes(await lambdaRes.json());
  };

React Native Component

You can create the desired UI for your app - here we’ve simply added a button and some text.

import {Button, StyleSheet, Text, View} from 'react-native';
import React, {useState} from 'react';
import {AwsClient} from 'react-native-quick-aws4';

const awsClient =  ...

export default function App() {
  const [res, setRes] = useState('');
  const invokeLambda = asyn ()=> ...
  return (
    <View style={styles.container}>
      <Text style={styles.text}>{res}</Text>
      <Button title="Invoke Lambda" onPress={invokeLambda} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {margin: 12},
  text: {
    fontSize: 18,
    fontFamily: 'monospace',
    paddingVertical: 12,
  },
});

Full code can be found on LearnAWS-io/ReactNative-Invoke-Lambda repo.

Application Demo

Initially the response is empty. When we click on Invoke Lambda, the Lambda gets invoked, and we see the response with the timestamp which we are sending in the payload.

What more can be done?

By having AWS IAM access - anything can be done, but should you do it? If you’re building something which is going to be accessible to everyone you should avoid embedding access key and secrets in your application. Build APIs which have authorization and rate limit, then only let the user access it.

AWS Services which can be used for public access

If you are an IAM expert and can set permissions with least privilege, you can use some of the AWS services like:

Can we use all the services for internal applications?

Yes and No. You might want to skip building APIs and directly interact with AWS services from your frontend application itself.

For example if you’re building an S3 file manager for your organization, it might be a good idea to just call the Amazon S3 APIs directly from your frontend. Just make sure you’re giving the least permission to the user who will use the Access Key and Secret.

If your organization is big - and many people would have access to the application, you can:

Creating so many IAM users with Access Key and Secret for every employee can be tedious, so you can just use Amazon Cognito with Identity pool to let people access AWS services and resources.

You can retrieve the Access Key and Secret from the Cognito itself, learn more on the docs.

If a company decides to let go someone they can just disable or delete the account from the Cognito user pool and they won’t have access to AWS services any longer.