Cloudinary SDK: Upload API
Use the Upload API to upload an image where the source is the URL to an existing image on the web. The API uses the POST method and supplies a string URL.

Upload URL Function

In this example we'll use the node.js SDK to upload an image that is referenced by a web address (URL). We'll supply the URL as a string in a JSON object using the POST method.
We'll use the same process we've been using in previous examples:
code function --> update template --> package --> build --> test --> deploy
The only new concept we'll look at in this example, that hasn't already be covered, is using the POST method, passing input values for POST, and parsing the values out of the POST body in the code.

Upload API

The Cloudinary Upload API provides many options and asset input methods. If you experiment with uploading in the DAM (Digital Asset Manager) Media Library that you can access in your cloud account, you can see the many input types that the Upload API can use: local file system, Web Address, Camera, and more. See the documentation for a complete picture of the Upload API.

Code Function

We begin by initializing the Cloudinary credentials.
cloudinary.config();
Input value are posted in a JSON object. Setting up this POST in Postman would look like this, where we are passing an image_url key with a value that is a URL representing the web address of an image.
Posting an image_url to AWS Lambda Function
In code we parse the body to get the JSON object and then extract the URL.
let imageUrl = "";
let body = JSON.parse(event.body);
if (body.image_url) imageUrl = body.image_url;
The Node.js SDK provides a function call to use the Upload API and return the response as a promise. As in the example with the Admin API we can take advantage of the fact the that function is asysnc and use await to get the response. When using async/await, it a good idea to wrap the awaited function in a try/catch block to handle the fail case.
The Upload API response from Cloudinary contains a lot of valuable information about the image, include the secure URL the at we use to deliver it via a CDN. For a successful upload we will relay the Cloudinary response back to the caller in the function response.
In this code we see and explicit JSON response header. If this is left out, as in previous examples it is assumed to be JSON.
cloudinary.config();
response = {};
let imageUrl = "";
let body = JSON.parse(event.body);
if (body.image_url) imageUrl = body.image_url;
try {
const result = await cloudinary.uploader.upload(imageUrl);
console.log(result);
response.statusCode = 200;
response.headers = { "Content-Type": "application/json" };
response.body = JSON.stringify({ result: result });
} catch (error) {
console.log("error calling upload api");
response.statusCode = 500;
response.headers = { "Content-Type": "application/json" };
response.body = JSON.stringify({ error: error });
}
return response;

Update Template

With the function defined, we can update the Template. We'll add the the Resource definition that links function code to an HTTP request.
Since we're defining the code in the same template as the other functions, we just need to provide a function name: UploadURLFunction as will would appear in the AWS Functions Console, the function name as it appears in the app.js file: uploadURL, the Path: /upload-url, and the method: post.
UploadURLFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.uploadURL
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /upload-url
Method: post
Next, we add the output definitions for the new function. The will create the AWS URL that serves the function. Scroll in the yaml code that contains the UploadURLApi value below to see that the /upload-url path is at the end of the AWS URL. The Outputs also define the ARN (Amazon Resource Name) for the UploadURL Function (the Lambda) and the IAM Role (policy and permissions).
UploadURLApi:
Description: "API Gateway endpoint URL for Prod stage for upload URL function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/upload-url/"
UploadURLFunction:
Description: "Upload URL Lambda Function ARN"
Value: !GetAtt UploadURLFunction.Arn
UploadURLFunctionIamRole:
Description: "Implicit IAM Role created for upload URL function"
Value: !GetAtt UploadURLFunctionRole.Arn

Package

Execute the package command to create package.yaml and set up ARN assignments.
sam package --template-file template.yaml --output-template-file package.yaml --s3-bucket my-bucket

Build

Execute the build command to create the local .aws directory that contains local code for local testing. We don't need to make any changes to the samconfig.toml as we are just adding a new function to the existing app.
sam build

Test

