Skip to content

Commit 99fcf45

Browse files
authoredDec 21, 2022
feat: Asynchronous initialization of Parse Server (#8232)
BREAKING CHANGE: This release introduces the asynchronous initialization of Parse Server to prevent mounting Parse Server before being ready to receive request; it changes how Parse Server is imported, initialized and started; it also removes the callback `serverStartComplete`; see the [Parse Server 6 migration guide](https://github.com/parse-community/parse-server/blob/alpha/6.0.0.md) for more details (#8232)
1 parent db9941c commit 99fcf45

21 files changed

+493
-309
lines changed
 

‎.babelrc

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
"presets": [
77
["@babel/preset-env", {
88
"targets": {
9-
"node": "14"
10-
}
9+
"node": "14",
10+
},
11+
"exclude": ["proposal-dynamic-import"]
1112
}]
1213
],
1314
"sourceMaps": "inline"

‎6.0.0.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Parse Server 6 Migration Guide <!-- omit in toc -->
2+
3+
This document only highlights specific changes that require a longer explanation. For a full list of changes in Parse Server 6 please refer to the [changelog](https://github.com/parse-community/parse-server/blob/alpha/CHANGELOG.md).
4+
5+
---
6+
7+
- [Import Statement](#import-statement)
8+
- [Asynchronous Initialization](#asynchronous-initialization)
9+
10+
---
11+
12+
## Import Statement
13+
14+
The import and initialization syntax has been simplified with more intuitive naming and structure.
15+
16+
*Parse Server 5:*
17+
```js
18+
// Returns a Parse Server instance
19+
const ParseServer = require('parse-server');
20+
21+
// Returns a Parse Server express middleware
22+
const { ParseServer } = require('parse-server');
23+
```
24+
25+
*Parse Server 6:*
26+
```js
27+
// Both return a Parse Server instance
28+
const ParseServer = require('parse-server');
29+
const { ParseServer } = require('parse-server');
30+
```
31+
32+
To get the express middleware in Parse Server 6, configure the Parse Server instance, start Parse Server and use its `app` property. See [Asynchronous Initialization](#asynchronous-initialization) for more details.
33+
34+
## Asynchronous Initialization
35+
36+
Previously, it was possible to mount Parse Server before it was fully started up and ready to receive requests. This could result in undefined behavior, such as Parse Objects could be saved before Cloud Code was registered. To prevent this, Parse Server 6 requires to be started asynchronously before being mounted.
37+
38+
*Parse Server 5:*
39+
```js
40+
// 1. Import Parse Server
41+
const { ParseServer } = require('parse-server');
42+
43+
// 2. Create a Parse Server instance as express middleware
44+
const server = new ParseServer(config);
45+
46+
// 3. Mount express middleware
47+
app.use("/parse", server);
48+
```
49+
50+
*Parse Server 6:*
51+
```js
52+
// 1. Import Parse Server
53+
const ParseServer = require('parse-server');
54+
55+
// 2. Create a Parse Server instance
56+
const server = new ParseServer(config);
57+
58+
// 3. Start up Parse Server asynchronously
59+
await server.start();
60+
61+
// 4. Mount express middleware
62+
app.use("/parse", server.app);
63+
```

‎README.md

+42-15
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ A big *thank you* 🙏 to our [sponsors](#sponsors) and [backers](#backers) who
4040

4141
---
4242

43-
- [Flavors & Branches](#flavors--branches)
43+
- [Flavors \& Branches](#flavors--branches)
4444
- [Long Term Support](#long-term-support)
4545
- [Getting Started](#getting-started)
4646
- [Running Parse Server](#running-parse-server)
@@ -55,6 +55,8 @@ A big *thank you* 🙏 to our [sponsors](#sponsors) and [backers](#backers) who
5555
- [Running Parse Server elsewhere](#running-parse-server-elsewhere)
5656
- [Sample Application](#sample-application)
5757
- [Parse Server + Express](#parse-server--express)
58+
- [Parse Server Health](#parse-server-health)
59+
- [Status Values](#status-values)
5860
- [Configuration](#configuration)
5961
- [Basic Options](#basic-options)
6062
- [Client Key Options](#client-key-options)
@@ -136,13 +138,13 @@ Parse Server is continuously tested with the most recent releases of Node.js to
136138

137139
Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and [MongoDB lifecycle schedule](https://www.mongodb.com/support-policy/lifecycles) and only test against versions that are officially supported and have not reached their end-of-life date. We consider the end-of-life date of a MongoDB "rapid release" to be the same as its major version release.
138140

139-
| Version | Latest Version | End-of-Life | Compatible |
140-
|-------------|----------------|---------------|--------------|
141-
| MongoDB 4.0 | 4.0.28 | April 2022 | ✅ Yes |
142-
| MongoDB 4.2 | 4.2.19 | April 2023 | ✅ Yes |
143-
| MongoDB 4.4 | 4.4.13 | February 2024 | ✅ Yes |
144-
| MongoDB 5 | 5.3.2 | October 2024 | ✅ Yes |
145-
| MongoDB 6 | 6.0.2 | July 2025 | ✅ Yes |
141+
| Version | Latest Version | End-of-Life | Compatible |
142+
|-------------|----------------|---------------|------------|
143+
| MongoDB 4.0 | 4.0.28 | April 2022 | ✅ Yes |
144+
| MongoDB 4.2 | 4.2.19 | April 2023 | ✅ Yes |
145+
| MongoDB 4.4 | 4.4.13 | February 2024 | ✅ Yes |
146+
| MongoDB 5 | 5.3.2 | October 2024 | ✅ Yes |
147+
| MongoDB 6 | 6.0.2 | July 2025 | ✅ Yes |
146148

147149
#### PostgreSQL
148150

@@ -282,11 +284,11 @@ We have provided a basic [Node.js application](https://github.com/parse-communit
282284
You can also create an instance of Parse Server, and mount it on a new or existing Express website:
283285

284286
```js
285-
var express = require('express');
286-
var ParseServer = require('parse-server').ParseServer;
287-
var app = express();
287+
const express = require('express');
288+
const ParseServer = require('parse-server').ParseServer;
289+
const app = express();
288290

289-
var api = new ParseServer({
291+
const server = new ParseServer({
290292
databaseURI: 'mongodb://localhost:27017/dev', // Connection string for your MongoDB database
291293
cloud: './cloud/main.js', // Path to your Cloud Code
292294
appId: 'myAppId',
@@ -295,8 +297,11 @@ var api = new ParseServer({
295297
serverURL: 'http://localhost:1337/parse' // Don't forget to change to https if needed
296298
});
297299

300+
// Start server
301+
await server.start();
302+
298303
// Serve the Parse API on the /parse URL prefix
299-
app.use('/parse', api);
304+
app.use('/parse', server.app);
300305

301306
app.listen(1337, function() {
302307
console.log('parse-server-example running on port 1337.');
@@ -305,6 +310,27 @@ app.listen(1337, function() {
305310

306311
For a full list of available options, run `parse-server --help` or take a look at [Parse Server Configurations](http://parseplatform.org/parse-server/api/master/ParseServerOptions.html).
307312

313+
## Parse Server Health
314+
315+
Check the Parse Server health by sending a request to the `/parse/health` endpoint.
316+
317+
The response looks like this:
318+
319+
```json
320+
{
321+
"status": "ok"
322+
}
323+
```
324+
325+
### Status Values
326+
327+
| Value | Description |
328+
|---------------|-----------------------------------------------------------------------------|
329+
| `initialized` | The server has been created but the `start` method has not been called yet. |
330+
| `starting` | The server is starting up. |
331+
| `ok` | The server started and is running. |
332+
| `error` | There was a startup error, see the logs for details. |
333+
308334
# Configuration
309335

310336
Parse Server can be configured using the following options. You may pass these as parameters when running a standalone `parse-server`, or by loading a configuration file in JSON format using `parse-server path/to/configuration.json`. If you're using Parse Server on Express, you may also pass these to the `ParseServer` object as options.
@@ -461,7 +487,7 @@ The following paths are already used by Parse Server's built-in features and are
461487
It’s possible to change the default pages of the app and redirect the user to another path or domain.
462488

463489
```js
464-
var server = ParseServer({
490+
const server = ParseServer({
465491
...otherOptions,
466492
467493
customPages: {
@@ -851,7 +877,7 @@ Then, create an `index.js` file with the following content:
851877
852878
```js
853879
const express = require('express');
854-
const { default: ParseServer, ParseGraphQLServer } = require('parse-server');
880+
const { ParseServer, ParseGraphQLServer } = require('parse-server');
855881

856882
const app = express();
857883

@@ -875,6 +901,7 @@ app.use('/parse', parseServer.app); // (Optional) Mounts the REST API
875901
parseGraphQLServer.applyGraphQL(app); // Mounts the GraphQL API
876902
parseGraphQLServer.applyPlayground(app); // (Optional) Mounts the GraphQL Playground - do NOT use in Production
877903

904+
await parseServer.start();
878905
app.listen(1337, function() {
879906
console.log('REST API running on http://localhost:1337/parse');
880907
console.log('GraphQL API running on http://localhost:1337/graphql');

‎spec/.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"jequal": true,
3535
"create": true,
3636
"arrayContains": true,
37-
"databaseAdapter": true
37+
"databaseAdapter": true,
38+
"databaseURI": true
3839
},
3940
"rules": {
4041
"no-console": [0],

‎spec/CLI.spec.js

+45-36
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,14 @@ describe('execution', () => {
219219
}
220220
});
221221

222-
it('shoud start Parse Server', done => {
223-
childProcess = spawn(binPath, [
224-
'--appId',
225-
'test',
226-
'--masterKey',
227-
'test',
228-
'--databaseURI',
229-
'mongodb://localhost/test',
230-
'--port',
231-
'1339',
232-
]);
222+
it('should start Parse Server', done => {
223+
const env = { ...process.env };
224+
env.NODE_OPTIONS = '--dns-result-order=ipv4first';
225+
childProcess = spawn(
226+
binPath,
227+
['--appId', 'test', '--masterKey', 'test', '--databaseURI', databaseURI, '--port', '1339'],
228+
{ env }
229+
);
233230
childProcess.stdout.on('data', data => {
234231
data = data.toString();
235232
if (data.includes('parse-server running on')) {
@@ -241,18 +238,24 @@ describe('execution', () => {
241238
});
242239
});
243240

244-
it('shoud start Parse Server with GraphQL', done => {
245-
childProcess = spawn(binPath, [
246-
'--appId',
247-
'test',
248-
'--masterKey',
249-
'test',
250-
'--databaseURI',
251-
'mongodb://localhost/test',
252-
'--port',
253-
'1340',
254-
'--mountGraphQL',
255-
]);
241+
it('should start Parse Server with GraphQL', async done => {
242+
const env = { ...process.env };
243+
env.NODE_OPTIONS = '--dns-result-order=ipv4first';
244+
childProcess = spawn(
245+
binPath,
246+
[
247+
'--appId',
248+
'test',
249+
'--masterKey',
250+
'test',
251+
'--databaseURI',
252+
databaseURI,
253+
'--port',
254+
'1340',
255+
'--mountGraphQL',
256+
],
257+
{ env }
258+
);
256259
let output = '';
257260
childProcess.stdout.on('data', data => {
258261
data = data.toString();
@@ -267,19 +270,25 @@ describe('execution', () => {
267270
});
268271
});
269272

270-
it('shoud start Parse Server with GraphQL and Playground', done => {
271-
childProcess = spawn(binPath, [
272-
'--appId',
273-
'test',
274-
'--masterKey',
275-
'test',
276-
'--databaseURI',
277-
'mongodb://localhost/test',
278-
'--port',
279-
'1341',
280-
'--mountGraphQL',
281-
'--mountPlayground',
282-
]);
273+
it('should start Parse Server with GraphQL and Playground', async done => {
274+
const env = { ...process.env };
275+
env.NODE_OPTIONS = '--dns-result-order=ipv4first';
276+
childProcess = spawn(
277+
binPath,
278+
[
279+
'--appId',
280+
'test',
281+
'--masterKey',
282+
'test',
283+
'--databaseURI',
284+
databaseURI,
285+
'--port',
286+
'1341',
287+
'--mountGraphQL',
288+
'--mountPlayground',
289+
],
290+
{ env }
291+
);
283292
let output = '';
284293
childProcess.stdout.on('data', data => {
285294
data = data.toString();

‎spec/CloudCode.spec.js

+42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22
const Config = require('../lib/Config');
33
const Parse = require('parse/node');
4+
const ParseServer = require('../lib/index').ParseServer;
45
const request = require('../lib/request');
56
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter')
67
.InMemoryCacheAdapter;
@@ -39,6 +40,47 @@ describe('Cloud Code', () => {
3940
});
4041
});
4142

43+
it('can load cloud code as a module', async () => {
44+
process.env.npm_package_type = 'module';
45+
await reconfigureServer({ appId: 'test1', cloud: './spec/cloud/cloudCodeModuleFile.js' });
46+
const result = await Parse.Cloud.run('cloudCodeInFile');
47+
expect(result).toEqual('It is possible to define cloud code in a file.');
48+
delete process.env.npm_package_type;
49+
});
50+
51+
it('cloud code must be valid type', async () => {
52+
await expectAsync(reconfigureServer({ cloud: true })).toBeRejectedWith(
53+
"argument 'cloud' must either be a string or a function"
54+
);
55+
});
56+
57+
it('should wait for cloud code to load', async () => {
58+
await reconfigureServer({ appId: 'test3' });
59+
const initiated = new Date();
60+
const parseServer = await new ParseServer({
61+
...defaultConfiguration,
62+
appId: 'test3',
63+
masterKey: 'test',
64+
serverURL: 'http://localhost:12668/parse',
65+
async cloud() {
66+
await new Promise(resolve => setTimeout(resolve, 1000));
67+
Parse.Cloud.beforeSave('Test', () => {
68+
throw 'Cannot save.';
69+
});
70+
},
71+
}).start();
72+
const express = require('express');
73+
const app = express();
74+
app.use('/parse', parseServer.app);
75+
const server = app.listen(12668);
76+
const now = new Date();
77+
expect(now.getTime() - initiated.getTime() > 1000).toBeTrue();
78+
await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith(
79+
new Parse.Error(141, 'Cannot save.')
80+
);
81+
await new Promise(resolve => server.close(resolve));
82+
});
83+
4284
it('can create functions', done => {
4385
Parse.Cloud.define('hello', () => {
4486
return 'Hello world!';

‎spec/DefinedSchemas.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ describe('DefinedSchemas', () => {
631631
const logger = require('../lib/logger').logger;
632632
spyOn(DefinedSchemas.prototype, 'wait').and.resolveTo();
633633
spyOn(logger, 'error').and.callThrough();
634-
spyOn(Parse.Schema, 'all').and.callFake(() => {
634+
spyOn(DefinedSchemas.prototype, 'createDeleteSession').and.callFake(() => {
635635
throw error;
636636
});
637637

0 commit comments

Comments
 (0)