-
-
Notifications
You must be signed in to change notification settings - Fork 515
feat: Add defined schema docs #912
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
Merged
mtrezza
merged 7 commits into
parse-community:gh-pages
from
Moumouls:moumouls/defined-docs
Dec 25, 2022
Merged
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b8bb023
feat: defined schema docs
Moumouls 2b651bb
fix: requested changes
Moumouls 938474c
fix: add bold
Moumouls 4a9fc1a
accounting for feedback from PR 863
epietrowicz 8af9deb
Merge branch 'gh-pages' into defined-schemas-updates
Moumouls 2bf0fdf
Merge branch 'upstream/gh-pages' into moumouls/defined-docs
Moumouls c1e1f8d
Update _includes/defined-schema/getting-started.md
mtrezza File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# 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 look to ALCs. Use CLPs combined with ACLs to deeply secure your Parse Server. | ||
|
||
## CLP Parameter Options | ||
|
||
Available options for CLP parameters: | ||
|
||
- `role:<roleName>`: If you are making use of Parse Roles you can set the permission based on a role." | ||
- `requiresAuthentication`: If set to `true` only authenticated users will have the permission. | ||
- `*`: Everybody has the permission. | ||
- `{}`: If you set the CLP key to `{}`, for example: `create: {}`. Then only calls with Parse Server Master Key will have the permission. | ||
|
||
## 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 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. | ||
|
||
`protectedFields` could be also combined as in the 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 who is a 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`. | ||
If a Parse User is member of `VerifiedUser` and `Admin`, he will have access to `phone` and `score`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# Core Classes/Fields | ||
|
||
Parse will never delete these fields on **ALL** classes if not provided in a class schema | ||
|
||
- `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` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Fields | ||
|
||
These field types are available on a Parse Schema. | ||
|
||
`required`: `boolean`, 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" }, | ||
}, | ||
}; | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# 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 }, | ||
// The special prefix _p_ is used to create indexes on pointer fields | ||
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 }, | ||
// An empty object means that only master key 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", | ||
// Define schemas of Parse Server | ||
schema: { | ||
definitions: [User, City, Country], | ||
// If set to `true`, the Parse Server API for schema changes is disabled and schema | ||
// changes are only possible by redeployingParse Server with a new schema definition | ||
lockSchemas: true, | ||
// If set to `true`, Parse Server will automatically delete non-defined classes from | ||
// the database; internal classes like `User` or `Role` are never deleted. | ||
strict: true, | ||
// 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 | ||
recreateModifiedFields: false, | ||
// If set to `true`, Parse Server will automatically delete non-defined class fields; | ||
// internal fields in classes like User or Role are never deleted. | ||
deleteExtraFields: false, | ||
}, | ||
serverStartComplete: () => { | ||
// Parse Server is ready with up-to-date schema | ||
parseServer.expressApp.get("/ready", (req: any, res: any) => { | ||
res.send("true"); | ||
}); | ||
}, | ||
}); | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Indexes | ||
|
||
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 }`. | ||
|
||
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 }, | ||
}, | ||
}; | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# 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. | ||
|
||
By default `strict` is `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. | ||
|
||
By default `deleteExtraFields` is `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 updating the field type when the field type is modified. For example when changing from `String` to `Number`. Data stored under the modified field will be lost. | ||
|
||
`recreateModifiedFields` defaults to `false`. **Be aware that if you set `recreateModifiedFields` to `true` and do not perform some data migration, you can result with data type inconsistency on the modified field.** | ||
|
||
Good practice would be to create the new field of the new type, and then create a Parse Cloud Job to migrate old field data to the newly created field. | ||
|
||
## lockSchemas | ||
|
||
You can set the `lockSchemas` option to `true` if you want to prevent any schema manipulation and to lock the schema as defined in the Parse Server configuration. If this options is `true` any create, update and delete request will be denied by the Parse Server API, even with the master key. You will not be able to manipulate `indexes`, `classLevelPermissions`, `fields`. | ||
|
||
## beforeMigration | ||
|
||
A function called before Parse Server performs schema updates based on the `definitions` option | ||
|
||
## afterMigration | ||
|
||
A function called after Parse Server performed schema updates based on the `definitions` option |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.