GraphQL support via cli (#5697)
* Including GraphQL options in CLI - now it was auto-generated * Improving the way that the headers are passed to the playground * Including README notes about GraphQL * Improving final text
This commit is contained in:
committed by
GitHub
parent
7ffb3b65e0
commit
5bc79cc3db
142
README.md
142
README.md
@@ -356,6 +356,148 @@ Live queries are meant to be used in real-time reactive applications, where just
|
||||
|
||||
Take a look at [Live Query Guide](https://docs.parseplatform.org/parse-server/guide/#live-queries), [Live Query Server Setup Guide](https://docs.parseplatform.org/parse-server/guide/#scalability) and [Live Query Protocol Specification](https://github.com/parse-community/parse-server/wiki/Parse-LiveQuery-Protocol-Specification). You can setup a standalone server or multiple instances for scalability (recommended).
|
||||
|
||||
# GraphQL
|
||||
|
||||
[GraphQL](https://graphql.org/), developed by Facebook, is an open-source data query and manipulation language for APIs. In addition to the traditional REST API, Parse Server automatically generates a GraphQL API based on your current application schema.
|
||||
|
||||
## Running
|
||||
|
||||
```
|
||||
$ npm install -g parse-server mongodb-runner
|
||||
$ mongodb-runner start
|
||||
$ parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://localhost/test --mountGraphQL --mountPlayground
|
||||
```
|
||||
|
||||
After starting the server, you can visit http://localhost:1337/playground in your browser to start playing with your GraphQL API.
|
||||
|
||||
***Note:*** Do ***NOT*** use --mountPlayground option in production.
|
||||
|
||||
## Checking the API health
|
||||
|
||||
Run the following:
|
||||
|
||||
```graphql
|
||||
query Health {
|
||||
health
|
||||
}
|
||||
```
|
||||
|
||||
You should receive the following response:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"health": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating your first object
|
||||
|
||||
Since your application does not have a schema yet, you can use the generic `create` mutation to create your first object. Run the following:
|
||||
|
||||
```graphql
|
||||
mutation CreateObject {
|
||||
objects {
|
||||
create(className: "GameScore" fields: { score: 1337 playerName: "Sean Plott" cheatMode: false }) {
|
||||
objectId
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should receive a response similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"objects": {
|
||||
"create": {
|
||||
"objectId": "7jfBmbGgyF",
|
||||
"createdAt": "2019-06-20T23:50:50.825Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using automatically generated operations
|
||||
|
||||
Parse Server learned from the first object that you created and now you have the `GameScore` class in your schema. You can now start using the automatically generated operations!
|
||||
|
||||
Run the following to create a second object:
|
||||
|
||||
```graphql
|
||||
mutation CreateGameScore {
|
||||
objects {
|
||||
createGameScore(fields: { score: 2558 playerName: "Luke Skywalker" cheatMode: false }) {
|
||||
objectId
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should receive a response similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"objects": {
|
||||
"createGameScore": {
|
||||
"objectId": "gySYolb2CL",
|
||||
"createdAt": "2019-06-20T23:56:37.114Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also run a query to this new class:
|
||||
|
||||
```graphql
|
||||
query FindGameScore {
|
||||
objects {
|
||||
findGameScore {
|
||||
results {
|
||||
playerName
|
||||
score
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should receive a response similar to this:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"objects": {
|
||||
"findGameScore": {
|
||||
"results": [
|
||||
{
|
||||
"playerName": "Sean Plott",
|
||||
"score": 1337
|
||||
},
|
||||
{
|
||||
"playerName": "Luke Skywalker",
|
||||
"score": 2558
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Learning more
|
||||
|
||||
Please look at the right side of your GraphQL Playground. You will see the `DOCS` and `SCHEMA` menus. They are automatically generated by analysing your application schema. Please refer to them and learn more about everything that you can do with your Parse GraphQL API.
|
||||
|
||||
Additionally, the [GraphQL Learn Section](https://graphql.org/learn/) is a very good source to start learning about the power of the GraphQL language.
|
||||
|
||||
# Upgrading to 3.0.0
|
||||
|
||||
Starting 3.0.0, parse-server uses the JS SDK version 2.0.
|
||||
|
||||
@@ -3,6 +3,8 @@ const commander = require('../lib/cli/utils/commander').default;
|
||||
const definitions = require('../lib/cli/definitions/parse-server').default;
|
||||
const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server')
|
||||
.default;
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
const testDefinitions = {
|
||||
arg0: 'PROGRAM_ARG_0',
|
||||
@@ -231,3 +233,84 @@ describe('LiveQuery definitions', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
const binPath = path.resolve(__dirname, '../bin/parse-server');
|
||||
let childProcess;
|
||||
|
||||
afterEach(async () => {
|
||||
if (childProcess) {
|
||||
childProcess.kill();
|
||||
}
|
||||
});
|
||||
|
||||
it('shoud start Parse Server', done => {
|
||||
childProcess = spawn(binPath, [
|
||||
'--appId',
|
||||
'test',
|
||||
'--masterKey',
|
||||
'test',
|
||||
'--databaseURI',
|
||||
'mongodb://localhost/test',
|
||||
]);
|
||||
childProcess.stdout.on('data', data => {
|
||||
data = data.toString();
|
||||
if (data.includes('parse-server running on')) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
childProcess.stderr.on('data', data => {
|
||||
done.fail(data.toString());
|
||||
});
|
||||
});
|
||||
|
||||
it('shoud start Parse Server with GraphQL', done => {
|
||||
childProcess = spawn(binPath, [
|
||||
'--appId',
|
||||
'test',
|
||||
'--masterKey',
|
||||
'test',
|
||||
'--databaseURI',
|
||||
'mongodb://localhost/test',
|
||||
'--mountGraphQL',
|
||||
]);
|
||||
let output = '';
|
||||
childProcess.stdout.on('data', data => {
|
||||
data = data.toString();
|
||||
output += data;
|
||||
if (data.includes('GraphQL running on')) {
|
||||
expect(output).toMatch('parse-server running on');
|
||||
done();
|
||||
}
|
||||
});
|
||||
childProcess.stderr.on('data', data => {
|
||||
done.fail(data.toString());
|
||||
});
|
||||
});
|
||||
|
||||
it('shoud start Parse Server with GraphQL and Playground', done => {
|
||||
childProcess = spawn(binPath, [
|
||||
'--appId',
|
||||
'test',
|
||||
'--masterKey',
|
||||
'test',
|
||||
'--databaseURI',
|
||||
'mongodb://localhost/test',
|
||||
'--mountGraphQL',
|
||||
'--mountPlayground',
|
||||
]);
|
||||
let output = '';
|
||||
childProcess.stdout.on('data', data => {
|
||||
data = data.toString();
|
||||
output += data;
|
||||
if (data.includes('Playground running on')) {
|
||||
expect(output).toMatch('GraphQL running on');
|
||||
expect(output).toMatch('parse-server running on');
|
||||
done();
|
||||
}
|
||||
});
|
||||
childProcess.stderr.on('data', data => {
|
||||
done.fail(data.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,6 +76,10 @@ class ParseGraphQLServer {
|
||||
renderPlaygroundPage({
|
||||
endpoint: this.config.graphQLPath,
|
||||
subscriptionEndpoint: this.config.subscriptionsPath,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': this.parseServer.config.appId,
|
||||
'X-Parse-Master-Key': this.parseServer.config.masterKey,
|
||||
},
|
||||
})
|
||||
);
|
||||
res.end();
|
||||
|
||||
@@ -153,6 +153,11 @@ module.exports.ParseServerOptions = {
|
||||
help: 'Adapter module for the files sub-system',
|
||||
action: parsers.moduleOrObjectParser,
|
||||
},
|
||||
graphQLPath: {
|
||||
env: 'PARSE_SERVER_GRAPHQL_PATH',
|
||||
help: 'Mount path for the GraphQL endpoint, defaults to /graphql',
|
||||
default: '/graphql',
|
||||
},
|
||||
host: {
|
||||
env: 'PARSE_SERVER_HOST',
|
||||
help: 'The host to serve ParseServer on, defaults to 0.0.0.0',
|
||||
@@ -219,11 +224,23 @@ module.exports.ParseServerOptions = {
|
||||
env: 'PARSE_SERVER_MIDDLEWARE',
|
||||
help: 'middleware for express server, can be string or function',
|
||||
},
|
||||
mountGraphQL: {
|
||||
env: 'PARSE_SERVER_MOUNT_GRAPHQL',
|
||||
help: 'Mounts the GraphQL endpoint',
|
||||
action: parsers.booleanParser,
|
||||
default: false,
|
||||
},
|
||||
mountPath: {
|
||||
env: 'PARSE_SERVER_MOUNT_PATH',
|
||||
help: 'Mount path for the server, defaults to /parse',
|
||||
default: '/parse',
|
||||
},
|
||||
mountPlayground: {
|
||||
env: 'PARSE_SERVER_MOUNT_PLAYGROUND',
|
||||
help: 'Mounts the GraphQL Playground - never use this option in production',
|
||||
action: parsers.booleanParser,
|
||||
default: false,
|
||||
},
|
||||
objectIdSize: {
|
||||
env: 'PARSE_SERVER_OBJECT_ID_SIZE',
|
||||
help: "Sets the number of characters in generated object id's, default 10",
|
||||
@@ -235,6 +252,11 @@ module.exports.ParseServerOptions = {
|
||||
help: 'Password policy for enforcing password related rules',
|
||||
action: parsers.objectParser,
|
||||
},
|
||||
playgroundPath: {
|
||||
env: 'PARSE_SERVER_PLAYGROUND_PATH',
|
||||
help: 'Mount path for the GraphQL Playground, defaults to /playground',
|
||||
default: '/playground',
|
||||
},
|
||||
port: {
|
||||
env: 'PORT',
|
||||
help: 'The port to run the ParseServer, defaults to 1337.',
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
* @property {Boolean} expireInactiveSessions Sets wether we should expire the inactive sessions, defaults to true
|
||||
* @property {String} fileKey Key for your files
|
||||
* @property {Adapter<FilesAdapter>} filesAdapter Adapter module for the files sub-system
|
||||
* @property {String} graphQLPath Mount path for the GraphQL endpoint, defaults to /graphql
|
||||
* @property {String} host The host to serve ParseServer on, defaults to 0.0.0.0
|
||||
* @property {String} javascriptKey Key for the Javascript SDK
|
||||
* @property {Boolean} jsonLogs Log as structured JSON objects
|
||||
@@ -40,9 +41,12 @@
|
||||
* @property {Number} maxLimit Max value for limit option on queries, defaults to unlimited
|
||||
* @property {String} maxUploadSize Max file size for uploads, defaults to 20mb
|
||||
* @property {Union} middleware middleware for express server, can be string or function
|
||||
* @property {Boolean} mountGraphQL Mounts the GraphQL endpoint
|
||||
* @property {String} mountPath Mount path for the server, defaults to /parse
|
||||
* @property {Boolean} mountPlayground Mounts the GraphQL Playground - never use this option in production
|
||||
* @property {Number} objectIdSize Sets the number of characters in generated object id's, default 10
|
||||
* @property {Any} passwordPolicy Password policy for enforcing password related rules
|
||||
* @property {String} playgroundPath Mount path for the GraphQL Playground, defaults to /playground
|
||||
* @property {Number} port The port to run the ParseServer, defaults to 1337.
|
||||
* @property {Boolean} preserveFileName Enable (or disable) the addition of a unique hash to the file names
|
||||
* @property {Boolean} preventLoginWithUnverifiedEmail Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false
|
||||
|
||||
@@ -180,6 +180,22 @@ export interface ParseServerOptions {
|
||||
startLiveQueryServer: ?boolean;
|
||||
/* Live query server configuration options (will start the liveQuery server) */
|
||||
liveQueryServerOptions: ?LiveQueryServerOptions;
|
||||
/* Mounts the GraphQL endpoint
|
||||
:ENV: PARSE_SERVER_MOUNT_GRAPHQL
|
||||
:DEFAULT: false */
|
||||
mountGraphQL: ?boolean;
|
||||
/* Mount path for the GraphQL endpoint, defaults to /graphql
|
||||
:ENV: PARSE_SERVER_GRAPHQL_PATH
|
||||
:DEFAULT: /graphql */
|
||||
graphQLPath: ?string;
|
||||
/* Mounts the GraphQL Playground - never use this option in production
|
||||
:ENV: PARSE_SERVER_MOUNT_PLAYGROUND
|
||||
:DEFAULT: false */
|
||||
mountPlayground: ?boolean;
|
||||
/* Mount path for the GraphQL Playground, defaults to /playground
|
||||
:ENV: PARSE_SERVER_PLAYGROUND_PATH
|
||||
:DEFAULT: /playground */
|
||||
playgroundPath: ?string;
|
||||
|
||||
serverStartComplete: ?(error: ?Error) => void;
|
||||
}
|
||||
|
||||
@@ -34,9 +34,10 @@ import { UsersRouter } from './Routers/UsersRouter';
|
||||
import { PurgeRouter } from './Routers/PurgeRouter';
|
||||
import { AudiencesRouter } from './Routers/AudiencesRouter';
|
||||
import { AggregateRouter } from './Routers/AggregateRouter';
|
||||
|
||||
import { ParseServerRESTController } from './ParseServerRESTController';
|
||||
import * as controllers from './Controllers';
|
||||
import { ParseGraphQLServer } from './GraphQL/ParseGraphQLServer';
|
||||
|
||||
// Mutate the Parse object to add the Cloud Code handlers
|
||||
addParseCloud();
|
||||
|
||||
@@ -264,6 +265,22 @@ class ParseServer {
|
||||
}
|
||||
|
||||
app.use(options.mountPath, this.app);
|
||||
|
||||
if (options.mountGraphQL === true || options.mountPlayground === true) {
|
||||
const parseGraphQLServer = new ParseGraphQLServer(this, {
|
||||
graphQLPath: options.graphQLPath,
|
||||
playgroundPath: options.playgroundPath,
|
||||
});
|
||||
|
||||
if (options.mountGraphQL) {
|
||||
parseGraphQLServer.applyGraphQL(app);
|
||||
}
|
||||
|
||||
if (options.mountPlayground) {
|
||||
parseGraphQLServer.applyPlayground(app);
|
||||
}
|
||||
}
|
||||
|
||||
const server = app.listen(options.port, options.host, callback);
|
||||
this.server = server;
|
||||
|
||||
|
||||
@@ -84,20 +84,40 @@ runner({
|
||||
});
|
||||
} else {
|
||||
ParseServer.start(options, () => {
|
||||
console.log(
|
||||
'[' + process.pid + '] parse-server running on ' + options.serverURL
|
||||
);
|
||||
printSuccessMessage();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ParseServer.start(options, () => {
|
||||
logOptions();
|
||||
console.log('');
|
||||
console.log(
|
||||
'[' + process.pid + '] parse-server running on ' + options.serverURL
|
||||
);
|
||||
printSuccessMessage();
|
||||
});
|
||||
}
|
||||
|
||||
function printSuccessMessage() {
|
||||
console.log(
|
||||
'[' + process.pid + '] parse-server running on ' + options.serverURL
|
||||
);
|
||||
if (options.mountGraphQL) {
|
||||
console.log(
|
||||
'[' +
|
||||
process.pid +
|
||||
'] GraphQL running on http://localhost:' +
|
||||
options.port +
|
||||
options.graphQLPath
|
||||
);
|
||||
}
|
||||
if (options.mountPlayground) {
|
||||
console.log(
|
||||
'[' +
|
||||
process.pid +
|
||||
'] Playground running on http://localhost:' +
|
||||
options.port +
|
||||
options.playgroundPath
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user