Parse 是 Facebook 提供的雲端服務代管, Bask-end as a Service ,可以當做一個現成的後端+資料庫來使用,前端可以使用 Parse 提供的 framework 來直接操作後端。

  • Parse Core: 資料庫,使用 NoSQL
  • Parse Server
    • Cloud Code: 可以自行定義 js function ,直接用 url 執行或者在特定物件儲存前後執行
    • Push: 整合 mobile 推播
    • 提供完整的會員功能,例如註冊、登入、 Facebook 登入、 email 驗證、忘記密碼等
  • Parse front-end framework
    • 支援 iOS, Android, PHP, Javascript 等
    • 使用 REST API 直接操作後端

會員功能非常完整,很多自己處理很瑣碎的問題都包起來了;而推播功能也整合了 iOS 和 Android ,可以簡單的一步驟推播。

因為功能完整、使用也快速容易,免費方案拿來做 MVP 綽綽有餘(參考:為什麼你該用 Parse 來打造 MVP),可以專注在應用程式上面。

然而好景不常, 2016 年初 Parse 宣布一年後關閉,用一年的時間讓運行在上面的服務們移轉。但也提供工具讓開發者們方便移轉,也把整個 Parse 給 open source 出去。

作為被影響到的服務,這邊就簡單介紹一下我用了哪些功能、怎麼移轉,整個移轉過程還算方便順利,基本上移轉後和原本的 Parse 也有 87% 像,不過要自己 host 、自己照顧好資料庫。

Migration 步驟

Follow 這個文章 How To Migrate from Parse to Self-hosted MongoDB 裡提到的 migration 步驟:

最後整個移轉後的服務:

  • Database: 轉移到 MongoDB 並 host 在 heroku
  • Parse Server: 後端使用 open source 的 parse-server ,並 host 在 heroku
  • Mail server: 使用 heroku 的 SendGrid
  • 前端基本不變,改了連到後端的參數還有小部分程式
Database

如同上面的文章的說明,先把現有的 Parse 後端的資料庫整個移出來。因為要 host 在 heroku ,就先在 heroku 上開好了一個空 app 並新增 mLab (MongoDB plugin) ,然後在 Parse 的 Dashboard 中把資料中的資料轉到這個 MongoDB 資料庫。

要注意的是 heroku 上的 mLab 不能直接改 plan ,如果要從免費版變付費版必須新增另一個、再把資料倒過去,所以如果怕麻煩可以在 migrate 前就先使用付費版。

Parse Server

使用 open source 的 Parse Server ,本體使用 Node.js 開發,有幾種不同的安裝方式,可以參考這篇文章:如何自行架設 Parse API Server

  • 直接安裝 npm module 的 parse-server ,不過只適合在本機運行
  • 建立新的 Express 專案
  • 把 parse-server 掛載到現有的 Express 專案

我使用第二種方法,可以參考上面那篇文章來設定。所有設定基本上都在 app.js ,這邊是我的全部設定:

var ParseServer = require('parse-server').ParseServer;
var config = require('./config');
var SimpleSendGridAdapter = require('parse-server-sendgrid-adapter');
var MailTemplateAdapter = require('parse-server-mail-template-adapter');
var api = new ParseServer({
  cloud: path.join(__dirname, 'cloud/main.js'), // Provide an absolute path
  databaseURI: config.mongodb.uri,
  serverURL: config.parse.serverURL + '/parse',
  appId: config.parse.appId,
  masterKey: config.parse.masterKey,
  clientKey: config.parse.clientKey,
  restAPIKey: config.parse.restAPIKey,
  javascriptKey: config.parse.javascriptKey,
  fileKey: config.parse.fileKey,
  publicServerURL: config.parse.serverURL + '/parse',
  verifyUserEmails: true,
  appName: '{appname}',
  emailAdapter: MailTemplateAdapter({
    adapter: SimpleSendGridAdapter({
      apiKey: config.sendgrid.apiKey,
      fromAddress: config.sendgrid.fromAddress,
    }),
    template: {
      verification: {
        subject: "%appname% Email 確認信  Please verify your e-mail for %appname%",
        bodyFile: "./mail/VerificationEmailBody.txt",
      },
      resetPassword: {
        subject: "Password Reset Request for %appname%",
        bodyFile: "./mail/ResetPasswordEmail.txt",
      }
    }
  }),
  push: {
    ios: config.ios_cert
  }
});
config