To invoke this function locally, we need to create an event input. Create a file named events/upload-url.json that contains the following JSON. This mocks information we expect to receive in the AWS event object. The functionality we're creating expects a URL to be passed in with a key of image_url, so that's what we need to mock.
We've been creating separate mock events, but you could create a single events/event.json file that mocks data for all functions to be tested locally.
In the body object that is posted to the function, the raw JSON is stringified, so in order to mock the function input the events/upload-url.json should contain the following content. Notice that the strings are escaped as this is a stringified object.
{
"body":"{\"image_url\": \"https://cdn.pixabay.com/photo/2017/07/18/18/24/dove-2516641_1280.jpg\"}"
}
This can be tested with by starting a server and using Postman or cURL as in testing the Admin API. However, let's start by using using local invoke and calling the function directly. We specify the name of the function: UploadURLFunction as defined in the template, the events file that provides our mock input: events/upload-url.json, and the environment variables file that contains the CLOUDINARY_URL credentials: config.json.
Don't forget to add the CLOUDINARY_URL mapped to this new function name in the config.json.
,
"UploadURLFunction": {
"CLOUDINARY_URL": "cloudinary://api_key:[email protected]_name"
}
}
Now invoke the function. The expectation is that the result will be the Cloudinary Upload API result, and you'll be able to find the secure_url to request the image from Cloudinary.
sam local invoke UploadURLFunction -n config.json -e events/upload-url.json

Local Invocation Response

The local invocation will return a response that looks like what is shown below. The code is logging the result so we can clearly see the results returned by Cloudinary as well as the results included in the function response.
sam local invoke UploadURLFunction -n config.json -e events/upload-url.json
Invoking app.uploadURL (nodejs12.x)
Fetching lambci/lambda:nodejs12.x Docker container image......
Mounting /Users/localuser/projects/aws2/first-sam-app/.aws-sam/build/UploadURLFunction as /var/task:ro,delegated inside runtime container
START RequestId: d4a7f486-6bf5-16c4-2d99-85821e2cca4a Version: $LATEST
2020-06-15T16:46:48.942Z d4a7f486-6bf5-16c4-2d99-85821e2cca4a INFO {
asset_id: '86416b6ccae1dcec09ad0c90b7dd2e6d',
public_id: '729afa50fbfaaca429a151af564feec1',
version: 1591397312,
version_id: 'd90079bdd3a94cb31f7133a2beb9ac67',
signature: '92e971f67307ea167be2e2430be68b8ca6ba2efb',
width: 1280,
height: 710,
format: 'jpg',
resource_type: 'image',
created_at: '2020-06-05T22:48:32Z',
tags: [],
bytes: 110695,
type: 'upload',
etag: '729afa50fbfaaca429a151af564feec1',
placeholder: false,
url: 'http://res.cloudinary.com/cloudname/image/upload/v1591397312/729afa50fbfaaca429a151af564feec1.jpg',
secure_url: 'https://res.cloudinary.com/cloudname/image/upload/v1591397312/729afa50fbfaaca429a151af564feec1.jpg',
access_mode: 'public',
overwritten: true,
original_filename: 'dove-2516641_1280'
}
END RequestId: d4a7f486-6bf5-16c4-2d99-85821e2cca4a
REPORT RequestId: d4a7f486-6bf5-16c4-2d99-85821e2cca4a
Init Duration: 918.37 ms
Duration: 1124.45 ms
Billed Duration: 1200 ms
Memory Size: 128 MB
Max Memory Used: 55 MB
{"statusCode":200,"headers":{"Content-Type":"application/json"},
"body":"{\"result\":{\"asset_id\":\"86416b6ccae1dcec09ad0c90b7dd2e6d\",
\"public_id\":\"729afa50fbfaaca429a151af564feec1\",
\"version\":1591397312,\"version_id\":\"d90079bdd3a94cb31f7133a2beb9ac67\",
\"signature\":\"92e971f67307ea167be2e2430be68b8ca6ba2efb\",
\"width\":1280,\"height\":710,\"format\":\"jpg\",
\"resource_type\":\"image\",\"created_at\":\"2020-06-05T22:48:32Z\",
\"tags\":[],\"bytes\":110695,\"type\":\"upload\",
\"etag\":\"729afa50fbfaaca429a151af564feec1\",
\"placeholder\":false,\"url\":\"http://res.cloudinary.com/cloudname/image/upload/v1591397312/729afa50fbfaaca429a151af564feec1.jpg\",
\"secure_url\":\"https://res.cloudinary.com/cloudname/image/upload/v1591397312/729afa50fbfaaca429a151af564feec1.jpg\",
\"access_mode\":\"public\",\"overwritten\":true,
\"original_filename\":\"dove-2516641_1280\"}}"}
You can also run the local web server and test using the localhost URL.
sam local start-api -n config.json
Mounting UrlHelperFunction at http://127.0.0.1:3000/url-helper [GET]
Mounting UploadURLFunction at http://127.0.0.1:3000/upload-url [POST]
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
Mounting ListByTagFunction at http://127.0.0.1:3000/list-by-tag [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-06-15 10:03:59 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Use Post or cURL to execute the function with the local web server.
curl --location --request POST '127.0.0.1:3000/upload-url' \
--header 'Content-Type: text/plain' \
--data-raw '{"image_url":"https://cdn.pixabay.com/photo/2017/07/18/18/24/dove-2516641_1280.jpg"}'

