Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add defined schema docs #863

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions _includes/defined-schema/class-level-permissions.md
@@ -0,0 +1,61 @@
# Class Level Permissions

Setting Class Level Permissions through Defined Schema is a good first step into security systems available on Parse Server.

## CLP Parameters

These CLP parameters are available:

- `find`: Control search permissions
- `get`: Control direct ID get permission
- `count`: Control counting objects permission
- `create`: Create permission
- `update`: Update permission
- `delete`: Delete permission
- `protectedFields`: Control get permission at field level

You can set each CLP parameter to add a first strong security layer. This security layer will be applied on the Parse Class and will cover all Parse Objects of the Parse Class.

Note: If you update CLP you do not need to update Parse Objects. CLP is a security layer at Class Level not Object Level. For Object Level permission you can take a look to ALCs. You can use CLPs combined to ACLs to deeply secure your Parse Server.

## CLP Parameter Options

Available options for CLP parameters:

- `role:MyRole`: If you have already created a Parse Role, you can use your created Parse Role (ie: `MyRole`) in CLP keys.
- `requiresAuthentication`: If true an authenticated user will have the permission.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If set to `true`, only authenticated users"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If set to `true`, only authenticated users"

- `*`: Everybody has the permission
- `{}`: if you set the CLP key with `{}` like `create: {}` only calls with your Parse Server Master Key will have the permission
Copy link
Member

@mtrezza mtrezza Mar 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"`role:<roleName>`: If you are making use of Parse Roles you can set the permission based on a role."
...
"If you set the CLP key to `{}`" ... "calls with the Parse Server master key will"


## CLP Protected Fields Parameter

This CLP parameter allows you to restrict access to fields to specific Parse users.

We will take the Parse User Class as example.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"as an example:"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"as an example."


```js
// className: '_User'
{
protectedFields: {
"*": ["authData", "emailVerified", "password", "username"],
},
}
```

Listed keys under `*` will be protected from all users. By default, `authData`, `emailVerified`, `password` are protected.
But in the above example we protect `username` from all users. So a Parse User, even authenticated will not be able to get the `username` of a another Parse User.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove next line between the 2 sentences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove next line between the 2 sentences.


`protectedFields` could be also combined as in the following example.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"following example:"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"following example:"


```js
{
protectedFields: {
"*": ["authData", "emailVerified", "password", "username", "phone", "score"],
"role:Admin": ["password", "authData", "emailVerified"],
"role:VerifiedUser": ["password", "authData", "emailVerified", "score"],
},
}
```

In the example above, a Parse User member of the Parse Role `Admin` will be able to get the `phone` and `score` of another Parse User. A Parse User member of the Parse Role `VerifiedUser` can only get `phone`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" A Parse User who is member of the Parse Role"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" A Parse User who is member of the Parse Role"

If a Parse User is member of `VerifiedUser` and `Admin`, he will have access to `phone` and `score`.
108 changes: 108 additions & 0 deletions _includes/defined-schema/core-classes-fields.md
@@ -0,0 +1,108 @@
# Core Classes/Fields

Parse will never delete these fields on **ALL** classes if not provided in a class schema
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"will never delete the following fields from any class, even if these fields are not defined in a class schema"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still open


- `objectId`
- `createdAt`
- `updatedAt`
- `ACL`

Parse Server will never delete the following fields from any class, even if these fields are not defined in a class schema.

- `_User`

- `username`
- `password`
- `email`
- `emailVerified`
- `authData`

- `_Installation`

- `installationId`
- `deviceToken`
- `channels`
- `deviceType`
- `pushType`
- `GCMSenderId`
- `timeZone`
- `localeIdentifier`
- `badge`
- `appVersion`
- `appName`
- `appIdentifier`
- `parseVersion`

- `_Role`

- `name`
- `users`
- `roles`

- `_Session`

- `user`
- `installationId`
- `sessionToken`
- `expiresAt`
- `createdWith`

