|
1 | | -Overview |
2 | | --------- |
| 1 | +# Overview |
3 | 2 |
|
4 | | -This sample Cloud Module used in the [Integrating with Third Party Service Tutorial](https://www.parse.com/tutorials/integrating-with-third-party-services) to show developers how to create a Cloud Module. The module showcases sending email using the Mailgun service. To use the official Mailgun module, head over to the [Mailgun Cloud Module guide](https://www.parse.com/docs/cloud_modules_guide#mailgun). |
| 3 | +This sample Cloud Module is used in the [Integrating with Third Party Services Tutorial][tutorial] to show developers how to create a Cloud Module. The module showcases sending email using the Mailgun service, but it can be adapted to use another provider of your choice. |
5 | 4 |
|
6 | | -Mailgun is a set of powerful APIs that allow you to send, receive, track and store email effortlessly. You can check out their service at [www.mailgun.com](http://www.mailgun.com/). To use this module, you will need to head over to the [Mailgun website](http://www.mailgun.com/) and create an account. |
| 5 | +Mailgun is a set of powerful APIs that allow you to send, receive, track and store email effortlessly. You can check out their service at [www.mailgun.com][mailgun]. To use this module, you will need to head over to the Mailgun website and create an account. |
7 | 6 |
|
8 | | -Installation |
9 | | ------------- |
| 7 | +# Installation |
10 | 8 |
|
11 | | - 1. Clone this respository to get the Cloud Module. |
12 | | -``` |
13 | | -git clone https://github.com/ParsePlatform/MyMailCloudModule.git |
14 | | -``` |
| 9 | +1. Clone this repository to get the Cloud Module. |
| 10 | + `git clone https://github.com/ParsePlatform/MyMailCloudModule.git` |
15 | 11 |
|
16 | | - 2. Copy `myMailModule-1.0.0.js` over to your Cloud Code Directory, placing it in the `cloud` directory. |
| 12 | +2. Copy `myMailModule-1.0.0.js` over to your Cloud Code Directory, placing it in the `cloud` directory. |
17 | 13 |
|
18 | | -Usage |
19 | | ------ |
| 14 | +# Usage |
20 | 15 |
|
21 | 16 | To use the module in your Cloud Code functions, start by requiring the module and initializing it with your credentials: |
22 | 17 |
|
@@ -56,4 +51,230 @@ curl -X POST \ |
56 | 51 | https://api.parse.com/1/functions/sendEmailToUser |
57 | 52 | ``` |
58 | 53 |
|
59 | | -For additional information about this Cloud Module, take a look at the [API Reference](https://www.parse.com/docs/js/symbols/Mailgun.html). |
| 54 | +# Tutorial |
| 55 | + |
| 56 | +In this tutorial, we'll take a look at how you would create your own JavaScript module to integrate with a third-party service such as Mailgun. If you are not familiar with [Mailgun][mailgun], take a look at their website to learn more about what they offer. The Mailgun feature most relevant to this tutorial is the ability to send emails. |
| 57 | + |
| 58 | +## 1. Creating Modules |
| 59 | + |
| 60 | +JavaScript has long lacked the ability organize code into multiple files. To solve this problem, the [CommonJS][commonjs] modules standard was created. Along with [many][commonjs-implementations] JavaScript libraries and services, Cloud Code uses this standard to allow creating more modular JavaScript projects. |
| 61 | + |
| 62 | +### 1.1. Loading Files with `require` |
| 63 | + |
| 64 | +All of the JavaScript modules you want to use in Cloud Code must be placed in the `cloud/` directory. To include a module in your project you can use the `require("/path/to/module")` method. |
| 65 | + |
| 66 | +```js |
| 67 | +var myModule = require('cloud/myModule.js'); |
| 68 | +``` |
| 69 | + |
| 70 | +The `require()` method in Cloud Code requires an absolute path to the file that will be imported. If you run into issues loading your module, make sure your path starts with `cloud/`. |
| 71 | + |
| 72 | +### 1.2. The `exports` Object |
| 73 | + |
| 74 | +The `require` function returns an object automatically created in all modules called `exports`. When you create a module you use this `exports` object to define the set of functionality that will be available to users of your module. Take a look at this simple example. |
| 75 | + |
| 76 | +```js |
| 77 | +// myMathModule.js |
| 78 | +exports.addTwoNumbers = function(x, y) { |
| 79 | + return x+y; |
| 80 | +} |
| 81 | + |
| 82 | +// cloud.js |
| 83 | +var math = require('cloud/myMathModule.js'); |
| 84 | +console.log('3 + 4 is ' + math.addTwoNumbers(3,4)); |
| 85 | +``` |
| 86 | + |
| 87 | +You don't need to declare or create the `exports` object anywhere. It is always available when creating modules. All fields and functions added to the `exports` object are handed down by the `require` function. This means that anything not set on the `exports` object remains private to the module. In the following example, the value of PI is not directly accessible from `cloud.js`. |
| 88 | + |
| 89 | +```js |
| 90 | +// myMathModule.js |
| 91 | +var PI = 3.14; // Private |
| 92 | +exports.getPI() { // Public |
| 93 | + return PI; |
| 94 | +} |
| 95 | + |
| 96 | +// cloud.js |
| 97 | +var math = require('cloud/myMathModule.js'); |
| 98 | +console.log(math.PI); // Doesn't work! |
| 99 | +console.log(math.getPI()); // Works! |
| 100 | +``` |
| 101 | + |
| 102 | +## 2. Implementing the Mailgun Module |
| 103 | +Now that we have a good understanding of modules, let's dive into the Mailgun JavaScript Module and take a look at how to implement something similar. |
| 104 | + |
| 105 | +### 2.1. The initialize method |
| 106 | + |
| 107 | +The first part of the module is the `initialize` method. Since most services will require users to provide credentials, creating an `initialize` method is a great way to centralize the location where these are set. |
| 108 | + |
| 109 | +```js |
| 110 | +var url = 'api.mailgun.net/v2'; |
| 111 | +var domain = ''; |
| 112 | +var key = ''; |
| 113 | + |
| 114 | +module.exports = { |
| 115 | + initialize: function(domainName, apiKey) { |
| 116 | + domain = domainName; |
| 117 | + key = apiKey; |
| 118 | + return this; |
| 119 | + }, |
| 120 | + ... |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +The module starts by declaring three private properties. When the `initialize` method is called, the credentials are stored in the private variables. Along with the credentials, we've also stored the api's url in a private property. When the Mailgun API is updated, we will only need to change the url here instead of everywhere in the module. |
| 125 | + |
| 126 | +It would be possible to pass the credentials with each function that uses the Mailgun API. But, by using an `initialize` method, the users of the module only need to set their credentials once like this: |
| 127 | + |
| 128 | +```js |
| 129 | +// cloud.js |
| 130 | + |
| 131 | +// Initialize Your Module |
| 132 | +var client = require('cloud/myMailModule-1.0.0.js'); |
| 133 | +client.initialize('myDomainName', 'myAPIKey'); |
| 134 | + |
| 135 | +// All my Cloud Code functions can use the Mailgun module |
| 136 | +Parse.Cloud.beforeSave... |
| 137 | +Parse.Cloud.define... |
| 138 | +... |
| 139 | +``` |
| 140 | + |
| 141 | +### 2.2. Sending an Email |
| 142 | + |
| 143 | +The main use of our module is to send emails. The function used to do this is remarkably simple, but requires some understanding of the Mailgun API, Parse HTTP requests and general HTTP networking concepts. Let's start by looking at the code. |
| 144 | + |
| 145 | +```js |
| 146 | +module.exports = { |
| 147 | + ... |
| 148 | + sendEmail: function(params, options) { |
| 149 | + return Parse.Cloud.httpRequest({ |
| 150 | + method: "POST", |
| 151 | + url: "https://api:" + key + "@" + url + "/" + domain + "/messages", |
| 152 | + body: params, |
| 153 | + }).then(function(httpResponse) { |
| 154 | + if (options && options.success) { |
| 155 | + options.success(httpResponse); |
| 156 | + } |
| 157 | + }, function(httpResponse) { |
| 158 | + if (options && options.error) { |
| 159 | + options.error(httpResponse); |
| 160 | + } |
| 161 | + }); |
| 162 | + } |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +This function simply calls `Parse.Cloud.httpRequest` with the appropriate parameters. As discussed in the [Cloud Code docs][cloudcode-networking], the `httpRequest` function allows you to send a request to an external web server. This is the key to integrating with third-party services as it allows us to communicate with other APIs. |
| 167 | + |
| 168 | +To determine the values for the `method`, `url` and `body` parameters, we need to take a look at Mailgun's [API reference documentation][mailgun-api-reference]. From this page we can find out that sending an email requires doing a POST request to the URL `https://api.mailgun.net/v2/{DomainName}/messages`. But how do we provide our Mailgun credential? Well, the majority of web APIs support basic HTTP authentication. This means we can add the credentials to the URL in the following format. |
| 169 | + |
| 170 | +``` |
| 171 | +https://api:apikey@api.url.here |
| 172 | +``` |
| 173 | + |
| 174 | +In the case of Mailgun, if we combine the URL and the credential we get: |
| 175 | + |
| 176 | +``` |
| 177 | +https://api:{ApiKey}@api.mailgun.net/v2/{DomainName}/messages |
| 178 | +``` |
| 179 | + |
| 180 | +So the value of `method` needs to be `POST` and the value of `url` should be the address shown above. As you can see from the function's code (repeated below for convenience), the value of `body` is simply set to the `params` variable which is supplied to the `sendEmail` function. |
| 181 | + |
| 182 | +```js |
| 183 | +module.exports = { |
| 184 | + ... |
| 185 | + sendEmail: function(params, options) { |
| 186 | + return Parse.Cloud.httpRequest({ |
| 187 | + method: "POST", |
| 188 | + url: "https://api:" + key + "@" + url + "/" + domain + "/messages", |
| 189 | + body: params, |
| 190 | + }).then(function(httpResponse) { |
| 191 | + if (options && options.success) { |
| 192 | + options.success(httpResponse); |
| 193 | + } |
| 194 | + }, function(httpResponse) { |
| 195 | + if (options && options.error) { |
| 196 | + options.error(httpResponse); |
| 197 | + } |
| 198 | + }); |
| 199 | + } |
| 200 | +} |
| 201 | +``` |
| 202 | + |
| 203 | +There are a variety of parameters that third party services will accept in the body of their requests, and these are subject to change at any time. Instead of hard coding a list of accepted parameters, our module lets the user provide a list of them and then relays them to Mailgun. This makes the module easier to update in the future and allows the module's API to be much more flexible. In the case of sending an email with Mailgun, the list of accepted body parameters can be found in their [API reference documentation][mailgun-api-reference]. |
| 204 | + |
| 205 | +The `success` and `error` parameters are two callback functions that are provided to the `sendEmail` function through the `options` parameters. The `httpRequest` method returns a [Promise][javascript-sdk-promises]. The promise's `then` method invokes the appropriate success or error callback based on whether the promise is resolved or rejected. |
| 206 | + |
| 207 | +Let's see what a call to the `sendEmail` method looks like. |
| 208 | + |
| 209 | +```js |
| 210 | +// First we initialize our module |
| 211 | +var client = require('cloud/myMailModule-1.0.0.js'); |
| 212 | +client.initialize('myDomainName', 'myAPIKey'); |
| 213 | + |
| 214 | +// Then we create a cloud function |
| 215 | +Parse.Cloud.define("sendEmailToUser", function(request, response) { |
| 216 | + client.sendEmail({ |
| 217 | + to: "email@example.com", |
| 218 | + from: "MyMail@CloudCode.com", |
| 219 | + subject: "Hello from Parse!", |
| 220 | + text: "Using Parse and My Mail Module is great!" |
| 221 | + }).then(function(httpResponse) { |
| 222 | + response.success("Email sent!"); |
| 223 | + }, function(httpResponse) { |
| 224 | + console.error(httpResponse); |
| 225 | + response.error("Uh oh, something went wrong"); |
| 226 | + }); |
| 227 | +}); |
| 228 | +``` |
| 229 | + |
| 230 | +In the example above, a Cloud Function named `sendEmailToUser` is defined that calls the `sendEmail` module method, passing in the parameters needed to send out an email. The success and error cases are echoed back to the user in the response. |
| 231 | + |
| 232 | +### 2.3. Versioning |
| 233 | + |
| 234 | +Versioning the module helps developers know which version they're currently running. While this is not a requirement, it's a good practice. We've named our module file `myMailModule-1.0.0.js` to indicate that this is version 1.0.0. Additionally, let's add a property to our module that provides versioning information. |
| 235 | + |
| 236 | +```js |
| 237 | +module.exports = { |
| 238 | + ... |
| 239 | + version: '1.0.0', |
| 240 | + ... |
| 241 | +``` |
| 242 | +
|
| 243 | +This makes the version information accessible via code. The example below shows a modified `sendEmailToUser` Cloud Function that includes the version in the text of the email sent out. |
| 244 | +
|
| 245 | +```js |
| 246 | +// Modify the message sent to include the version |
| 247 | +Parse.Cloud.define("sendEmailToUser", function(request, response) { |
| 248 | + client.sendEmail({ |
| 249 | + to: "email@example.com", |
| 250 | + from: "MyMail@CloudCode.com", |
| 251 | + subject: "Hello from Parse!", |
| 252 | + text: "Using Parse and My Mail Module version " + client.version + " is great!" |
| 253 | + }).then(function(httpResponse) { |
| 254 | + response.success("Email sent!"); |
| 255 | + }, function(httpResponse) { |
| 256 | + console.error(httpResponse); |
| 257 | + response.error("Uh oh, something went wrong"); |
| 258 | + }); |
| 259 | +}); |
| 260 | +``` |
| 261 | +
|
| 262 | +And that's all there is to know about creating modules for Cloud Code! Make sure to take a look at the |
| 263 | +
|
| 264 | +- [Cloud Code guide][cloud-code-guide], |
| 265 | +- [JavaScript Modules guide][javascript-modules-guide], and |
| 266 | +- [JavaScript SDK Guide][javascript-sdk-guide] |
| 267 | +
|
| 268 | +If you have any questions or comments, visit our [Help Center][parse-help]. |
| 269 | +
|
| 270 | +[tutorial]: #Tutorial |
| 271 | +[cloud-code-guide]: https://www.parse.com/docs/cloudcode/guide |
| 272 | +[javascript-modules-guide]: https://www.parse.com/docs/cloudcode/guide#cloud-code-advanced-modules |
| 273 | +[javascript-sdk-guide]: https://www.parse.com/docs/js/guide |
| 274 | +[javascript-sdk-promises]: https://www.parse.com/docs/js/guide#promises |
| 275 | +[cloudcode-networking]: https://www.parse.com/docs/cloudcode/guide#cloud-code-advanced-networking |
| 276 | +[mailgun]: http://www.mailgun.com/ |
| 277 | +[commonjs]: http://wiki.commonjs.org/wiki/Modules/1.1.1 |
| 278 | +[commonjs-implementations]: http://wiki.commonjs.org/wiki/CommonJS#Implementations |
| 279 | +[mailgun-api-reference]: http://documentation.mailgun.com/api-sending.html |
| 280 | +[parse-help]: https://parse.com/help |
0 commit comments