As the Google Cloud Functions (GCF) ecosystem develops there are many useful examples of functions becoming available. However, it is still kind of hard to lock down the HTTP functions. For example, you may want to add authentication and protect the functions from attacks from the open Internet. On the other hand, Google Cloud Endpoints have been around for a little longer and designed specifically for HTTP access. You can restrict Endpoints with API keys, put them behind behind a load balancer to host on a consistent domain, and protect them with Cloud Armor. This post will demonstrate how to import a Nodejs HTTP Cloud Function into an Endpoints app.
The App Engine Nodejs runtime uses the Express, a popular web framework that is not especially opinionated and makes it simple to import a GCF function to an App Engine app. The JavaScript module approach used by GCF makes it easy to port code without changes.
Follow the instructions in the GCF
Quickstart, including
cloning of the GoogleCloudPlatform/nodejs-docs-samples.
The Quickstart exports the function helloGET
, which writes the 'Hello World'
text to the HTTP response. The code for the Quickstart is contained in the
directory nodejs-docs-samples/functions/helloworld
. Notice that the
Quickstart for Node.js in the App
Engine is
contained in the same GitHub repo.
Starting from the GoogleCloudPlatform directory, copy the GCF files to the relevant App Engine directory:
cp functions/helloworld/index.js appengine/hello-world/standard/.
Make a few changes to the App Engine code to import the function as per this line:
var mygcf = require('./index');
and replace the /
route with this:
app.get('/', (req, res) => {
res.status(200);
mygcf.helloGET(req, res);
res.end();
});
Install some extra stuff that is not needed by the HTTP trigger, but used elsewhere in the GCF Quickstart, so we do not need to change the GCF file:
cd appengine/hello-world/standard/
npm install -save @google-cloud/debug-agent
npm install -save pug
npm install -save safe-buffer
Try running the app locally
cd appengine/hello-world/standard
npm start
Check that it runs properly locally:
curl http://localhost:8080
Deploy to App Engine Standard:
gcloud app deploy
Check that the app is working properly
gcloud app browse
Let's try the same thing for the App Engine Endpoints example. Follow the
instructions in Getting Started with Endpoints on App Engine Flexible
Environment.
Replace the string YOUR-PROJECT-ID with your project id in the files
openapi-appengine.yaml
and replace ENDPOINTS-SERVICE-NAME
in app.yaml with the full hostname.
Starting from the GoogleCloudPlatform directory, copy the file:
cp functions/helloworld/index.js endpoints/getting-started/.
cd endpoints/getting-started
Make a few changes to the App Engine code to import the function as per this gist. Specifically, add the following line near the top of app.js:
const mygcf = require('./index');
Replace the echo route with this implementation:
app.post('/echo', (req, res) => {
res.status(200).json({ message: mygcf.helloGET(req, res) }).end();
});
Install the extra npm modules
npm install -save @google-cloud/debug-agent
npm install -save pug
Deploy the endpoint service and backend:
gcloud endpoints services deploy openapi-appengine.yaml
gcloud app deploy
Send requests to the API:
PROJECT_ID={Your project}
ENDPOINTS_HOST=${PROJECT_ID}.appspot.com
ENDPOINTS_KEY={Your key}
curl --request POST \
--header "content-type:application/json" \
--data '{"message":"hello world"}' \
"${ENDPOINTS_HOST}/echo?key=${ENDPOINTS_KEY}"
The solution so far leverages the API of the Endpoints interface. That can be readily changed to better match the semantics of the functions you are porting. Replace your openapi-appengine.yaml and app.js files with the versions from the gist and then redeploy the endpoint service and backend:
gcloud endpoints services deploy openapi-appengine.yaml
gcloud app deploy
Send requests to the new APIs
curl --request GET \
--header "content-type:application/json" \
"${ENDPOINTS_HOST}/helloget?key=${ENDPOINTS_KEY}"
curl --request POST \
--header "content-type:application/json" \
--data '{"name":"Tester"}' \
"${ENDPOINTS_HOST}/hellohttp?key=${ENDPOINTS_KEY}"
The new Endpoints API now is now more appropriate for the GCF example: a GET request to /helloget with no parameters and a POST request to /hellohttp with a parameter called 'name.'