- `_Product`

- `productIdentifier`
- `download`
- `downloadName`
- `icon`
- `order`
- `title`
- `subtitle`

- `_PushStatus`

- `pushTime`
- `source`
- `query`
- `payload`
- `title`
- `expiry`
- `expiration_interval`
- `status`
- `numSent`
- `numFailed`
- `pushHash`
- `errorMessage`
- `sentPerType`
- `failedPerType`
- `sentPerUTCOffset`
- `failedPerUTCOffset`
- `count`

- `_JobStatus`

- `jobName`
- `source`
- `status`
- `message`
- `params`
- `finishedAt`

- `_JobSchedule`

- `jobName`
- `description`
- `params`
- `startAfter`
- `daysOfWeek`
- `timeOfDay`
- `lastRun`
- `repeatMinutes`

- `_Audience`
- `objectId`
- `name`
- `query`
- `lastUsed`
- `timesUsed`
- `_Idempotency`
- `reqId`
- `expire`
42 changes: 42 additions & 0 deletions _includes/defined-schema/fields.md
@@ -0,0 +1,42 @@
# Fields

These field types are available on a Parse Schema.

`required`: `boolean`, by default false. Force the field to be set on create and update.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"by default `false`."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

", forces the field to be set on create and update, is `false` by default."

`defaultValue`: `any`, a value used by Parse Server when you create a Parse Object if the field is not provided.
`targetClass`: `string`, a Parse Class name used by Parse Server to validate the `Pointer`/`Relation`

✅: Supported
❌: Not Supported

| Type | -- required -- | -- defaultValue -- | -- targetClass -- |
| -------- | -------------- | ------------------ | ----------------- |
| String | ✅ | ✅ | ❌ |
| Boolean | ✅ | ✅ | ❌ |
| Date | ✅ | ✅ | ❌ |
| Object | ✅ | ✅ | ❌ |
| Array | ✅ | ✅ | ❌ |
| GeoPoint | ✅ | ✅ | ❌ |
| File | ✅ | ✅ | ❌ |
| Bytes | ✅ | ✅ | ❌ |
| Polygon | ✅ | ✅ | ❌ |
| Relation | ❌ | ❌ | ✅ (required) |
| Pointer | ✅ | ❌ | ✅ (required) |

Example:

```js
const UserSchema = {
className: "_User",
fields: {
birthDate: { type: "Date" },
firstname: { type: "String", required: true },
lastname: { type: "String", required: true },
tags: { type: "Array" },
location: { type: "GeoPoint" },
city: { type: "Pointer", targetClass: "City" },
friends: { type: "Relation", targetClass: "_User" },
zone: { type: "Polygon" },
},
};
```
118 changes: 118 additions & 0 deletions _includes/defined-schema/getting-started.md
@@ -0,0 +1,118 @@
# Getting Started

## Introduction

For use cases in which a pre-defined schema is beneficial or required, you can define class fields, indexes, Class Level Permissions and more

## Quick Start

You can use Defined Schema as in the following example.

