https://github.com/jkettmann/universal-react-relay-starter-kit
A starter kit for React in combination with Relay including a GraphQL server, server side rendering, code splitting, i18n, SEO.
https://github.com/jkettmann/universal-react-relay-starter-kit
boilerplate graphql i18n internationalization isomorphic react reactjs relay seo server-side-rendering ssr starter-kit universal
Last synced: 5 days ago
JSON representation
A starter kit for React in combination with Relay including a GraphQL server, server side rendering, code splitting, i18n, SEO.
- Host: GitHub
- URL: https://github.com/jkettmann/universal-react-relay-starter-kit
- Owner: jkettmann
- License: mit
- Created: 2017-09-17T07:23:44.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-02-25T03:17:21.000Z (about 7 years ago)
- Last Synced: 2025-04-18T05:53:37.152Z (11 days ago)
- Topics: boilerplate, graphql, i18n, internationalization, isomorphic, react, reactjs, relay, seo, server-side-rendering, ssr, starter-kit, universal
- Language: JavaScript
- Homepage:
- Size: 1.99 MB
- Stars: 14
- Watchers: 2
- Forks: 2
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
This starter kit aims at helping developers starting a professional app to create a basic setup quickly. It will use AWS infrastructure, but it should be simple to switch to other providers.
## Content
- [Setup](#setup)
- [Installation](#installation)
- [Technologies](#technologies)
- [Design decisions](#design-decisions)
- [How to create a new route](#how-to-create-a-new-route)
- [Functional components](#functional-components)
- [Roadmap](#roadmap)
- [Credits](#credits)## Setup
### Currently necessary for development
- [AWS S3 Bucket](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_S3_BUCKET.md)
- [AWS DynamoDB](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_DYNAMO_DB.md)
- [AWS Cognito](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_COGNITO.md)
- [AWS IAM User](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_IAM_USER.md)### Only for production
- [AWS Elastic Beanstalk](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_ELASTIC_BEANSTALK.md)
### CI integrations
- [GitHub](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/GITHUB_CI.md)
- [GitLab](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/GITLAB_CI.md)## Installation
This project uses [dotenv](https://github.com/motdotla/dotenv) to set environment variables from a `.env` file. Therefore you need to add a file named `.env` to the root of the project. The content should be as follows. Please fill out `...` with your AWS or Facebook keys etc.```
### Common environment variables ###
NODE_ENV=development
AWS_REGION=eu-central-1
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...### Environment variables for the app server ###
PORT_APP=3000
GRAPHQL_ENDPOINT=http://localhost:8080
# name of the AWS S3 Bucket used to store uploaded images
S3_IMAGE_BUCKET=...
FACEBOOK_APP_ID=...### Environment variables for the GraphQL server ###
PORT_GRAPHQL=8080
# Important for allowing CORS access.
# Should be a domain including protocol, like https://example.com
APP_ENDPOINT=http://localhost:3000
# Important for setting cookie from GraphQL server for the app.
# Should be a domain, like example.com
COOKIE_DOMAIN=localhost
# Select a secret for cookies to be signed with
COOKIE_SECRET=...
AWS_COGNITO_USER_POOL_ID=...
AWS_COGNITO_USER_POOL_CLIENT_ID=...
AWS_COGNITO_IDENTITY_POOL_ID=...
```- Install [watchman](https://facebook.github.io/watchman/)
- (optional) When you installed `watchman` you can also easily switch to [yarn](https://yarnpkg.com/en/)- run following commands:
- `yarn install` or `npm install`
- `yarn run relay-compiler` or `npm run relay-compiler`
- `yarn start` or `npm start`- open [localhost:3000](http://localhost:3000) in your browser
## Technologies
- [React](https://github.com/facebook/react)
- [Relay modern](https://github.com/facebook/relay) as GraphQL client
- [React Universal Component](https://github.com/faceyspacey/react-universal-component) for server-side-rendering and code-splitting
- [styled-components](https://github.com/styled-components/styled-components)
- [recompose](https://github.com/acdlite/recompose)
- [GraphQL](https://github.com/graphql/graphql-js)
- [Express](https://github.com/expressjs/express)
- Hot reloading on client ([react-hot-loader](https://github.com/gaearon/react-hot-loader)) and server ([webpack-hot-server-middleware](https://github.com/glenjamin/webpack-hot-middleware))## Design decisions
- Kind of flat component structure: The [relay-compiler](https://facebook.github.io/relay/docs/relay-compiler.html) enforces unique fragment names. This is easily achieved using a flat component structure.
At the same time [styled-components](https://github.com/styled-components/styled-components) requires defining simple styled wrapper components. Component files stay very clean when defining these wrapper components in own files.
This is why components are defined in `index.js` inside their own folder with smaller wrapper components next to them. Currently Relay container components have to have the same name as their fragment due to [relay-compiler](https://facebook.github.io/relay/docs/relay-compiler.html). See [this issue](https://github.com/facebook/relay/issues/2093).
## How to create a new route
Add your new page component to `client/pages/MyNewPage/MyNewPage.js`. If the component does not need any data from the server, just add your component like following
```
const MyNewPage = () => (
Some content
)export default MyNewPage
```In order to enable code splitting via `react-universal-component` for this page, add the file `client/async/MyNewPage.js` and export your page component like following:
```
export { default } from '../pages/MyNewPage/MyNewPage'
```Now we need to add a new route to the Router. Open `client/Routes.js` and add following route:
```
```
Now you should have a new page at `/myNewPage`.
If the page component needs fetched data, use `Relay.createFragmentContainer` or another appropriate container function
```
const MyNewPage = ({ viewer }) => (
You are currently {!viewer.isLoggedIn && 'not'} logged in.
)MyNewPage.propTypes = {
viewer: PropTypes.shape({
isLoggedIn: PropTypes.bool,
}).isRequired,
}export default createFragmentContainer(
MyNewPage,
graphql`
fragment MyNewPage_viewer on Viewer {
isLoggedIn
}
`,
)
```Additionally you have to define a query for the route. Open the `Routes.js` and add the query.
```
const myNewPageQuery = graphql`query Routes_MyNewPage_Query { viewer { ...MyNewPage_viewer } }````
In this case the necessary viewer attributes will be fetched by `found-relay` and passed to your component as `viewer` prop.
## Functional components
Functional components are easier to test and understand, see following comparison.
```
class Button extends React.Component {
render() {
return (
{this.props.label}
)
}
}
``````
const Button = ({ label }) => (
{label}
)
```It becomes a bit trickier when the component needs to have some logic, for example a click handler which passes the components id to its parent or setting the label to upper case. This is what [recompose](https://github.com/acdlite/recompose) is used for.
```
class Button extends React.Component {
onClick = () => {
const { id, onClick } = this.props
onClick(id)
}render() {
const label = label.toUpperCase()
return (
{label}
)
}
}export default Button
``````
import { compose, withHandlers, withProps } from 'recompose'const Button = ({ label, onClick }) => (
{label}
)const enhance = compose(
withHandlers({
onClick: ({ id, onClick }) => () => onClick(id)
}),
withProps(({ label }) => ({ label: label.toUpperCase() })),
})export default enhance(Button)
```See [recompose](https://github.com/acdlite/recompose) for more information.
## Notes
- `path-to-regexp` is installed with version `1.7.0` for [found](https://github.com/4Catalyzer/found). If not adding it to the dependencies a warning is logged (`Incorrect version of path-to-regexp imported.`) and server side rendering won't work. Compare [this issue](https://github.com/4Catalyzer/found/issues/126).
## Roadmap
- [x] Use real database
- [x] Login and registration (probably using [Passport](http://passportjs.org/) and [AWS Cognito](https://aws.amazon.com/de/cognito/))
- [x] Server side security using [helmet](https://github.com/helmetjs/helmet)
- [ ] Use facebook identity provider on cognito user pool instead identity pool
- [ ] Unit and snapshot tests using [Jest](https://github.com/facebook/jest) and end-to-end tests using [cypress](https://www.cypress.io/)## Credits
### Icons
User icon made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/)
Close icon made by [Cole Bemis](https://www.flaticon.com/authors/cole-bemis) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/)