227 lines
6.8 KiB
Markdown
227 lines
6.8 KiB
Markdown
|
|
# Upgrading Parse Server to version 3.0.0
|
|||
|
|
|
|||
|
|
parse-server 3.0.0 comes also with the JS SDK version 2.0 which requires a migration. Follow the migration guide [here](https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md).
|
|||
|
|
|
|||
|
|
With the 3.0.0, parse-server has completely revamped it's cloud code interface. Gone are the backbone style calls, welcome promises and async/await.
|
|||
|
|
|
|||
|
|
In order to leverage those new nodejs features, you'll need to run at least node 8.10. We've dropped the support of node 6 a few months ago, so if you haven't made the change yet, now would be a really good time to take the plunge.
|
|||
|
|
|
|||
|
|
## Migrating Cloud Code Hooks
|
|||
|
|
|
|||
|
|
### Synchronous validations:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// before
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', function(request, response) {
|
|||
|
|
if (!passesValidation(req.object)) {
|
|||
|
|
response.error('Ooops something went wrong');
|
|||
|
|
} else {
|
|||
|
|
response.success();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// after
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', (request) => {
|
|||
|
|
if (!passesValidation(req.object)) {
|
|||
|
|
throw 'Ooops something went wrong';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
All methods are wrapped in promises, so you can freely `throw` `Error`, `Parse.Error` or `string` to mark an error.
|
|||
|
|
|
|||
|
|
### Asynchronous validations:
|
|||
|
|
|
|||
|
|
For asynchronous code, you can use promises or async / await.
|
|||
|
|
|
|||
|
|
Consider the following beforeSave call that would replace the contents of a fileURL with a proper copy of the image as a Parse.File.
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// before
|
|||
|
|
Parse.Cloud.beforeSave('Post', function(request, response) {
|
|||
|
|
Parse.Cloud.httpRequest({
|
|||
|
|
url: request.object.get('fileURL'),
|
|||
|
|
success: function(contents) {
|
|||
|
|
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
|
|||
|
|
file.save({
|
|||
|
|
success: function() {
|
|||
|
|
request.object.set('file', file);
|
|||
|
|
response.success();
|
|||
|
|
},
|
|||
|
|
error: response.error
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
error: response.error
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
As we can see the current way, with backbone style callbacks is quite tough to read and maintain.
|
|||
|
|
It's also not really trivial to handle errors, as you need to pass the response.error to each error handlers.
|
|||
|
|
|
|||
|
|
Now it can be modernized with promises:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// after (with promises)
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', (request) => {
|
|||
|
|
return Parse.Cloud.httpRequest({
|
|||
|
|
url: request.object.get('fileURL')
|
|||
|
|
}).then((contents) => {
|
|||
|
|
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
|
|||
|
|
request.object.set('file', file);
|
|||
|
|
return file.save();
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
And you can even make it better with async/await.
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// after with async/await
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', async (request) => {
|
|||
|
|
const contents = await Parse.Cloud.httpRequest({
|
|||
|
|
url: request.object.get('fileURL')
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const file = new Parse.File('image.png', { base64: contents.buffer.toString('base64') });
|
|||
|
|
request.object.set('file', file);
|
|||
|
|
await file.save();
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Aborting hooks and functions
|
|||
|
|
|
|||
|
|
In order to abort a `beforeSave` or any hook, you now need to throw an error:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// after with async/await
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', async (request) => {
|
|||
|
|
// valid, will result in a Parse.Error(SCRIPT_FAILED, 'Something went wrong')
|
|||
|
|
throw 'Something went wrong'
|
|||
|
|
// also valid, will fail with Parse.Error(SCRIPT_FAILED, 'Something else went wrong')
|
|||
|
|
throw new Error('Something else went wrong')
|
|||
|
|
// using a Parse.Error is also valid
|
|||
|
|
throw new Parse.Error(1001, 'My error')
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Migrating Functions
|
|||
|
|
|
|||
|
|
Cloud Functions can be migrated the same way as cloud code hooks.
|
|||
|
|
In functions, the response object is not passed anymore, as it is in cloud code hooks
|
|||
|
|
|
|||
|
|
Continuing with the image downloader example:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// before
|
|||
|
|
Parse.Cloud.define('downloadImage', function(request, response) {
|
|||
|
|
Parse.Cloud.httpRequest({
|
|||
|
|
url: request.params.url,
|
|||
|
|
success: function(contents) {
|
|||
|
|
const file = new Parse.File(request.params.name, { base64: contents.buffer.toString('base64') });
|
|||
|
|
file.save({
|
|||
|
|
success: function() {
|
|||
|
|
response.success(file);
|
|||
|
|
},
|
|||
|
|
error: response.error
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
error: response.error
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
You would call this method with:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
Parse.Cloud.run({
|
|||
|
|
url: '....',
|
|||
|
|
name: 'my-file'
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
To migrate this function you would follow the same practices as the ones before, and we'll jump right away to the async/await implementation:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// after with async/await
|
|||
|
|
Parse.Cloud.beforeSave('MyClassName', async (request) => {
|
|||
|
|
const {
|
|||
|
|
url, name
|
|||
|
|
} = request.params;
|
|||
|
|
const response = await Parse.Cloud.httpRequest({ url });
|
|||
|
|
|
|||
|
|
const file = new Parse.File(name, { base64: response.buffer.toString('base64') });
|
|||
|
|
await file.save();
|
|||
|
|
return file;
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Migrating jobs
|
|||
|
|
|
|||
|
|
As with hooks and functions, jobs don't have a status object anymore.
|
|||
|
|
The message method has been moved to the request object.
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
// before
|
|||
|
|
Parse.Cloud.job('downloadImageJob', function(request, status) {
|
|||
|
|
var query = new Parse.Query('ImagesToDownload');
|
|||
|
|
query.find({
|
|||
|
|
success: function(images) {
|
|||
|
|
var done = 0;
|
|||
|
|
for (var i = 0; i<images.length; i++) {
|
|||
|
|
var image = images[i];
|
|||
|
|
status.message('Doing '+image.get('url'));
|
|||
|
|
Parse.Cloud.httpRequest({
|
|||
|
|
url: image.get('url'),
|
|||
|
|
success: function(contents) {
|
|||
|
|
status.message('Got '+image.get('url'));
|
|||
|
|
const file = new Parse.File(image.get('name'), { base64: contents.buffer.toString('base64') });
|
|||
|
|
file.save({
|
|||
|
|
success: function() {
|
|||
|
|
status.message('Saved '+image.get('url'));
|
|||
|
|
done++;
|
|||
|
|
if (done == images.length) {
|
|||
|
|
status.success();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function() {
|
|||
|
|
response.message('Error '+image.get('url'));
|
|||
|
|
done++;
|
|||
|
|
if (done == images.length) {
|
|||
|
|
status.error();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
error: status.error
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
Parse.Cloud.job('downloadImageJob', async function(request) {
|
|||
|
|
const query = new Parse.Query('ImagesToDownload');
|
|||
|
|
const images = await query.find();
|
|||
|
|
const promises = images.map(async (image) => {
|
|||
|
|
request.message('Doing ' + image.get('url'));
|
|||
|
|
const contents = await Parse.Cloud.httpRequest({
|
|||
|
|
url: image.get('url')
|
|||
|
|
});
|
|||
|
|
request.message('Got ' + image.get('url'));
|
|||
|
|
const file = new Parse.File(image.get('name'), { base64: contents.buffer.toString('base64') });
|
|||
|
|
await file.save();
|
|||
|
|
request.message('Saved ' + image.get('url'));
|
|||
|
|
});
|
|||
|
|
await Promise.all(promises);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
As you can see the new implementation is more concise, easier to read and maintain.
|
|||
|
|
|
|||
|
|
If you encounter a problem or discover an issue with this guide, or with any Parse Community project, feel free to [reach out on github](https://github.com/parse-community/parse-server/issues/new/choose)
|
|||
|
|
|