```js
const { ParseServer } = require("parse-server");

const UserSchema = {
className: "_User",
fields: {
birthDate: { type: "Date" },
firstname: { type: "String", required: true },
lastname: { type: "String", required: true },
tags: { type: "Array" },
location: { type: "GeoPoint" },
city: { type: "Pointer", targetClass: "City" },
friends: { type: "Relation", targetClass: "_User" },
zone: { type: "Polygon" },
},
indexes: {
tagsIndex: { tags: 1 },
// Special _p_ word to create indexes on pointer fields
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"// The special prefix _p_ is used to create indexes on pointer fields"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"// Special p prefix"

cityPointerIndex: { _p_city: 1 },
tagAndCityIndex: { _p_city: 1, tags: 1 },
},
classLevelPermissions: {
find: { requiresAuthentication: true },
count: { "role:Admin": true },
get: { requiresAuthentication: true },
update: { requiresAuthentication: true },
create: { "role:Admin": true },
delete: { "role:Admin": true },
protectedFields: {
// These fields will be protected from all other users
// authData, and password are already protected by default
"*": ["authData", "emailVerified", "password", "username"],
},
},
};

const City = {
className: "City",
fields: {
name: { type: "String", required: true },
location: { type: "GeoPoint" },
country: { type: "Pointer", targetClass: "Country" },
},
classLevelPermissions: {
find: { requiresAuthentication: true },
count: { requiresAuthentication: true },
get: { requiresAuthentication: true },
// Only a user linked into the Admin Parse Role
// authorized to manage cities
update: { "role:Admin": true },
create: { "role:Admin": true },
delete: { "role:Admin": true },
},
};

const Country = {
className: "Country",
fields: {
name: { type: "String", required: true },
},
classLevelPermissions: {
find: { requiresAuthentication: true },
count: { requiresAuthentication: true },
get: { requiresAuthentication: true },
// Empty object meas that only master key
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write comment on 1 line instead of 2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write comment on 1 line instead of 2

// is authorized to manage countries
update: {},
create: {},
delete: {},
},
};

ParseServer.start({
databaseURI: "mongodb://your.mongo.uri",
appId: "myAppId",
masterKey: "mySecretMasterKey",
serverURL: "http://localhost:1337/parse",
port: 1337,
publicServerURL: "http://localhost:1337/parse",
// Then just register schemas into Parse Server
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Define schemas of Parse Server

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Define schemas of Parse Server

schema: {
definitions: [User, City, Country],
// Parse Schema API will be disabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Disable Parse Schema API; if you need to update the schemas later
// you need to redeploy Parse Server with the new schema definition

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, the Parse Server API for schema changes is disabled and schema
// changes are only possible by redeploying Parse Server with a new schema definition

// If you need to update schemas Parse server
// need to be updated and deployed (CI/CD strategy)
lockSchemas: true,
// If true, Parse Server will delete non defined Classes from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, Parse Server will automatically delete non-defined classes from
// the database; internal classes like `User` or `Role` are never deleted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Parse Server will automatically delete non-defined classes from the
// database; internal classes like `User` or `Role` are never deleted.

// the database. (Core classes like Role, User are never deleted)
strict: true,
// If true, a field type change, the changed field is deleted
// from the database (all data in this field will be deleted)
// and then create the field with the new type
recreateModifiedFields: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, a field type change will cause the field including its data to be
// deleted from the database, and then a new field to be created with the new type

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, a field type change will cause the field including all of its data
// being deleted from the database and then recreate the field with the new type

// If true, Parse will delete non defined fields on a class. (Core fields are never deleted)
deleteExtraFields: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, Parse Server will automatically delete non-defined class fields;
// internal fields in classes like User or Role are never deleted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If set to `true`, Parse Server will automatically delete non-defined class fields.
// internal fields in classes like User or Role are never deleted.

},
serverStartComplete: () => {
// Here your Parse Server is ready
// with schemas up to date

// Just a code example if you want to expose
// an endpoint when parse is fully initialized
parseServer.expressApp.get("/ready", (req: any, res: any) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Parse Server is ready with up-to-date schema

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace whole comment block with:

// Parse Server is ready with up-to-date schema

res.send("true");
});
},
});
```
26 changes: 26 additions & 0 deletions _includes/defined-schema/indexes.md
@@ -0,0 +1,26 @@
# Indexes

To optimize the Parse Server performance you can define indexes and compound indexes.

To define an index on a `Pointer` field you need to use a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reformat and reword to:

To optimize the Parse Server performance you can define indexes and compound indexes. Parse Server does not support indexes on special _Join classes used under the hood by the Relation type.

To define an index on a Pointer field you need to use the special notation _p_<FIELDNAME>. For example, if you define city: { type: "Pointer", targetClass: "City" } in your fields you can define an index on this pointer with cityIndexExample: { _p_city: true }.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reformat and reword to:

