Commit 9a25a8ca authored by jnsone11's avatar jnsone11
Browse files

add ssr

parent 54bab7b0
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build" : "docker build -t workshop/graphqlapi .", "build" : "docker build --no-cache -t workshop/graphqlapi .",
"showImages" : "docker images", "showImages" : "docker images",
"start" : "node_modules/.bin/postgraphile -c postgresql://postgres@backend-database-postgresql/postgres -s workshop -w" "start" : "node_modules/.bin/postgraphile -o -c postgresql://postgres@backend-database-postgresql/postgres -s workshop -w"
}, },
"author": "Jens Neuse <jens.neuse@gmx.de>", "author": "Jens Neuse <jens.neuse@gmx.de>",
"license": "ISC", "license": "ISC",
......
webpackJsonp([0],{16:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const l=n(0),o=n(18),r=n(28);o.hydrate(l.createElement(()=>l.createElement(r.default,null),null),document.getElementById("react-root"))},28:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const l=n(0),o=n(0);t.default=class extends o.Component{render(){return l.createElement("div",null,"Hello World !!!")}}},5:function(e,t,n){n(6),e.exports=n(16)}},[5]); webpackJsonp([0],{240:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(0),a=l(24);t.default=class extends r.Component{render(){return n.createElement("div",null,n.createElement("h1",null,"Header"),n.createElement("ul",null,n.createElement("li",null,n.createElement(a.Link,{to:"/"},"Main")),n.createElement("li",null,n.createElement(a.Link,{to:"/authors"},"Authors")),n.createElement("li",null,n.createElement(a.Link,{to:"/articles"},"Articles"))))}}},241:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(0);t.default=class extends r.Component{render(){return n.createElement("div",null,n.createElement("p",null,"Main"))}}},242:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(0),a=l(77),o=l(31),u=a.default`
\ No newline at end of file query getAllAuthors {
allAuthors {
totalCount
nodes {
nodeId
id
name
bornIn
articlesByAuthorId {
totalCount
}
}
}
}
`;t.default=class extends r.Component{render(){return n.createElement("div",null,n.createElement("p",null,"Authors"),n.createElement(o.Query,{query:u},e=>e.loading?n.createElement("p",null,"Loading..."):e.error?n.createElement("p",null,e.error.message):n.createElement("ol",null,e.data.allAuthors.nodes.map(e=>n.createElement("li",null,n.createElement("h3",null,e.name),n.createElement("h4",null,e.bornIn),n.createElement("p",null,"Total Articles Count: ",e.articlesByAuthorId.totalCount))))))}}},252:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(0),a=l(77),o=l(31),u=a.default`
query getAllArticles {
allArticles {
totalCount
nodes {
nodeId
id
title
description
content
authorId
authorByAuthorId {
id
}
}
}
}
`;t.default=class extends r.Component{render(){return n.createElement("div",null,n.createElement(o.Query,{query:u},e=>e.loading?n.createElement("div",null,"Loading..."):e.error?n.createElement("div",null,"Error: ",e.error.message):n.createElement("div",null,n.createElement("p",null,"Articles"),n.createElement("ol",null,e.data.allArticles.nodes.map(e=>n.createElement("li",{key:e.nodeId},n.createElement("h3",null,e.title),e.description&&n.createElement("p",null,e.description)||n.createElement("p",null,"No description available")))))))}}},45:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(0),a=l(24),o=l(240),u=l(241),c=l(242),d=l(252);t.default=class extends r.Component{render(){return n.createElement("div",null,n.createElement(o.default,null),n.createElement(a.Route,{exact:!0,path:"/",component:u.default}),n.createElement(a.Route,{exact:!0,path:"/authors",component:c.default}),n.createElement(a.Route,{exact:!0,path:"/articles",component:d.default}))}}},82:function(e,t,l){l(83),e.exports=l(92)},92:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=l(0),r=l(94),a=l(24),o=l(31),u=l(200),c=l(218),d=l(231),s=new c.default({link:new u.default({uri:"/graphql"}),cache:(new d.InMemoryCache).restore(window.__APOLLO_STATE__)}),i=l(45),m=document.getElementById("react-root"),E=e=>n.createElement(o.ApolloProvider,{client:s},n.createElement(a.BrowserRouter,null,n.createElement(e,null)));e.hot.accept(45,()=>{Promise.resolve().then(()=>l(45)).then(e=>{r.hydrate(E(e.default),m)})}),r.hydrate(E(i.default),m)}},[82]);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
/* tslint:disable */
// This file was automatically generated and should not be edited.
export interface getAllArticlesQuery {
// Reads and enables pagination through a set of `Article`.
allArticles: {
__typename: "ArticlesConnection",
// The count of *all* `Article` you could get from the connection.
totalCount: number | null,
// A list of `Article` objects.
nodes: Array< {
__typename: "Article",
// A globally unique identifier. Can be used in various places throughout the system to identify this single value.
nodeId: string,
id: string,
title: string | null,
description: string | null,
content: string | null,
authorId: string,
// Reads a single `Author` that is related to this `Article`.
authorByAuthorId: {
__typename: "Author",
id: string,
} | null,
} | null >,
} | null,
};
export interface getAllAuthorsQuery {
// Reads and enables pagination through a set of `Author`.
allAuthors: {
__typename: "AuthorsConnection",
// The count of *all* `Author` you could get from the connection.
totalCount: number | null,
// A list of `Author` objects.
nodes: Array< {
__typename: "Author",
// A globally unique identifier. Can be used in various places throughout the system to identify this single value.
nodeId: string,
id: string,
name: string,
bornIn: string | null,
// Reads and enables pagination through a set of `Article`.
articlesByAuthorId: {
__typename: "ArticlesConnection",
// The count of *all* `Article` you could get from the connection.
totalCount: number | null,
},
} | null >,
} | null,
};
This diff is collapsed.
...@@ -7,23 +7,39 @@ ...@@ -7,23 +7,39 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build-server": "webpack --config webpack.server.config.js --progress", "build-server": "webpack --config webpack.server.config.js --progress",
"build-client": "webpack --config webpack.client.config.js --progress", "build-client": "webpack --config webpack.client.config.js --progress",
"start": "node ./dist/server/server.js" "start": "node ./dist/server/server.js",
"introspect-graphql-schema": "./node_modules/.bin/apollo-codegen introspect-schema http://localhost:8080/graphql --output schema.json",
"generate-graphql-ts": "./node_modules/.bin/apollo-codegen generate **/*.tsx --addTypename --schema schema.json --target typescript --output graphql-types.ts"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/graphql": "^0.12.3",
"@types/react": "^16.0.35", "@types/react": "^16.0.35",
"@types/react-dom": "^16.0.3", "@types/react-router-dom": "^4.2.3",
"apollo-cache-inmemory": "^1.1.5",
"apollo-client": "^2.2.1",
"apollo-link-http": "^0.6.1-beta.6",
"express": "^4.16.2", "express": "^4.16.2",
"graphql": "^0.12.3",
"graphql-tag": "^2.6.1",
"node-fetch": "^2.0.0-alpha.9",
"postgraphile": "^4.0.0-alpha2.30",
"react": "^16.2.0", "react": "^16.2.0",
"react-apollo": "^2.0.4",
"react-dom": "^16.2.0", "react-dom": "^16.2.0",
"typescript": "^2.6.2" "react-router-dom": "^4.2.2",
"typescript": "^2.6.2",
"whatwg-fetch": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.11.0", "@types/express": "^4.11.0",
"@types/node": "^9.4.0", "@types/node": "^9.4.0",
"@types/node-fetch": "^1.6.7",
"@types/react-dom": "^16.0.3",
"@types/systemjs": "^0.20.6", "@types/systemjs": "^0.20.6",
"@types/webpack-env": "^1.13.4", "@types/webpack-env": "^1.13.4",
"apollo-codegen": "^0.18.3",
"awesome-typescript-loader": "^3.4.1", "awesome-typescript-loader": "^3.4.1",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"react-hot-loader": "^3.1.3", "react-hot-loader": "^3.1.3",
......
This diff is collapsed.
import * as React from 'react' import * as React from 'react'
import {Component} from 'react' import {Component} from 'react'
import {hydrate} from 'react-dom' import {hydrate} from 'react-dom'
import {BrowserRouter as Router} from 'react-router-dom'
import { ApolloProvider } from 'react-apollo';
import HttpLink from "apollo-link-http";
import ApolloClient from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
declare let window: {
__APOLLO_STATE__: any
}
const apolloClient = new ApolloClient({
link: new HttpLink({ uri: "/graphql" }),
cache: new InMemoryCache().restore(window.__APOLLO_STATE__)
});
import Routes from '../common/routes' import Routes from '../common/routes'
...@@ -14,9 +28,11 @@ declare let module: { ...@@ -14,9 +28,11 @@ declare let module: {
const render = (Routings: React.ComponentClass) => { const render = (Routings: React.ComponentClass) => {
return ( return (
<div> <ApolloProvider client={apolloClient}>
<Router>
<Routings/> <Routings/>
</div> </Router>
</ApolloProvider>
) )
} }
......
import * as React from 'react'
import {Component} from 'react'
import gql from 'graphql-tag';
import {getAllArticlesQuery} from '../../../graphql-types'
import { graphql } from 'react-apollo';
const query = gql`
query getAllArticles {
allArticles {
totalCount
nodes {
nodeId
id
title
description
content
authorId
authorByAuthorId {
id
}
}
}
}
`
interface Props {
data: getAllArticlesQuery
}
class AllArticles extends Component<Props> {
render() {
return (
<div>
{JSON.stringify(this.props)}
</div>
);
}
}
export default graphql<getAllArticlesQuery,{},Props>(query)(AllArticles);
\ No newline at end of file
import * as React from 'react'
import gql from 'graphql-tag';
import {graphql,QueryProps} from 'react-apollo'
import {getAllAuthorsQuery} from '../../../graphql-types';
const query = gql`
query getAllAuthors {
allAuthors {
totalCount
nodes {
nodeId
id
name
bornIn
articlesByAuthorId {
totalCount
}
}
}
}
`;
interface Props {
data: getAllAuthorsQuery
}
class AllAuthors extends React.Component<Props> {
render() {
return (
<div>
<p>{JSON.stringify(this.props)}</p>
</div>
)
}
}
export default graphql<getAllAuthorsQuery,{},Props>(query)(AllAuthors);
\ No newline at end of file
import * as React from 'react'
import {Component} from 'react'
import {Link} from 'react-router-dom'
class Header extends Component {
render() {
return (
<div>
<h1>Header</h1>
<ul>
<li>
<Link to="/">Main</Link>
</li>
<li>
<Link to="/authors">Authors</Link>
</li>
<li>
<Link to="/articles">Articles</Link>
</li>
</ul>
</div>
)
}
}
export default Header;
\ No newline at end of file
import * as React from 'react'
import {Component} from 'react'
class Home extends Component {
render(){
return (
<div>
<p>
Main
</p>
</div>
)
}
}
export default Home;
\ No newline at end of file
import * as React from 'react' import * as React from 'react'
import {Component} from 'react' import {Component} from 'react'
import {Route} from 'react-router-dom'
import Header from './components/header'
import Main from './components/home'
import AllAuthors from './components/allAuthors'
import AllArticles from './components/allArticles'
class Routes extends Component { class Routes extends Component {
render(){ render(){
return ( return <div>
<div> <Header/>
<div>Hello World!!</div> <Route exact path="/" component={Main} />
<div>OK OK</div> <Route exact path="/authors" component={AllAuthors}/>
<Route exact path="/articles" component={AllArticles}/>
</div> </div>
)
} }
} }
......
import * as express from 'express' import * as express from 'express'
import {postgraphile} from 'postgraphile'
import render from './serverRender' import render from './serverRender'
const IS_PRODUCTION: boolean = process.env.NODE_ENV === 'production'; const IS_PRODUCTION: boolean = process.env.NODE_ENV === 'production';
...@@ -8,6 +8,11 @@ const HOST: string = 'localhost'; ...@@ -8,6 +8,11 @@ const HOST: string = 'localhost';
const server = express(); const server = express();
server.use(postgraphile('postgres://postgres@localhost:15432/postgres','workshop',{
graphiql: true,
graphqlRoute: '/graphql'
}));
if (!IS_PRODUCTION){ if (!IS_PRODUCTION){
const webpackDevMiddleware = require("webpack-dev-middleware"); const webpackDevMiddleware = require("webpack-dev-middleware");
...@@ -24,6 +29,9 @@ if (!IS_PRODUCTION){ ...@@ -24,6 +29,9 @@ if (!IS_PRODUCTION){
} }
server.get('*', (req: express.Request, res: express.Response) => { server.get('*', (req: express.Request, res: express.Response) => {
const context = {};
render(req.url).then((html: string) => { render(req.url).then((html: string) => {
res.status(200); res.status(200);
res.end(html); res.end(html);
......
import * as React from "react"; import * as React from "react";
//import {renderToString} from "react-dom/server"
import Routes from '../common/routes' import Routes from '../common/routes'
import ApolloClient from "apollo-client";
import * as fetch from 'node-fetch'
import { ApolloProvider, renderToStringWithData} from 'react-apollo';
import HttpLink from 'apollo-link-http';
import { StaticRouter } from 'react-router';
import { InMemoryCache } from "apollo-cache-inmemory";
import {renderToString} from 'react-dom/server'
export default function(url: string) : Promise<string> { export default function(url: string) : Promise<string> {
const link = new HttpLink({
uri: 'http://localhost:8080/graphql'
});
const client = new ApolloClient({
ssrMode: true,
link,
cache: new InMemoryCache()
});
const context = {};
const App = ( const App = (
<div> <ApolloProvider client={client}>
<Routes/> <StaticRouter location={url} context={context}>
</div> <Routes/>
</StaticRouter>
</ApolloProvider>
); );
//const applicationHTML = renderToString(App); console.log(context);
return new Promise((resolve: (result:any )=> void,reject: (error:any) => void ) => {
return new Promise((resolve,reject) => { renderToStringWithData(App)
resolve(`<!DOCTYPE html> .then((reactHtmlContent) => {
<html>
<head> const initialState = JSON.stringify(client.extract()).replace(/</g, '\\u003c');
<link rel="apple-touch-icon" sizes="57x57" href="/icon.png" />
</head> resolve(`<!DOCTYPE html>
<body> <html>
<div id="react-root"></div> <head>
<script type="text/javascript" src="/vendor.js"></script> <link rel="apple-touch-icon" sizes="57x57" href="/icon.png" />
<script type="text/javascript" src="/client.js"></script> </head>
</body> <body>
</html>`); <div id="react-root">${reactHtmlContent}</div>
<script>window.__APOLLO_STATE__=${initialState}</script>
<script type="text/javascript" src="/vendor.js"></script>
<script type="text/javascript" src="/client.js"></script>
</body>
</html>`);
}).catch((reason:any) => {
reject(reason);
});
}); });
} }
\ No newline at end of file
declare module 'lodash.flowright';
\ No newline at end of file
declare module 'postgraphile'
\ No newline at end of file
...@@ -8,28 +8,33 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl ...@@ -8,28 +8,33 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const IS_PRODUCTION = process.env.NODE_ENV === 'production'; const IS_PRODUCTION = process.env.NODE_ENV === 'production';
console.log('IS_PRODUCTION: ',IS_PRODUCTION); console.log('IS_PRODUCTION: ', IS_PRODUCTION);
let plugins = [ let plugins = [
new webpack.optimize.CommonsChunkPlugin({ new webpack
name: 'vendor', .optimize
filename: 'vendor.js', .CommonsChunkPlugin({
minChunks: function (module, count) { name: 'vendor',
return module.context && module.context.indexOf("node_modules") !== -1; filename: 'vendor.js',
} minChunks: function (module, count) {
}), return module.context && module
.context
.indexOf("node_modules") !== -1;
}
}),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': { 'process.env': {
'NODE_ENV': JSON.stringify('production'), 'NODE_ENV': JSON.stringify('production')
} }
}), }),
new webpack.optimize.AggressiveMergingPlugin(), new webpack
new webpack.optimize.OccurrenceOrderPlugin(), .optimize
.AggressiveMergingPlugin(),
new webpack
.optimize
.OccurrenceOrderPlugin(),
new UglifyJSPlugin(), new UglifyJSPlugin(),
new webpack.LoaderOptionsPlugin({ new webpack.LoaderOptionsPlugin({minimize: true, debug: false}),
minimize: true,
debug: false
}),
/*new CompressionPlugin({ /*new CompressionPlugin({
asset: "[path].gz[query]", asset: "[path].gz[query]",
algorithm: "gzip", algorithm: "gzip",
...@@ -40,36 +45,32 @@ let plugins = [ ...@@ -40,36 +45,32 @@ let plugins = [
]; ];
if (IS_PRODUCTION) { if (IS_PRODUCTION) {
plugins.push(new BundleAnalyzerPlugin({ plugins.push(new BundleAnalyzerPlugin({analyzerMode: 'static'}));
analyzerMode: 'static'
}));
} else { } else {
plugins.push(new webpack.HotModuleReplacementPlugin());