diff --git a/samples/relay-modern-starter-kit/.babelrc b/samples/relay-modern-starter-kit/.babelrc
new file mode 100644
index 000000000..dafb83b59
--- /dev/null
+++ b/samples/relay-modern-starter-kit/.babelrc
@@ -0,0 +1,19 @@
+{
+ "passPerPreset": true,
+ "presets": [
+ {
+ "plugins": [
+ ["relay", {
+ "schema": "data/schema.json"}
+ ],
+ "syntax-async-functions",
+ "transform-class-properties",
+ "transform-object-rest-spread",
+ "react-require"
+ ]
+ },
+ "react",
+ "es2015",
+ "stage-0"
+ ]
+}
diff --git a/samples/relay-modern-starter-kit/.gitignore b/samples/relay-modern-starter-kit/.gitignore
new file mode 100644
index 000000000..cccc85cb4
--- /dev/null
+++ b/samples/relay-modern-starter-kit/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+node_modules
+npm-debug.log
+data/schema.graphql
+.idea/
+__generated__/
\ No newline at end of file
diff --git a/samples/relay-modern-starter-kit/LICENSE b/samples/relay-modern-starter-kit/LICENSE
new file mode 100644
index 000000000..779606a8b
--- /dev/null
+++ b/samples/relay-modern-starter-kit/LICENSE
@@ -0,0 +1,31 @@
+BSD License
+
+For Relay Starter Kit software
+
+Copyright (c) 2013-2015, Facebook, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/samples/relay-modern-starter-kit/PATENTS b/samples/relay-modern-starter-kit/PATENTS
new file mode 100644
index 000000000..be39fb39a
--- /dev/null
+++ b/samples/relay-modern-starter-kit/PATENTS
@@ -0,0 +1,33 @@
+Additional Grant of Patent Rights Version 2
+
+"Software" means the Relay Starter Kit software distributed by Facebook, Inc.
+
+Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
+("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
+(subject to the termination provision below) license under any Necessary
+Claims, to make, have made, use, sell, offer to sell, import, and otherwise
+transfer the Software. For avoidance of doubt, no license is granted under
+Facebook's rights in any patent claims that are infringed by (i) modifications
+to the Software made by you or any third party or (ii) the Software in
+combination with any software or other technology.
+
+The license granted hereunder will terminate, automatically and without notice,
+if you (or any of your subsidiaries, corporate affiliates or agents) initiate
+directly or indirectly, or take a direct financial interest in, any Patent
+Assertion: (i) against Facebook or any of its subsidiaries or corporate
+affiliates, (ii) against any party if such Patent Assertion arises in whole or
+in part from any software, technology, product or service of Facebook or any of
+its subsidiaries or corporate affiliates, or (iii) against any party relating
+to the Software. Notwithstanding the foregoing, if Facebook or any of its
+subsidiaries or corporate affiliates files a lawsuit alleging patent
+infringement against you in the first instance, and you respond by filing a
+patent infringement counterclaim in that lawsuit against that party that is
+unrelated to the Software, the license granted hereunder will not terminate
+under section (i) of this paragraph due to such counterclaim.
+
+A "Necessary Claim" is a claim of a patent owned by Facebook that is
+necessarily infringed by the Software standing alone.
+
+A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
+or contributory infringement or inducement to infringe any patent, including a
+cross-claim or counterclaim.
diff --git a/samples/relay-modern-starter-kit/README.md b/samples/relay-modern-starter-kit/README.md
new file mode 100644
index 000000000..37dc1a987
--- /dev/null
+++ b/samples/relay-modern-starter-kit/README.md
@@ -0,0 +1,81 @@
+# Relay Modern Starter Kit with Pagination
+
+This kit includes an app server, a GraphQL server, and a transpiler that you can use to get started building an app with Relay. For a walkthrough, see the [Relay tutorial](https://facebook.github.io/relay/docs/tutorial.html).
+
+Few changes have been made to the original starter kit. It is now compatible with the [Relay Modern](https://facebook.github.io/relay/docs/relay-modern.html) API version. It also uses a [PaginationContainer](https://facebook.github.io/relay/docs/PaginationContainer.html) for item retrieving.
+
+## Installation
+
+First install required npm modules:
+
+```
+npm install
+```
+
+Generate JavaScript sources of Relay-wrapped components by executing:
+
+```
+npm run relay
+```
+
+## Running
+
+Start the GraphQL server by executing:
+
+```
+fsharpi server.fsx
+```
+
+Start the Relay application server:
+
+```
+npm start
+```
+
+Visit `localhost:8090` in your browser in order to access the application.
+
+## Developing
+
+### GraphQL Server
+
+Following information supposed to get you started with modification of the GraphQL server.
+
+#### Debugging
+
+The sample contains a `server.fs` copy of the server and an F# project file which can be used for debugging with mono:
+
+```
+xbuild && mono --debug --debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:55555 ./bin/Debug/mono/FSharp.Data.GraphQL.ModernRelay.exe
+```
+
+This will start the server awaiting for the mono debugger to be attached.
+
+#### Uri
+
+By default the server will start on `localhost:8083`. This can be modified by changing `address` and `port` in `server.fsx` or `server.fs` respectively. Changing this settings requires adjustment of `graphqlServerUri` in `./src/relayEnvironment.jsx`.
+
+### Relay Application
+
+#### Schema modification
+
+Each time the schema is modified in the `server.fsx` GraphQL server, a new schema file has to be generated on the Relay application side.
+
+Accessing the `localhost:8083` in your browser will yield an introspection reply containing the schema of the database. Save this response to `./data/schema.json` whenever the schema is getting changed.
+
+Relay API has to know the path to the schema file. `.babelrc` contains this information by setting the `relay` plugin with `schema` option. Adjust this if the path to schema should change.
+
+The generated `./data/schema.json` or `./data/schema.graphql` (see optional below) file is used for the ahead-of-time compilation by Relay Modern compiler. The compilation process should be triggered by executing:
+
+```
+npm run relay
+```
+
+The command checks relevant `.jsx` and `.js` files in `./src` folder and generates their compiled in `__generated__` for defined containers. Those are e.g. [FragmentContainer](https://facebook.github.io/relay/docs/fragment-container.html) or the root Relay tree [QueryRenderer](https://facebook.github.io/relay/docs/query-renderer.html).
+
+#### Optional
+
+There are two accepted schema formats: `.json` and `.graphql`. The conversion to the latter can be done by executing `npm run json-to-graphql`. Input and output files can be adjusted in `./scripts/jsonToGraphql.js`.
+
+## License
+
+Relay Starter Kit is [BSD licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS).
diff --git a/samples/relay-modern-starter-kit/data/schema.json b/samples/relay-modern-starter-kit/data/schema.json
new file mode 100644
index 000000000..87e2220f3
--- /dev/null
+++ b/samples/relay-modern-starter-kit/data/schema.json
@@ -0,0 +1,1326 @@
+{
+ "documentId": 577680162,
+ "data": {
+ "__schema": {
+ "queryType": {
+ "name": "Query"
+ },
+ "mutationType": null,
+ "subscriptionType": null,
+ "types": [
+ {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "description": "The `Boolean` scalar type represents `true` or `false`.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "Date",
+ "description": "The `Date` scalar type represents a Date value with Time component. The Date type appears in a JSON response as a String representation compatible with ISO-8601 format.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "Float",
+ "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "ID",
+ "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "Int",
+ "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INTERFACE",
+ "name": "Node",
+ "description": "An object that can be uniquely identified by its id",
+ "fields": [
+ {
+ "name": "id",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": [
+ {
+ "kind": "OBJECT",
+ "name": "Widget",
+ "ofType": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "User",
+ "ofType": null
+ }
+ ]
+ },
+ {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "description": "Information about pagination in a connection.",
+ "fields": [
+ {
+ "name": "endCursor",
+ "description": "When paginating forwards, the cursor to continue.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "hasNextPage",
+ "description": "When paginating forwards, are there more items?",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "hasPreviousPage",
+ "description": "When paginating backwards, are there more items?",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "startCursor",
+ "description": "When paginating backwards, the cursor to continue.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "Query",
+ "description": null,
+ "fields": [
+ {
+ "name": "node",
+ "description": "Fetches an object given its ID",
+ "args": [
+ {
+ "name": "id",
+ "description": "Identifier of an object",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "INTERFACE",
+ "name": "Node",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "viewer",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "User",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "String",
+ "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
+ "name": "URI",
+ "description": "The `URI` scalar type represents a string resource identifier compatible with URI standard. The URI type appears in a JSON response as a String.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "User",
+ "description": "A person who uses our app",
+ "fields": [
+ {
+ "name": "id",
+ "description": "The ID of an object",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "widgets",
+ "description": "A person's collection of widgets",
+ "args": [
+ {
+ "name": "first",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "after",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": null,
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "WidgetConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+ {
+ "kind": "INTERFACE",
+ "name": "Node",
+ "ofType": null
+ }
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "Widget",
+ "description": "A shiny widget",
+ "fields": [
+ {
+ "name": "id",
+ "description": "The ID of an object",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+ {
+ "kind": "INTERFACE",
+ "name": "Node",
+ "ofType": null
+ }
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "WidgetConnection",
+ "description": "A connection from an object to a list of objects of type Widget",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "Information to aid in pagination.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "WidgetEdge"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "totalCount",
+ "description": "A count of the total number of objects in this connection, ignoring pagination. This allows a client to fetch the first five objects by passing \\\"5\\\" as the argument to `first`, then fetch the total count so it could display \\\"5 of 83\\\", for example. In cases where we employ infinite scrolling or don't have an exact count of entries, this field will return `null`.",
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "WidgetEdge",
+ "description": "An edge in a connection from an object to another object of type Widget",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge. Must NOT be an enumerable collection.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Widget",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__Directive",
+ "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. In some cases, you need to provide options to alter GraphQL’s execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.",
+ "fields": [
+ {
+ "name": "args",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__InputValue"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "locations",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "__DirectiveLocation"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "onField",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "onFragment",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "onOperation",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "ENUM",
+ "name": "__DirectiveLocation",
+ "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "QUERY",
+ "description": "Location adjacent to a query operation.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "MUTATION",
+ "description": "Location adjacent to a mutation operation.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "SUBSCRIPTION",
+ "description": "Location adjacent to a subscription operation.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "FIELD",
+ "description": "Location adjacent to a field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "FRAGMENT_DEFINITION",
+ "description": "Location adjacent to a fragment definition.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "FRAGMENT_SPREAD",
+ "description": "Location adjacent to a fragment spread.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INLINE_FRAGMENT",
+ "description": "Location adjacent to an inline fragment.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__EnumValue",
+ "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
+ "fields": [
+ {
+ "name": "deprecationReason",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "isDeprecated",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__Field",
+ "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
+ "fields": [
+ {
+ "name": "args",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__InputValue"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "deprecationReason",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "isDeprecated",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "type",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__InputValue",
+ "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
+ "fields": [
+ {
+ "name": "defaultValue",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "type",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__Schema",
+ "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
+ "fields": [
+ {
+ "name": "directives",
+ "description": "A list of all directives supported by this server.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Directive"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "mutationType",
+ "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
+ "args": [],
+ "type": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "queryType",
+ "description": "The type that query operations will be rooted at.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "subscriptionType",
+ "description": "If this server support subscription, the type that subscription operations will be rooted at.",
+ "args": [],
+ "type": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "types",
+ "description": "A list of all types supported by this server.",
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type"
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum. Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.",
+ "fields": [
+ {
+ "name": "description",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "enumValues",
+ "description": null,
+ "args": [
+ {
+ "name": "includeDeprecated",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "defaultValue": "False"
+ }
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__EnumValue",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "fields",
+ "description": null,
+ "args": [
+ {
+ "name": "includeDeprecated",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "defaultValue": "False"
+ }
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Field",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "inputFields",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__InputValue",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "interfaces",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "kind",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "__TypeKind",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "ofType",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "possibleTypes",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "__Type",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "ENUM",
+ "name": "__TypeKind",
+ "description": "An enum describing what kind of type a given __Type is.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "SCALAR",
+ "description": "Indicates this type is a scalar.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "OBJECT",
+ "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INTERFACE",
+ "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "UNION",
+ "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "ENUM",
+ "description": "Indicates this type is an enum. `enumValues` is a valid field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INPUT_OBJECT",
+ "description": "Indicates this type is an input object. `inputFields` is a valid field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "LIST",
+ "description": "Indicates this type is a list. `ofType` is a valid field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "NON_NULL",
+ "description": "Indicates this type is a non-null. `ofType` is a valid field.",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ }
+ ],
+ "directives": [
+ {
+ "name": "include",
+ "description": "Directs the executor to include this field or fragment only when the `if` argument is true.",
+ "locations": [
+ "FIELD",
+ "FRAGMENT_SPREAD",
+ "INLINE_FRAGMENT"
+ ],
+ "args": [
+ {
+ "name": "if",
+ "description": "Included when true.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ]
+ },
+ {
+ "name": "skip",
+ "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
+ "locations": [
+ "FIELD",
+ "FRAGMENT_SPREAD",
+ "INLINE_FRAGMENT"
+ ],
+ "args": [
+ {
+ "name": "if",
+ "description": "Skipped when true.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/relay-modern-starter-kit/index.html b/samples/relay-modern-starter-kit/index.html
new file mode 100644
index 000000000..ebc7a43ff
--- /dev/null
+++ b/samples/relay-modern-starter-kit/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/relay-modern-starter-kit/package.json b/samples/relay-modern-starter-kit/package.json
new file mode 100644
index 000000000..1233cf531
--- /dev/null
+++ b/samples/relay-modern-starter-kit/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "modern-relay-starter-kit",
+ "private": true,
+ "description": "A quick way to get up and running with Relay",
+ "repository": "facebook/relay-starter-kit",
+ "version": "0.1.0",
+ "scripts": {
+ "start": "npm run serve | npm run dev",
+ "serve": "./node_modules/.bin/http-server -p 8080",
+ "dev": "webpack-dev-server --progress --colors --port 8090",
+ "relay": "relay-compiler --src ./src --schema data/schema.graphql --extensions jsx js",
+ "json-to-graphql": "babel-node ./scripts/jsonToGraphql.js"
+ },
+ "dependencies": {
+ "graphql": "^0.5.0",
+ "graphql-relay": "0.4.1",
+ "react": "^15.0.2",
+ "react-dom": "^15.0.2",
+ "react-relay": "dev"
+ },
+ "peerDependencies": {},
+ "devDependencies": {
+ "babel-cli": "6.6.5",
+ "babel-core": "^6.8.0",
+ "babel-eslint": "6.0.2",
+ "babel-loader": "^6.2.4",
+ "babel-plugin-react-require": "^2.1.0",
+ "babel-plugin-relay": "dev",
+ "babel-plugin-syntax-async-functions": "6.5.0",
+ "babel-plugin-transform-class-properties": "^6.8.0",
+ "babel-plugin-transform-object-rest-spread": "^6.6.5",
+ "babel-polyfill": "6.7.4",
+ "babel-preset-es2015": "^6.6.0",
+ "babel-preset-react": "^6.5.0",
+ "babel-preset-stage-0": "6.5.0",
+ "babel-relay-plugin": "0.8.1",
+ "http-server": "^0.9.0",
+ "jsx-loader": "^0.13.2",
+ "react": "^15.0.2",
+ "react-dom": "^15.0.2",
+ "relay-compiler": "dev",
+ "webpack": "^1.13.0",
+ "webpack-dev-server": "^1.14.1"
+ }
+}
diff --git a/samples/relay-modern-starter-kit/relay-modern-starter-kit.fsproj b/samples/relay-modern-starter-kit/relay-modern-starter-kit.fsproj
new file mode 100644
index 000000000..25744d4c7
--- /dev/null
+++ b/samples/relay-modern-starter-kit/relay-modern-starter-kit.fsproj
@@ -0,0 +1,147 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ 474179d3-0090-49e9-88f8-2971c0966077
+ Exe
+ FSharp.Data.GraphQL
+ FSharp.Data.GraphQL.ModernRelay
+ v4.5
+ 4.4.0.0
+ FSharp.Data.GraphQL.ModernRelay
+
+
+
+ true
+ full
+ false
+ false
+ .\bin\Debug\mono
+ TRACE;DEBUG
+ 3
+ --warnon:1182
+
+
+ pdbonly
+ true
+ true
+ .\bin\Release\mono
+ TRACE
+ 3
+ .\bin\Release\FSharp.Data.ModernRelay.xml
+ --warnon:1182
+
+
+
+
+ True
+
+
+ ..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll
+
+
+ ..\..\packages\Suave\lib\net40\Suave.dll
+
+
+
+
+
+
+
+
+
+
+
+
+ FSharp.Data.GraphQL.Shared
+ {6768ea38-1335-4b8e-bc09-ccded1f9aaf6}
+ True
+
+
+
+
+ FSharp.Data.GraphQL.Server
+ {474179d3-0090-49e9-88f8-2971c0966077}
+ True
+
+
+
+ 11
+
+
+ true
+ full
+ false
+ false
+ .\bin\Debug
+ DEBUG;TRACE
+ 3
+ --warnon:1182
+ x64
+
+
+ pdbonly
+ true
+ true
+ .\bin\Release
+ TRACE
+ 3
+ .\bin\Release\FSharp.Data.GraphQL.xml
+ --warnon:1182
+ x64
+
+
+
+
+ $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets
+
+
+
+
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/relay-modern-starter-kit/scripts/jsonToGraphql.js b/samples/relay-modern-starter-kit/scripts/jsonToGraphql.js
new file mode 100644
index 000000000..fe22777a9
--- /dev/null
+++ b/samples/relay-modern-starter-kit/scripts/jsonToGraphql.js
@@ -0,0 +1,11 @@
+var fs = require('fs');
+
+const {
+ buildClientSchema,
+ printSchema
+} = require('graphql/utilities');
+
+fs.readFile('./data/schema.json', "utf8", (err, str) => {
+ if (err) throw err;
+ fs.writeFileSync('./data/schema.graphql', printSchema(buildClientSchema(JSON.parse(str).data)));
+})
\ No newline at end of file
diff --git a/samples/relay-modern-starter-kit/server.fs b/samples/relay-modern-starter-kit/server.fs
new file mode 100644
index 000000000..953bec680
--- /dev/null
+++ b/samples/relay-modern-starter-kit/server.fs
@@ -0,0 +1,144 @@
+open System
+
+let address = "127.0.0.1"
+let port = 8083
+
+// Data
+
+type Widget =
+ { Id: string;
+ Name: string }
+
+type User =
+ { Id: string;
+ Name: string;
+ Widgets: Widget list }
+
+let viewer = {
+ Id = "1"
+ Name = "Anonymous"
+ Widgets = [
+ { Id = "1"; Name = "What's it"}
+ { Id = "2"; Name = "Who's it"}
+ { Id = "3"; Name = "How's it"} ]}
+
+let getUser id = if viewer.Id = id then Some viewer else None
+let getWidget id = viewer.Widgets |> List.tryFind (fun w -> w.Id = id)
+
+// Schema definition
+open FSharp.Data.GraphQL
+open FSharp.Data.GraphQL.Types
+open FSharp.Data.GraphQL.Relay
+
+let rec Widget = Define.Object(
+ name = "Widget",
+ description = "A shiny widget",
+ interfaces = [ Node ],
+ fields = [
+ Define.GlobalIdField(fun _ w -> w.Id)
+ Define.Field("name", String, fun _ w -> w.Name)])
+
+and User = Define.Object(
+ name = "User",
+ description = "A person who uses our app",
+ interfaces = [ Node ],
+ fields = [
+ Define.GlobalIdField(fun _ w -> w.Id)
+ Define.Field("name", String, fun _ w -> w.Name)
+ Define.Field("widgets", ConnectionOf Widget |> Nullable, "A person's collection of widgets", Connection.allArgs,
+ fun ctx user ->
+ let totalCount = user.Widgets.Length
+ let widgets, hasNextPage =
+ match ctx with
+ | SliceInfo(Forward(n, after)) ->
+ match after with
+ | Some (GlobalId("Widget", id)) ->
+ let i = user.Widgets |> List.indexed |> List.pick (fun (i, e) -> if e.Id = id then Some i else None)
+ user.Widgets |> List.skip (i+1) |> List.take n,
+ i+1+n < totalCount
+ | None ->
+ user.Widgets |> List.take n,
+ n < totalCount
+ | _ -> failwithf "Cursor %A is not 'Widget' global id" after
+ | _ -> user.Widgets, false
+ let edges = widgets |> Seq.map (fun b -> { Cursor = toGlobalId "Widget" (string b.Id); Node = b }) |> Seq.toArray
+ let headCursor = edges |> Array.tryHead |> Option.map (fun edge -> edge.Cursor)
+ let pi = { HasNextPage = hasNextPage; EndCursor = headCursor; StartCursor = None; HasPreviousPage = false }
+ let con = { TotalCount = Some totalCount; PageInfo = pi; Edges = edges }
+ Some con
+ )])
+
+and Node = Define.Node(fun () -> [ User; Widget ])
+
+let Query = Define.Object("Query", [
+ Define.NodeField(Node, fun ctx () id ->
+ match id with
+ | GlobalId("User", i) -> getUser i |> Option.map box
+ | GlobalId("Widget", i) -> getWidget i |> Option.map box
+ | _ -> None)
+ Define.Field("viewer", User, fun _ () -> viewer)])
+
+let schema = Schema(query = Query, config = { SchemaConfig.Default with Types = [ User; Widget ]})
+let ex = Executor(schema)
+
+// server initialization
+open Suave
+open Suave.Operators
+open Newtonsoft.Json
+open FSharp.Data.GraphQL.Execution
+
+let settings = JsonSerializerSettings()
+settings.ContractResolver <- Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
+let json o = JsonConvert.SerializeObject(o, settings)
+
+let tryParse fieldName data =
+ let raw = Text.Encoding.UTF8.GetString data
+ if raw <> null && raw <> ""
+ then
+ let map = JsonConvert.DeserializeObject