{"id":15556449,"url":"https://github.com/jkettmann/universal-react-relay-starter-kit","last_synced_at":"2026-03-10T04:33:38.217Z","repository":{"id":98551976,"uuid":"103810828","full_name":"jkettmann/universal-react-relay-starter-kit","owner":"jkettmann","description":"A starter kit for React in combination with Relay including a GraphQL server, server side rendering, code splitting, i18n, SEO.","archived":false,"fork":false,"pushed_at":"2018-02-25T03:17:21.000Z","size":2091,"stargazers_count":14,"open_issues_count":15,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-24T20:11:59.514Z","etag":null,"topics":["boilerplate","graphql","i18n","internationalization","isomorphic","react","reactjs","relay","seo","server-side-rendering","ssr","starter-kit","universal"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jkettmann.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-09-17T07:23:44.000Z","updated_at":"2020-06-19T07:23:25.000Z","dependencies_parsed_at":"2023-07-13T08:30:48.823Z","dependency_job_id":null,"html_url":"https://github.com/jkettmann/universal-react-relay-starter-kit","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jkettmann/universal-react-relay-starter-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkettmann%2Funiversal-react-relay-starter-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkettmann%2Funiversal-react-relay-starter-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkettmann%2Funiversal-react-relay-starter-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkettmann%2Funiversal-react-relay-starter-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jkettmann","download_url":"https://codeload.github.com/jkettmann/universal-react-relay-starter-kit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkettmann%2Funiversal-react-relay-starter-kit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30324444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T01:36:58.598Z","status":"online","status_checked_at":"2026-03-10T02:00:06.579Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boilerplate","graphql","i18n","internationalization","isomorphic","react","reactjs","relay","seo","server-side-rendering","ssr","starter-kit","universal"],"created_at":"2024-10-02T15:13:55.022Z","updated_at":"2026-03-10T04:33:38.200Z","avatar_url":"https://github.com/jkettmann.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"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.\n\n## Content\n\n- [Setup](#setup)\n- [Installation](#installation)\n- [Technologies](#technologies)\n- [Design decisions](#design-decisions)\n- [How to create a new route](#how-to-create-a-new-route)\n- [Functional components](#functional-components)\n- [Roadmap](#roadmap)\n- [Credits](#credits)\n\n## Setup\n\n### Currently necessary for development\n\n- [AWS S3 Bucket](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_S3_BUCKET.md)\n- [AWS DynamoDB](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_DYNAMO_DB.md)\n- [AWS Cognito](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_COGNITO.md)\n- [AWS IAM User](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_IAM_USER.md)\n\n### Only for production\n\n- [AWS Elastic Beanstalk](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/AWS_ELASTIC_BEANSTALK.md)\n\n### CI integrations\n\n- [GitHub](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/GITHUB_CI.md)\n- [GitLab](https://github.com/jkettmann/universal-react-relay-starter-kit/docs/GITLAB_CI.md)\n\n## Installation\nThis 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.\n\n```\n### Common environment variables ###\nNODE_ENV=development\nAWS_REGION=eu-central-1\nAWS_ACCESS_KEY_ID=...\nAWS_SECRET_ACCESS_KEY=...\n\n### Environment variables for the app server ###\nPORT_APP=3000\nGRAPHQL_ENDPOINT=http://localhost:8080\n# name of the AWS S3 Bucket used to store uploaded images\nS3_IMAGE_BUCKET=...\nFACEBOOK_APP_ID=...\n\n### Environment variables for the GraphQL server ###\nPORT_GRAPHQL=8080\n# Important for allowing CORS access.\n# Should be a domain including protocol, like https://example.com\nAPP_ENDPOINT=http://localhost:3000\n# Important for setting cookie from GraphQL server for the app.\n# Should be a domain, like example.com\nCOOKIE_DOMAIN=localhost\n# Select a secret for cookies to be signed with\nCOOKIE_SECRET=...\nAWS_COGNITO_USER_POOL_ID=...\nAWS_COGNITO_USER_POOL_CLIENT_ID=...\nAWS_COGNITO_IDENTITY_POOL_ID=...\n```\n\n- Install [watchman](https://facebook.github.io/watchman/)\n- (optional) When you installed `watchman` you can also easily switch to [yarn](https://yarnpkg.com/en/)\n\n- run following commands:\n  - `yarn install` or `npm install`\n  - `yarn run relay-compiler` or `npm run relay-compiler`\n  - `yarn start` or `npm start`\n\n- open [localhost:3000](http://localhost:3000) in your browser\n\n## Technologies\n\n- [React](https://github.com/facebook/react)\n- [Relay modern](https://github.com/facebook/relay) as GraphQL client\n- [React Universal Component](https://github.com/faceyspacey/react-universal-component) for server-side-rendering and code-splitting\n- [styled-components](https://github.com/styled-components/styled-components)\n- [recompose](https://github.com/acdlite/recompose)\n- [GraphQL](https://github.com/graphql/graphql-js)\n- [Express](https://github.com/expressjs/express)\n- 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))\n\n## Design decisions\n\n- 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.\n\n  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.\n\n  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).\n\n## How to create a new route\n\nAdd 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\n\n```\nconst MyNewPage = () =\u003e (\n  \u003cdiv\u003e\n    Some content\n  \u003c/div\u003e\n)\n\nexport default MyNewPage\n```\n\nIn 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:\n\n```\nexport { default } from '../pages/MyNewPage/MyNewPage'\n```\n\nNow we need to add a new route to the Router. Open `client/Routes.js` and add following route:\n\n```\n\u003cRoute\n  path=\"/myNewPage\"\n  render={createRender('MyNewPage')}\n/\u003e\n```\n\nNow you should have a new page at `/myNewPage`.\n\nIf the page component needs fetched data, use `Relay.createFragmentContainer` or another appropriate container function\n\n```\nconst MyNewPage = ({ viewer }) =\u003e (\n  \u003cdiv\u003e\n    You are currently {!viewer.isLoggedIn \u0026\u0026 'not'} logged in.\n  \u003c/div\u003e\n)\n\nMyNewPage.propTypes = {\n  viewer: PropTypes.shape({\n    isLoggedIn: PropTypes.bool,\n  }).isRequired,\n}\n\nexport default createFragmentContainer(\n  MyNewPage,\n  graphql`\n    fragment MyNewPage_viewer on Viewer {\n      isLoggedIn\n    }\n  `,\n)\n```\n\nAdditionally you have to define a query for the route. Open the `Routes.js` and add the query.\n\n```\nconst myNewPageQuery = graphql`query Routes_MyNewPage_Query { viewer { ...MyNewPage_viewer } }`\n\n\u003cRoute\n  path=\"/myNewPage\"\n  render={createRender('MyNewPage')}\n  query={myNewPageQuery}\n/\u003e\n```\n\nIn this case the necessary viewer attributes will be fetched by `found-relay` and passed to your component as `viewer` prop.\n\n## Functional components\n\nFunctional components are easier to test and understand, see following comparison.\n\n```\nclass Button extends React.Component {\n  render() {\n    return (\n      \u003cbutton\u003e\n        {this.props.label}\n      \u003c/button\u003e\n    )\n  }\n}\n```\n\n```\nconst Button = ({ label }) =\u003e (\n  \u003cbutton\u003e\n    {label}\n  \u003c/button\u003e\n)\n```\n\nIt 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.\n\n```\nclass Button extends React.Component {\n  onClick = () =\u003e {\n    const { id, onClick } = this.props\n    onClick(id)\n  }\n\n  render() {\n    const label = label.toUpperCase()\n    return (\n      \u003cbutton onClick={this.onClick}\u003e\n        {label}\n      \u003c/button\u003e\n    )\n  }\n}\n\nexport default Button\n```\n\n```\nimport { compose, withHandlers, withProps } from 'recompose'\n\nconst Button = ({ label, onClick }) =\u003e (\n  \u003cbutton onClick={onClick}\u003e\n    {label}\n  \u003c/button\u003e\n)\n\nconst enhance = compose(\n  withHandlers({\n    onClick: ({ id, onClick }) =\u003e () =\u003e onClick(id)\n  }),\n  withProps(({ label }) =\u003e ({ label: label.toUpperCase() })),\n})\n\nexport default enhance(Button)\n```\n\nSee [recompose](https://github.com/acdlite/recompose) for more information.\n\n## Notes\n\n- `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).\n\n## Roadmap\n\n- [x] Use real database\n- [x] Login and registration (probably using [Passport](http://passportjs.org/) and [AWS Cognito](https://aws.amazon.com/de/cognito/))\n- [x] Server side security using [helmet](https://github.com/helmetjs/helmet)\n- [ ] Use facebook identity provider on cognito user pool instead identity pool\n- [ ] Unit and snapshot tests using [Jest](https://github.com/facebook/jest) and end-to-end tests using [cypress](https://www.cypress.io/)\n\n## Credits\n\n### Icons\n\nUser 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/)\nClose 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/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkettmann%2Funiversal-react-relay-starter-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjkettmann%2Funiversal-react-relay-starter-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkettmann%2Funiversal-react-relay-starter-kit/lists"}