copyright | lastupdated | ||
---|---|---|---|
|
2019-03-07 |
{:shortdesc: .shortdesc} {:new_window: target="_blank"} {:codeblock: .codeblock} {:screen: .screen} {:tip: .tip} {:pre: .pre}
{: #serverless-api-webapp}
In this tutorial, you will create a serverless web application by hosting static website content on GitHub Pages and implementing the application backend using {{site.data.keyword.openwhisk}}.
As an event-driven platform, {{site.data.keyword.openwhisk_short}} supports a variety of use cases. Building web applications and APIs is one of them. With web apps, events are the interactions between the web browsers (or REST clients) and your web app, the HTTP requests. Instead of provisioning a virtual machine, a container or a Cloud Foundry runtime to deploy your backend, you can implement your backend API with a serverless platform. This can be a good solution to avoid paying for idle time and to let the platform scale as needed.
Any action (or function) in {{site.data.keyword.openwhisk_short}} can be turned into a HTTP endpoint ready to be consumed by web clients. When enabled for web, these actions are called web actions. Once you have web actions, you can assemble them into a full-featured API with API Gateway. API Gateway is a component of {{site.data.keyword.openwhisk_short}} to expose APIs. It comes with security, OAuth support, rate limiting, custom domain support.
- Deploy a serverless backend and a database
- Expose a REST API
- Host a static website
- Optional: Use a custom domain for the REST API
{: #services}
This tutorial uses the following runtimes and services:
This tutorial may incur costs. Use the Pricing Calculator to generate a cost estimate based on your projected usage.
{: #architecture}
The application shown in this tutorial is a simple guestbook website where users can post messages.
- The user access the application hosted in GitHub Pages.
- The web application calls a backend API.
- The backend API is defined in API Gateway.
- API Gateway forwards the request to {{site.data.keyword.openwhisk_short}}.
- The {{site.data.keyword.openwhisk_short}} actions use {{site.data.keyword.cloudant_short_notm}} to store and retrieve guestbook entries.
{: #prereqs}
This guide uses GitHub Pages to host the static website. Make sure you have a public GitHub account.
Let's start by creating a {{site.data.keyword.cloudant_short_notm}}. {{site.data.keyword.cloudant_short_notm}} is a fully managed data layer designed for modern web and mobile applications that leverages a flexible JSON schema. {{site.data.keyword.cloudant_short_notm}} is built upon and compatible with Apache CouchDB and accessible through a secure HTTPS API, which scales as your application grows.
- In the Catalog, select Cloudant.
- Set the service name to guestbook-db, select Use both legacy credentials and IAM as authentication methods and click Create.
- Back in the Resource list, click on the *guestbook-db entry under the Name column. Note: You may be required to wait until the service is provisioned.
- In the service details screen, click on Launch Cloudant Dashboard which will open in another browser tab. Note: Log in maybe required to your Cloudant instance.
- Click on Create Database and create a database named guestbook.
- Back to the service details tab, Under Service Credentials
- Create New credential, accept the defaults and click Add.
- Click View credentials under Actions. We will need these credentials later to allow Cloud Functions actions to read/write to your Cloudant service.
In this section, you will create serverless actions (commonly termed as Functions). {{site.data.keyword.openwhisk}} (based on Apache OpenWhisk) is a Function-as-a-Service (FaaS) platform which executes functions in response to incoming events and costs nothing when not in use.
You will create a sequence which is a chain of actions where output of one action acts as an input to the following action and so on. The first sequence you will create is used to persist a guest message. Provided a name, an emailID and a comment, the sequence will:
- Create a document to be persisted.
- Store the document in the {{site.data.keyword.cloudant_short_notm}} database.
Start by creating the first action:
- Switch to Functions https://{DomainName}/openwhisk.
- On the left pane, click on Actions and then Create.
- Create Action with name
prepare-entry-for-save
and select Node.js as the Runtime (Note: Pick the lastest version). - Replace the existing code with the code snippet below:
{: codeblock}
/** * Prepare the guestbook entry to be persisted */ function main(params) { if (!params.name || !params.comment) { return Promise.reject({ error: 'no name or comment'}); } return { doc: { createdAt: new Date(), name: params.name, email: params.email, comment: params.comment } }; }
- Save
Then add the action to a sequence:
- Click on Enclosing Sequences and then Add To Sequence.
- For the sequence name, enter
save-guestbook-entry-sequence
and then click Create and Add.
Finally add a second action to the sequence:
- Click on save-guestbook-entry-sequence and then click Add.
- Select Use Public, Cloudant and then choose create-document under Actions
- Create New Binding
- For Name, enter
binding-for-guestbook
- For Cloudant Instance, select
Input your own credentials
and fill in the following fields with the credentials information captured for your cloudant service: Username, Password, Host and Database =guestbook
and click Add and then Save. {: tip} - To test it, click on Change Input and enter the JSON below
{: codeblock}
{ "name": "John Smith", "email": "[email protected]", "comment": "this is my comment" }
- Apply and then Invoke.
The second sequence is used to retrieve the existing guestbook entries. This sequence will:
- List all documents from the database.
- Format the documents and returning them.
- Under Functions, click on Actions and then Create a new Node.js action and name it
set-read-input
. - Replace the existing code with the code snippet below. This action passes the appropriate parameters to the next action.
{: codeblock}
function main(params) { return { params: { include_docs: true } }; }
- Save
Add the action to a sequence:
- Click on Enclosing Sequences, Add to Sequence and Create New
- Enter
read-guestbook-entries-sequence
for the Action Name and click Create and Add.
Complete the sequence:
- Click on read-guestbook-entries-sequence sequence and then click Add to create and add the second action to get documents from Cloudant.
- Under Use Public, choose {{site.data.keyword.cloudant_short_notm}} and then list-documents
- Under My Bindings, choose binding-for-guestbook and Add to create and add this public action to your sequence.
- Click Add again to create and add the third action which will format the documents from {{site.data.keyword.cloudant_short_notm}}.
- Under Create New enter
format-entries
for name and then click Create and Add. - Click on format-entries and replace the code with below:
{: codeblock}
const md5 = require('spark-md5'); function main(params) { return { entries: params.rows.map((row) => { return { name: row.doc.name, email: row.doc.email, comment: row.doc.comment, createdAt: row.doc.createdAt, icon: (row.doc.email ? `https://secure.gravatar.com/avatar/${md5.hash(row.doc.email.trim().toLowerCase())}?s=64` : null) }}) }; }
- Save
- Choose the sequence by clicking on Actions and then read-guestbook-entries-sequence.
- Click on Save and then Invoke. The output should look like the following:
- Go to Actions https://{DomainName}/openwhisk/actions.
- Select the read-guestbook-entries-sequence sequence. Next to the name, click on Web Action, check Enable Web Action and Save.
- Do the same for the save-guestbook-entry-sequence sequence.
- Go to APIs https://{DomainName}/openwhisk/apimanagement and Create a {{site.data.keyword.openwhisk_short}} API
- Set name to
guestbook
and base path to/guestbook
- Click on Create operation and create an operation to retrieve guestbook entries:
- Set path to
/entries
- Set verb to
GET*
- Select the read-guestbook-entries-sequence action
- Set path to
- Click on Create operation and create an operation to persist a guestbook entry:
- Set path to
/entries
- Set verb to
PUT
- Select the save-guestbook-entry-sequence action
- Set path to
- Save and expose the API.
- Fork the Guestbook user interface repository https://github.com/IBM-Cloud/serverless-guestbook to your public GitHub.
- Modify docs/guestbook.js and replace the value of apiUrl with the route given by API Gateway.
- Commit the modified file to your forked repository.
- In the Settings page of your repository, scroll to GitHub Pages, change the source to master branch /docs folder and Save.
- Access the public page for your repository.
- You should see the "test" guestbook entry created earlier.
- Add new entries.
Creating a managed API gives you a default endpoint like https://service.us.apiconnect.ibmcloud.com/gws/apigateway/api/1234abcd/app
. In this section, you will configure this endpoint to be able to handle requests coming from your custom subdomain.
Exposing {{site.data.keyword.openwhisk_short}} actions through a custom domain will require a secure HTTPS connection. You should obtain a SSL certificate for the domain and subdomain you plan to use with the serverless back-end. Assuming a domain like mydomain.com, the actions could be hosted at guestbook-api.mydomain.com. The certificate will need to be issued for guestbook-api.mydomain.com (or *.mydomain.com).
You can get free SSL certificates from Let's Encrypt. During the process you may need to configure a DNS record of type TXT in your DNS interface to prove you are the owner of the domain. {:tip}
Once you have obtained the SSL certificate and private key for your domain make sure to convert them to the PEM format.
- To convert a Certificate to PEM format:
openssl x509 -in domain-crt.txt -out domain-crt.pem -outform PEM
- To convert a Private Key to PEM format:
openssl rsa -in domain-key.txt -out domain-key.pem -outform PEM
- Create a {{site.data.keyword.cloudcerts_short}} instance in a supported location.
- In the service dashboard, use Import Certificate:
- Set Name to the custom subdomain and domain, such as
guestbook-api.mydomain.com
. - Browse for the Certificate file in PEM format.
- Browse for the Private key file in PEM format.
- Import.
- Set Name to the custom subdomain and domain, such as
- Go to APIs / Custom domains.
- In the Region selector, select the location where you deployed the actions.
- Locate the custom domain linked to the organization and space where you created the actions and the managed API. Click Change Settings in the action menu.
- Make note of the Default domain / alias value.
- Check Apply custom domain
- Set Domain name to the domain you will use such as
guestbook-api.mydomain.com
. - Select the {{site.data.keyword.cloudcerts_short}} instance holding the certificate.
- Select the certificate for the domain.
- Set Domain name to the domain you will use such as
- Go to your DNS provider and create a new DNS TXT record mapping your domain to the API default domain / alias. The DNS TXT record can be removed once the settings have been applied.
- Save the custom domain settings. The dialog will check for the existence of the DNS TXT record.
- Finally, return to your DNS provider's settings and create a CNAME record pointing your custom domain (e.g. guestbook-api.mydomain.com) to the Default domain / Alias. This will cause traffic through your custom domain to be routed to your backend API.
Once the DNS changes have been propagated, you will be able to access your guestbook api at https://guestbook-api.mydomain.com/guestbook.
- Edit docs/guestbook.js and update the value of apiUrl with https://guestbook-api.mydomain.com/guestbook
- Commit the modified file.
- Your application now accesses the API through your custom domain.
- Delete {{site.data.keyword.cloudant_short_notm}} service
- Delete API from {{site.data.keyword.openwhisk_short}}
- Delete actions from {{site.data.keyword.openwhisk_short}}