DEV Community

Cover image for Locking Down Your Parse Server Schema in Production
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Locking Down Your Parse Server Schema in Production

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.


Most people set up Parse Server and just roll with it — add classes from the dashboard, tweak fields on the fly, and rely on the schema to “just work.”

But in production? You need control. You need structure. You need schema definitions in code, not ad-hoc changes in a GUI.

Here's how to fully define and lock your Parse Server schema using JavaScript.

Schema Definitions in Code

You can define every class and field explicitly in your Node.js codebase. Here's what a _User and OrgMember class look like:

const UserSchema = [
  {
    className: "_User",
    fields: {
      objectId: { type: "String" },
      createdAt: { type: "Date" },
      updatedAt: { type: "Date" },
      ACL: { type: "ACL" },
      username: { type: "String" },
      password: { type: "String" },
      email: { type: "String" },
      emailVerified: { type: "Boolean" },
      authData: { type: "Object" },
      first_name: { type: "String" },
      last_name: { type: "String" },
      aadhaarStatus: { type: "String" },
      profilePicUrl: { type: "String", required: false },
      sessionPointer: {
        type: "Pointer",
        targetClass: "_Session",
        required: false,
      },
      stripeCustomerId: { type: "String", required: false },
      location: { type: "String", required: false },
      profilePic: { type: "String" },
      verificationToken: { type: "String", required: false },
      appDetail: { type: "String", required: false },
    },
    classLevelPermissions: {
      find: { "*": true },
      count: { "*": true },
      get: { "*": true },
      create: { "*": true },
      update: { "*": true },
      delete: { "*": true },
      addField: { "*": true },
      protectedFields: { "*": [] },
    },
    indexes: {
      _id_: { _id: 1 },
      username_1: { username: 1 },
      email_1: { email: 1 },
    },
  },
  {
    className: "OrgMember",
    fields: {
      objectId: { type: "String" },
      createdAt: { type: "Date" },
      updatedAt: { type: "Date" },
      ACL: { type: "ACL" },
      email: { type: "String", required: true },
      UserPointer: {
        type: "Pointer",
        targetClass: "_User",
        required: false,
      },
      onboard_status: { type: "String", required: false },
      is_admin: { type: "Boolean", required: false, defaultValue: false },
      isActive: { type: "Boolean", required: false, defaultValue: true },
      extraParams: { type: "String", required: false },
    },
    classLevelPermissions: {
      find: { requiresAuthentication: true },
      count: { requiresAuthentication: true },
      get: { requiresAuthentication: true },
      create: { requiresAuthentication: true },
      update: { requiresAuthentication: true },
      delete: { requiresAuthentication: true },
      addField: { requiresAuthentication: true },
      protectedFields: { "*": [] },
    },
    indexes: {
      _id_: { _id: 1 },
    },
  },
];
Enter fullscreen mode Exit fullscreen mode

Parse Server Config with Locked Schema

Once you define your schema, plug it into ParseServer.startApp():

ParseServer.startApp({
  databaseURI: process.env.DATABASE_URI,
  appId: process.env.APP_ID,
  masterKey: process.env.MASTER_KEY,
  serverURL: process.env.SERVER_URL,
  port: 1337,
  publicServerURL: process.env.SERVER_URL,
  schema: {
    definitions: UserSchema,
    lockSchemas: true,
    strict: true,
    recreateModifiedFields: false,
    deleteExtraFields: false,
  },
  serverStartComplete: () => {
    parseServer.expressApp.get("/ready", (req, res) => {
      res.send("true");
    });
  },
});
Enter fullscreen mode Exit fullscreen mode

Important Flags Explained

  • lockSchemas: true
    → No schema changes allowed via API. Schema can only change via code.

  • strict: true
    → Any class not listed in your schema will be deleted (be careful!).

  • recreateModifiedFields: false
    → Prevents automatic field type overrides.

  • deleteExtraFields: false
    → Fields not listed in your schema won’t be deleted.

Env Setup Example

DATABASE_URI=postgres://parse:parse@localhost:5433/parse
APP_ID=parse
MASTER_KEY=parse
SERVER_URL=http://localhost:1337/parse
Enter fullscreen mode Exit fullscreen mode

Result

This setup ensures:

  • You treat your database schema like code, not UI.
  • You get versioned, reviewable schema changes via Git.
  • No accidental field changes happen in prod.

Final Tip

Once this is deployed, you can delete any unused classes (like Role, _Installation, etc.) from the DB — they won’t come back unless defined in schema.

Use this setup if you're serious about Parse in production with PostgreSQL.


LiveAPI helps you get all your backend APIs documented in a few minutes.

With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.

LiveAPI Demo

If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.

Top comments (1)

Collapse
 
dotallio profile image
Dotallio

Treating your schema as code is honestly the only way I've found to keep production sane - love how you laid out the config details. Did you run into any headaches migrating old Parse DBs to locked schemas?