simple-relay-starter
A simple example of how to get started with Relay using some slightly different approaches to relay-starter-kit that may make it easier to navigate for first-time users, especially Node.js users.
Unlike relay-starter-kit, this
project uses Browserify instead of
Webpack, does not use a proxy for the GraphQL
endpoint and does not require ES6 features for any server-side code, so it can
be run directly with node
– resulting in less boilerplate and making it
easier to understand the code.
Example
$ npm install$ npm run build$ npm start
Then navigate to http://localhost:3000 and
observe the network request to /graphql
that Relay makes to retrieve the data.
For development, you can use:
$ npm run dev
Which will build the schema and then watch for any file changes, rebuilding the schema and/or restarting the server as necessary.
Here are the files involved:
App.js
:
var React = var Relay = // A simple top-level component that illustrates how to render Relay-fetched// data using props. In this case Relay will populate a `user` property that// has a collection of `widgets` based on the queries and fragments we give it// further below.Component { return <div> <h2>User: thispropsusername</h2> <h2>Widgets:</h2> <ul> /* In schema/schema.js we define a Connection between users and widgets */ /* Connections use `edges` and `node` to hold paging info and child items */ thispropsuserwidgetsedges </ul> </div> } // The component we need to export is a Relay wrapper around our App component// from above. It declares the GraphQL fragments where we list the properties// we want to be fetched – eg, user.name, user.widgets.edges, etcexportsContainer = Relay // The Relay root container needs to know what queries will occur at the top// level – these configurations are currently called Routes in Relay, but this// name is misleading and under review so we don't use it here.exportsqueries = name: 'AppQueries' // can be anything, just used as an identifier params: {} queries: // We can use this shorthand so long as the component we pair this with has // a fragment named "user", as we do above. Relay.QL`query { user }`
browser.js
:
var React = var ReactDOM = var Relay = var App = // This file is the entry point on the browser – browserify will compile it, as// well as App.js and any other client-side dependencies and create// public/bundle.js which will be requested by public/index.html ReactDOM
public/index.html
:
<!-- include React and Relay scripts (we don't bundle them) --><!-- now request our browserified bundle which will run the React.render -->
server.js
:
var express = var graphqlHttp = var schema = // The server is just a simple Express appvar app = // We respond to all GraphQL requests from `/graphql` using the// `express-graphql` middleware, which we pass our schema to.app // The rest of the routes are just for serving static filesappapp app
schema/database.js
:
// We use these types to hold data and resolve from GraphQL types in our schema { thisid = id thisname = name} { thisid = id thisuserId = userId thisname = name} // In a realistic system, the get functions below would return objects from a// datastore like a DB or a REST API instead of an in-memory store like this.// You can also return promises for async fetching var users = 1 'Anonymous' var widgets = 1 1 'What\'s-it' 2 1 'Who\'s-it' 3 1 'How\'s-it' moduleexports = User: User Widget: Widget { return users0 } { return users0 } { return widgets0 } { return widgets }
schema/schema.js
:
var GraphQL = var GraphQLRelay = var db = // This module exports a GraphQL Schema, which is a declaration of all the// types, queries and mutations we'll use in our system. // Relay adds some specific types that it needs to function, including Node, Edge, Connection // Firstly we need to create the Node interface in our system. This has nothing// to do with Node.js! In Relay, Node refers to an entity – that is, an object// with an ID. // To create this interface, we need to pass in a resolving function as the// first arg to nodeDefinitions that can fetch an entity given a global Relay// ID. The second arg can be used to resolve an entity into a GraphQL type –// but it's actually optional, so we'll leave it out and use isTypeOf on the// GraphQL types further below. var nodeDefinitions = GraphQLRelay // We can now use the Node interface in the GraphQL types of our schema var widgetType = name: 'Widget' description: 'A shiny widget' // Relay will use this function to determine if an object in your system is // of a particular GraphQL type { return obj instanceof dbWidget } // We can either declare our fields as an object of name-to-definition // mappings or a closure that returns said object (see userType below) fields: id: GraphQLRelay name: type: GraphQLGraphQLString description: 'The name of the widget' // This declares this GraphQL type as a Node interfaces: nodeDefinitionsnodeInterface var userType = name: 'User' description: 'A person who uses our app' { return obj instanceof dbUser } // We use a closure here because we need to refer to widgetType from above { return id: GraphQLRelay name: type: GraphQLGraphQLString description: 'The name of the user' // Here we set up a paged one-to-many relationship ("Connection") widgets: description: 'A user\'s collection of widgets' // Relay gives us helper functions to define the Connection and its args type: GraphQLRelayconnectionType args: GraphQLRelayconnectionArgs // You can define a resolving function for any field. // It can also return a promise if you need async data fetching { // This wraps a Connection object around your data array // Use connectionFromPromisedArray if you return a promise instead return GraphQLRelay } } interfaces: nodeDefinitionsnodeInterface // Now we can bundle our types up and export a schema// GraphQL expects a set of top-level queries and optional mutations (we have// none in this simple example so we leave the mutation field out)moduleexports = query: name: 'Query' fields: // Relay needs this to query Nodes using global IDs node: nodeDefinitionsnodeField // Our own root query field(s) go here user: type: userType { return db }