To optimize the Parse Server performance you can define indexes and compound indexes. Parse Server does not support indexes on special _Join classes used under the hood by the Relation type.

To define an index on a Pointer field you need to use the special notation _p_<FIELDNAME>. For example, if you define city: { type: "Pointer", targetClass: "City" } in your fields you can define an index on this pointer with cityIndexExample: { _p_city: true }.

special notation `_p_<FIELDNAME>`.
For example if you define `city: { type: "Pointer", targetClass: "City" }` in your `fields` you can define an index on this pointer with `cityIndexExample: { _p_city: true }`.

Note: Currently Defined Schemas do not support indexes on special `_Join` classes used under the hood by the `Relation` type.

Example:

```js
const UserSchema = {
className: "_User",
fields: {
tags: { type: "Array" },
city: { type: "Pointer", targetClass: "City" },
},
indexes: {
tagsIndex: { tags: 1 },
cityPointerIndex: { _p_city: 1 },
tagAndCityIndex: { _p_city: 1, tags: 1 },
},
};
```
39 changes: 39 additions & 0 deletions _includes/defined-schema/options.md
@@ -0,0 +1,39 @@
# Options

## definitions

An array of your Defined Parse Classes.

## strict

You can set the `strict` option to `true` if you want Parse Server to delete all Parse Objects when you remove a Defined Parse Class from your `definitions`. Data stored in removed classes will be lost.

`strict` is default to `false`. If you often change your schemas be aware that you will have stale data classes in your database. You will need to delete these classes (collection for MongoDB, table for Postgres) manually, through your database CLI/UI.

## deleteExtraFields

You can set the `deleteExtraFields` option to `true` if you want Parse Server to delete a removed Defined Parse Class field from your database. Data stored in the removed field will be lost.

`deleteExtraFields` is default to `false`. Be aware that you will have stale data fields in your database since Parse Server will not delete field data automatically. You will need to delete these fields manually.

## recreateModifiedFields

You can set the `recreateModifiedFields` option to `true` if you want Parse Server to clean field data before Parse Server update the field type when you change the type of a field (ie: from `String` to `Number`). Data stored on the modified field will be lost.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"before updating the field type when you change the field type, for example from String to Number. Data stored under the modified"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"before updating the field type when you change the field type"


`recreateModifiedFields` is default to `false`. **Be aware that if you do not perform some data migration, you can result with data type inconsistency on modified field.**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`recreateModifiedFields` defaults to `false`.

The "Be aware" comment could be unclear for readers, could you rephrase that? What is "some data migration"? In which cases does the user have to be aware, if the field is true of false?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`recreateModifiedFields` defaults to `false`


On production a good practice could be to create a new field with your new type, and then create a Parse Cloud Job to migrate old field data to the new created field.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Good practice would be to create the new field of the new type" ... "to the newly created field."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Good practice would be to create a new field with a new type" ... "to the newly created field."


## lockSchemas

You can set the `lockSchemas` option to `true` if you want to to prevent any `Parse.Schema` manipulation outside of the Defined Schema feature. If this options is `true` any create/update/delete request to `Parse.Schema` will be denied. You will not be able to manipulate `indexes`, `classLevelPermissions`, `fields`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"if you want to prevent any schema manipulation and to lock the schema as defined in the Parse Server configuration." ... "any create, update and delete request will be denied by the Parse Server API, even with the master key."

You can delete the following sentence "This option help to keep " as it does not provide any additional information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"if you want to prevent any schema manipulation and to lock the schema as defined in the Parse Server configuration."


This option help to keep one source of truth. And prevent developers or custom code to interfere with your schema definitions and data structure, even with the master key.

## beforeMigration

A function called before parse-server performs schema updates based on the `definitions` option
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always use "Parse Server" when you refer to the product, don't use "parse-server" unless you are referring to the repository.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still open

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still open


## afterMigration

A function called after parse-server performed schema updates based on the `definitions` option