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(request.object)) {
|
||
response.error('Ooops something went wrong');
|
||
} else {
|
||
response.success();
|
||
}
|
||
});
|
||
|
||
// after
|
||
Parse.Cloud.beforeSave('MyClassName', (request) => {
|
||
if (!passesValidation(request.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)
|
||
|