---
title: Cloud code
nav: Cloud code
group: Build your app
updated: 2026-11-13
description: Write and deploy server-side code for your SashiDo app. The GitHub repository, functions, triggers, background jobs, webhooks, your own Express routes and static files, npm packages, environment variables, logs and local development.
keywords: parse cloud code, sashido cloud code, parse server cloud functions, parse beforeSave, parse background jobs, parse server express, parse cloud code github deploy
---

Cloud code is the JavaScript that runs inside your app's Parse Server: functions your clients call, triggers on saves and deletes, background jobs, and your own HTTP routes.

## The repository

Every app has a private GitHub repository. In **Core › Cloud Code**, link your GitHub account and accept the invitation, then clone it:

```text
cloud/
  main.js        ← loaded by Parse Server: require your other files from here
  functions.js
  app.js         ← your own Express routes ("Advanced Cloud Code")
public/          ← static files, served at your app's public URL
package.json     ← npm dependencies, installed on every build
index.js         ← runs the app locally (see below)
```

Keep `cloud/main.js` as the entry point: SashiDo loads that path.

## Deploy

Push to the repository's `master` branch. SashiDo builds the code (installing the dependencies in `package.json`) and deploys it, one deployment at a time. Follow it in **Runtime › Builds** and **Runtime › Deployments**; a failed build keeps the previous version running, and its log says why.

From a terminal or CI: `POST /apps/{id}/build` and `/deploy` with the [Management API](/en/docs/developers/api#builds), or ask your AI assistant to deploy and wait (`deploy_app`, `wait_for_deployment`).

## Functions

```javascript
// cloud/functions.js
Parse.Cloud.define('averageRating', async (request) => {
  const { movie } = request.params;
  if (!movie) throw new Parse.Error(Parse.Error.INVALID_QUERY, 'movie is required');
  const reviews = await new Parse.Query('Review').equalTo('movie', movie).find();
  return reviews.reduce((sum, r) => sum + r.get('stars'), 0) / (reviews.length || 1);
});
```

```javascript
// a client
const rating = await Parse.Cloud.run('averageRating', { movie: 'Arrival' });
```

`request.user` is the logged-in caller (or `undefined`), `request.master` says whether the master key was used. Queries in a function run with the caller's permissions unless you pass `{ useMasterKey: true }`.

## Triggers

| Trigger | Runs | Typical use |
|---|---|---|
| `beforeSave` | Before an object is saved | Validate, normalize, set the ACL |
| `afterSave` | After it's saved | Counters, notifications, sync to other systems |
| `beforeDelete` / `afterDelete` | Around a delete | Refuse, clean up related objects |
| `beforeFind` / `afterFind` | Around a query | Limit what a query may return |

```javascript
Parse.Cloud.beforeSave('Review', (request) => {
  const stars = request.object.get('stars');
  if (!(stars >= 1 && stars <= 5)) {
    throw new Parse.Error(Parse.Error.VALIDATION_ERROR, 'stars must be 1 to 5');
  }
});
```

Throwing refuses the operation, and the client gets the error. Triggers run on every matching request, so keep them fast: see [Performance](/en/docs/developers/performance#cloud-code).

## Background jobs

Long or periodic work (cleanups, reports, imports) goes in a job:

```javascript
Parse.Cloud.job('cleanupOldSessions', async (request) => {
  const { message } = request;
  message('Starting');
  // ... work in batches
  return 'Done';
});
```

Schedule it in **Core › Jobs** (once, or repeating), or run it from the dashboard. Each run's status and message are kept, and through [MCP](/en/docs/developers/mcp) your assistant can run a job and wait for its result.

## Your own routes and static files

`cloud/app.js` exports an Express app, mounted at the root of your app's public URL (`https://pg-app-<id>.scalabl.cloud/`):

```javascript
// cloud/app.js
const express = require('express');
const app = express();

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  // verify the signature, then handle the event
  res.sendStatus(200);
});

module.exports = app;
```

`cloud/main.js` keeps the line `module.exports.app = require('./app');`. Files in `public/` are served as they are, e.g. `public/index.html` at the public URL. With a custom domain (**App Settings › Emails & Hosting › Host Name**, with a free SSL certificate) both answer on your domain.

## Webhooks

Functions and triggers can also run on your own server: **Core › Webhooks** points a function name or a class trigger at a URL. Parse calls it with the request and checks the answer; your server verifies the `X-Parse-Webhook-Key` header (the **Webhook Key** in **App Settings › Security & Keys**).

## Secrets and configuration

Set API keys and settings as [environment variables](/en/docs/developers/api#environment-variables) (dashboard **Runtime › Environment Variables**) and read them with `process.env.STRIPE_KEY`. Never commit them to the repository.

## Logs

`console.log` and `console.error` in cloud code go to **Core › Logs**, next to Parse's own errors (e.g. a function that threw). Your assistant can read them with `get_logs` and `get_error_logs`.

## Run it locally

Run your cloud code on your machine with a local Parse Server, and test functions and triggers automatically before you push: see [Local development and testing](/en/docs/developers/local-development).

## Write code for your version

The Parse Server version is in **Runtime › Parse Server Version**. Since Parse Server 3, functions and triggers are `async` and return or throw; `response.success()` and `response.error()` are gone. `plan_parse_server_upgrade` through [MCP](/en/docs/developers/mcp) lists, file by file, what your code needs before a version upgrade.