該設的 key 要設好,不要上傳到暴露的 repo 。 heroku 的 environment variables 很方便,可以把 key 直接設在裡面並用另外的 config 直接取用。除了 key 之外的各種敏感資料也都設定成 environment variables 。

fileKey

Parse 資料庫可以存檔案,其實機制是把檔案存在 Parse 的 filesystem 裡,從 DB 的 key 找到檔案。移轉資料庫只是把 key 移過去、檔案本身並沒有過去,因此在新的 parse server 上找不到檔案。目前 Parse 還沒有提供良好的檔案移轉方法,必須在新建的 parse server 上提供和原本 Parse 上的 app 同樣的 fileKey ,parse server 看到檔案的時候就會去原本的 Parse app 裡面找檔案。這方法顯然只能在目前 work 、在未來 Parse 整個關機前勢必得把檔案移出來,就不知道 Parse 接下來會怎麼做了

cloud

原本 Parse 的 Cloud Code 裡放的 main.js 可以直接無痛搬到新建的 parse server 專案中,在 cloud 上指定位置就好。

verifyUserEmails

因為原本的服務有 email 驗證功能所以要設為 true ,預設為 false

emailAdapter

parse-server 預設的 mail server 其實是 Mailgun , heroku 上也有提供,不過設定比較麻煩所以就還是選用熟悉的 SendGrid
template 是設定 email 驗證信和找回密碼信的 template ,使用 parse-server-mail-template-adapter 這個 module 來設定。

push

依照官方說明設定,由於前端只有 iOS App ,就只提這部份:

push: {
  ios: [
    {
      pfx: '', // Dev PFX or P12
      passphrase: '', // optional password to your p12
      bundleId: '',
      production: false // Dev
    },
    {
      pfx: '', // Prod PFX or P12
      passphrase: '', // optional password to your p12
      bundleId: '',  
      production: true // Prod
    }
  ]
}

這邊比較困擾的是 Apple 發的憑證 P12 ,heroku 本身沒有 filesystem 可以使用,所以就要另外找地方放、或是直接上傳到 repo ,但如果是 public repo 就不能這樣做。

最後作法是直接上傳到 repo 但使用密碼加密,密碼也就是 passphrase 則藏在環境變數裡。

後端部分大約這樣就設定完成了。

前端
設定修改

以 Swift 為例(記得更新 Parse framework ),原本的:

Parse.setApplicationId("{appId}", clientKey: "{clientKey}")

改為:

let config = ParseClientConfiguration(block: {
    (ParseMutableClientConfiguration) -> Void in
    ParseMutableClientConfiguration.applicationId = "{appId}";
    ParseMutableClientConfiguration.clientKey = "{clientKey}";
    ParseMutableClientConfiguration.server = "http://{server}/parse";
});
Parse.initializeWithConfiguration(config);

就好了!

其他細微修改

在進行資料比對時,例如判斷查回來的使用者就是現在的使用者,寫了像是這樣的 code :

// user is an Object with type PFUser
if user == PFUser.currentUser()! {
    ...
}

也就是物件比對,其實這樣的寫法是不好的,因為真正想做的不是物件比對,而是 id 比對,但原本在 Parse 可以通過。

移轉到 Parse Server 後,原本這樣的比對在 id 相同的情況下由 true 變成 false 了!所以就改寫成:

// user is an Object with type PFUser
if user.objectId == PFUser.currentUser()!.objectId {
    ...
}

其他類似的比對也都要修改。

Reference

parse server
https://github.com/ParsePlatform/parse-server
setup parse server:
http://www.jollen.org/blog/2016/02/how-to-setup-parse-api-server.html
http://www.appcoda.com.tw/parse-server-installation/
migrate data
http://www.appcoda.com/parse-server-migration/
parse server push
https://github.com/ParsePlatform/parse-server/wiki/Push
parse server send mail
https://github.com/ParsePlatform/parse-server
mongodb
https://blog.gtwang.org/programming/getting-started-with-mongodb-shell-1/
deploy parse server to heroku
https://devcenter.heroku.com/articles/deploying-a-parse-server-to-heroku