Deploy

When testing is complete, we can deploy the function.
sam deploy
CloudFormation output from the deploy command will provide the global URL that we need to execute the function on the Web. The output below show the output for all of the functions because we have packaged them all in the same template.
CloudFormation outputs from deployed stack
------------------------------------------------------------------------------------------------------------------------------------
Outputs
------------------------------------------------------------------------------------------------------------------------------------
Key ListByTagApi
Description API Gateway endpoint URL for Prod stage for list by tag function
Value https://whd3754g79.execute-api.us-east-1.amazonaws.com/Prod/list-by-tag/
Key UrlHelperdApi
Description API Gateway endpoint URL for Prod stage for url helper function
Value https://whd3754g79.execute-api.us-east-1.amazonaws.com/Prod/url-helper/
Key ListByTagFunctionIamRole
Description Implicit IAM Role created for list by tag function
Value arn:aws:iam::697239927605:role/cld-stack-ListByTagFunctionRole-G76A49EOY0RZ
Key HelloWorldFunctionIamRole
Description Implicit IAM Role created for Hello World function
Value arn:aws:iam::697239927605:role/cld-stack-HelloWorldFunctionRole-1ESSBB2JLUIS5
Key UrlHelperFunctionIamRole
Description Implicit IAM Role created for url helper function
Value arn:aws:iam::697239927605:role/cld-stack-UrlHelperFunctionRole-BY12Z5KFTQ20
Key UploadURLApi
Description API Gateway endpoint URL for Prod stage for upload URL function
Value https://whd3754g79.execute-api.us-east-1.amazonaws.com/Prod/upload-url/
Key UploadURLFunction
Description Upload URL Lambda Function ARN
Value arn:aws:lambda:us-east-1:697239927605:function:cld-stack-UploadURLFunction-1XFM5R7C7W2JE
Key HelloWorldApi
Description API Gateway endpoint URL for Prod stage for Hello World function
Value https://whd3754g79.execute-api.us-east-1.amazonaws.com/Prod/hello/
Key ListByTagFunction
Description List by tag Lambda Function ARN
Value arn:aws:lambda:us-east-1:697239927605:function:cld-stack-ListByTagFunction-1U0HPOYO8ML2Z
Key UploadURLFunctionIamRole
Description Implicit IAM Role created for upload URL function
Value arn:aws:iam::697239927605:role/cld-stack-UploadURLFunctionRole-1FXMV6UO58GRL
Key UrlHelperFunction
Description Url helper Lambda Function ARN
Value arn:aws:lambda:us-east-1:697239927605:function:cld-stack-UrlHelperFunction-KBVPOSF5SVGW
Key HelloWorldFunction
Description Hello World Lambda Function ARN
Value arn:aws:lambda:us-east-1:697239927605:function:cld-stack-HelloWorldFunction-WLHH8OU16U1R
Use cURL or Postman to test the newly deployed function.

Timeout

Sometimes a function that involves connection and integration to another service, whether it's an AWS service or a partner service. Can timeout. Recall that the time out configuration is in the template.yaml and can be increased from the default of 3 seconds. In the config below, it has been increased to 5 seconds.
Globals:
Function:
Timeout: 5
Copy link
On this page
Upload URL Function
Upload API
Code Function
Update Template
Package
Build
Test
Deploy