# 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('downloadImage',{
  url: 'https://example.com/file',
  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.define('downloadImage', 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)

