Guide

Parse Dashboard

An admin console over every class, query, user and role — the thing you would otherwise build or buy. It is one install away and already wired into the generated project, but it is not bundled, and the reason is worth understanding before you expose it.

What you get

Parse Dashboard is the official web console for a Parse Server. It is a real application, not a table viewer:

AreaWhat it does
BrowserEvery class as a spreadsheet — read, edit, add and delete rows, including pointers and files
Query editorConstraint builder plus raw queries, saved for reuse
Users & rolesInspect accounts, see role membership, reset a password, log a session out
SchemaAdd and remove classes, fields and indexes, and see the CLP for each class
Cloud codeRead the deployed cloud code and job status
PushCompose and send a push notification, with delivery stats
ConfigEdit Parse.Config values live, without a deploy
LogsServer logs, filtered by level

For a decorator-driven project the browser and the schema view are the useful pair: your @ParseClass and @ParseField declarations become the schema at boot, and the dashboard is where you confirm that what you declared is what the database actually has.

Getting it

Two commands. Nothing to configure.

npm install parse-dashboard
npm run dev

It appears at http://localhost:1337/dashboard. The generated app.ts already looks for it:

/**
 * Mount Parse Dashboard at /dashboard, if it is installed.
 *
 * It is NOT a dependency of this project. The dashboard is a bundled React
 * app and pulls in a lot for something plenty of services never expose, so it
 * is opt-in - the same treatment node-cron and swagger-ui-express get.
 *
 * Returns what happened, so the startup banner can tell the truth about it.
 */
function mountDashboard(app: express.Express): 'mounted' | 'absent' | 'unsafe' {
  let ParseDashboard;
  try {
    ParseDashboard = require('parse-dashboard');
  } catch {
    return 'absent';   // not installed: hint, do not fail
  }

  // The dashboard holds the master key. Unauthenticated, it is a full read
  // and write console for your entire database, reachable by anyone who
  // finds the URL - so credentials are not optional.
  const user = process.env.DASHBOARD_USER || 'admin';
  const pass = process.env.DASHBOARD_PASS || (IS_PRODUCTION ? '' : 'change-me-now');

  // Refuse rather than expose. A dashboard that did not start is a bug
  // report; one that started wide open is an incident.
  if (!pass) return 'unsafe';

  const dashboard = new ParseDashboard(
    {
      apps: [{ serverURL: SERVER_URL, appId: APP_ID, masterKey: MASTER_KEY,
               appName: 'my-api' }],
      users: [{ user, pass }],
    },
    {
      // It refuses to serve over plain HTTP unless told to, which is the
      // right default - the master key would be in flight.
      allowInsecureHTTP: !IS_PRODUCTION,
    }
  );

  app.use('/dashboard', dashboard);
  return 'mounted';
}

The startup banner reports which of the three states you are in:

  Docs        http://localhost:1337/api-docs
  Dashboard   http://localhost:1337/dashboard             # installed and mounted
  Dashboard   npm install parse-dashboard, then restart   # not installed
  Dashboard   not mounted: set DASHBOARD_PASS             # production, no password

Why it is not bundled

Three reasons, in order of how much they matter:

So it follows the same rule this library applies to node-cron and swagger-ui-express: detected if present, a one-line hint if not, never a crash.

It holds the master key

This is the whole security story in one sentence The dashboard authenticates to your API with the master key, which bypasses every class-level permission and every row ACL. Whoever reaches the dashboard can read and write everything — the CLP you wrote, the ACLs your triggers apply, none of it is in the way. Treat the URL as you would a database root password.

Credentials

# .env
DASHBOARD_USER=admin
DASHBOARD_PASS=something-long-and-unguessable
DASHBOARD_PASSDevelopmentProduction
set mounts, uses it mounts, uses it
unset mounts with change-me-now, warns every boot refuses to mount, says why

The asymmetry is deliberate. On a laptop, a console you have to configure before you can look at your own data is friction for no benefit. In production, a console that silently came up with a published default password is the worst outcome available, so it does not come up at all.

HTTPS

Parse Dashboard refuses to serve over plain HTTP unless you pass allowInsecureHTTP, because the master key would be travelling in clear. The template passes it only when NODE_ENV is not production. If you terminate TLS at a proxy and the dashboard sees HTTP behind it, that is what trustProxy is for — do not reach for allowInsecureHTTP to make the warning go away.

Read-only operators

Not everyone who needs to look at data needs to change it. Parse Server has a read-only master key, and the dashboard has read-only users to go with it:

// ParseServer options
readOnlyMasterKey: process.env.READ_ONLY_MASTER_KEY,

// ParseDashboard config
apps: [{
  serverURL: SERVER_URL,
  appId: APP_ID,
  masterKey: MASTER_KEY,
  readOnlyMasterKey: process.env.READ_ONLY_MASTER_KEY,
  appName: 'my-api',
}],
users: [
  { user: 'admin',   pass: process.env.DASHBOARD_PASS },
  // Sees everything, can change nothing. Give support and analytics this
  // one rather than sharing the account that can drop a class.
  { user: 'support', pass: process.env.SUPPORT_PASS, readOnly: true },
],
A default is changing Parse Server 9 warns that allowAggregationForReadOnlyMasterKey will default to false in a future version. Aggregation pipelines can contain write-capable stages such as $out and $merge, so a read-only key that may run them is not entirely read-only. Set it explicitly rather than inheriting whichever default your version happens to have.

How it sits with the middleware

The dashboard is mounted at /dashboard, outside MOUNT_PATH, so restrictRoutes never sees its requests. That is not a loophole: the dashboard talks to the API with the master key, and the master key bypasses restrictRoutes anyway.

  // Everything under /api is gated by restrictRoutes.
  app.use(MOUNT_PATH, restrictRoutes);
  app.use(MOUNT_PATH, parseServer.app);

  // /dashboard is not, and does not need to be - it authenticates its own
  // users, and reaches the API as master.
  app.use('/dashboard', dashboard);

Which also means the dashboard is unaffected by allowAuthRoutes and by any CLP you set — it will happily browse a class whose CLP denies everything, as in the orders tutorial. That is expected, and it is a good way to confirm a CLP is doing its job: locked out through the API, visible in the dashboard.

Exposing it in production

In rough order of preference:

For the first option, point a local dashboard at the deployed server — no code change, and nothing extra listening in production:

npx parse-dashboard \
  --appId "$APP_ID" \
  --masterKey "$MASTER_KEY" \
  --serverURL "https://api.example.com/api" \
  --appName production

When it does not appear

SymptomCause
Banner says npm install parse-dashboard Not installed, or installed without restarting
Banner says not mounted: set DASHBOARD_PASS NODE_ENV=production with no password — set one
Parse Dashboard can only be remotely accessed via HTTPS Serving over plain HTTP outside development; terminate TLS, or set trustProxy if a proxy already does
Login page loads, credentials rejected DASHBOARD_USER / DASHBOARD_PASS changed but the server was not restarted
Logs in, then "unable to connect" serverURL is not reachable from the dashboard process — check it includes your mount path, e.g. /api, not just the host
Classes list is empty The models never registered. See Troubleshooting — usually importFiles pointed at .ts with the default ['.js']

Next: Permissions & ACL for the rules the dashboard is deliberately allowed to ignore, and Seeding data for creating the roles you will see in it.