olebedev / go-starter-kit

[abandoned] Golang isomorphic react/hot reloadable/redux/css-modules/SSR starter kit
Other
2.82k stars 358 forks source link

A sample app would be really helpful #11

Open charl opened 8 years ago

charl commented 8 years ago

There are a lot of moving parts to this setup and a sample app (say something like https://facebook.github.io/flux/docs/todo-list.html) would be really helpful.

olebedev commented 8 years ago

You mean docs that described what is going on or just simple app?

Btw, there is everything exactly like any regular isomorphic application except server rendering and data fetching. Good news that data fetching via fetch polyfill. It is new standard for client/server communication, see here.

Whatever, docs are required. I will make a detailed docs soon. Or maybe would you like to give me a hand?

charl commented 8 years ago

I'd be happy to give you a hand @olebedev but I am just starting down this road.

I had a simple app in mind that illustrates how everything fits together as well as a description of the tooling you use to build apps with go-starter-kit for total beginners.

If there's anything a beginner can help with let me know and I'll pitch in.

olebedev commented 8 years ago

Hey @charl, thanks for you help. I am really appreciate it. And it seems that no need to be extraordinary experienced Gopher to improve README file here. All that we need just describe all steps in detail in some understandable way.

So, what about the new shiny application? At this moment I don't see any reason to develop something new. Because the existing app shows all specific isomorphic render cases. In particular there is only one thing that need to be clarify, it is data fetching at the server side. The rest are pretty regular things. More important things are about how the app executes, how to build the app and which endpoints the app has. Of course I ready to describe in draft any aspect if you will ask me in gitter chat.

For example we can start from more detailed what it contains description. And after we can describe the project structure(base on tree command output) and first developer steps(improving of install section). I am convinced that it will help some totally new developers to understand how to use it in right way. At the end we can describe some front-end goodies which the app contains. Again, I repeat it. No need to develop new app. Existing is cover all specific cases and even more.

There is only one reason why I cannot do it myself - english is not my native language. It would be great if you will help me with it. So, what can you say? Is it interesting for you?

javiercbk commented 8 years ago

I'm making a quick single page app with a login (no database) just to see how that scenario would play out.

There are a couple of things I'm interested in:

Thing I'm not interested in testing:

If you guys think of any cool use case that might improve this sample app, I'm happy to hear from you.

Kielan commented 8 years ago

+1 in particular the make file seems to be causing a lot of people issues including myself. I had to rewrite it to get it to work.

There's no reason for so many moving parts and reserved values for file locations ect as it takes a while for someone jumping into the project to grok. This is an opinionated statement and I can see why some might disagree but based on the issues it seems to be a big problem with this repo.

olebedev commented 8 years ago

Obviously, It is hard to start different projects in the current state as it overengineered a bit. Now it looks like a framework and this is the problem. BTW, the same starter kits based on node.js overengineered as well. The project needs to be simplified.

As mentioned, I need help with it. In particular, I need to know what is the most important part for you. It could be the matter when I will start to simplify it. So guys, please tell me what is the most important part of it for you? Which things from here are sould get rid out as you see?

@Kielan what kind of rewriting was applied, could you provide it?

/cc @charl, @javiercbk, @whatisgravity, @smd686s

whatisgravity commented 8 years ago

I think a simple todo list that provides a CRUD example would be very helpful. With how much is there it is hard to see where to start.

Kielan commented 8 years ago

@olebedev Correction as I got ahead of myself, I am still re writing the makefile. Some documentation on what LDFLAGS is doing as well as BINDATA would be helpful. I don't see a bindata.go file in the server folder.

In general I don't want to have any version control method forced upon me.

I'm not sure why you are telling people to reset their gopath to a more specific folder, as the project should already be within their gopath.

Also curious about the command go get ./... does what exactly?

widnyana commented 8 years ago

@Kielan if I simplify it, that command will install all the required dependency into $GOPATH

we know npm has package.json on the root dir and we can install it all via npm i, in golang the tools will iterate through import statement listed on the source code, and install all the package.

the bad news, go get doesnt handle package versioning. it will fetch the master branch, that's why srlt, gopkg.in and etc exists

kockok commented 8 years ago

A sample app with user authentication would be perfect! And how about production deploy on a VPS?

olebedev commented 8 years ago

Hi @kockok, a sample app with user authentication is not the case for this repo. Bit I REALLY appreciate if some could do it separately. Regarding deployment, you could just add deploy target as CI derective. Wercker allows us to deploy built artifact everywhere we want.

kockok commented 8 years ago

I found this client side auth pretty neat. https://github.com/lynndylanhurley/redux-auth

geir54 commented 7 years ago

I've started playing around with this in https://github.com/geir54/go-starter-kit if any one is still interested

olebedev commented 7 years ago

@geir54, looks good to me. Please, send a PR when you finish

geir54 commented 7 years ago

@olebedev Do you want this as part of this repo? I was thinking to have a seperate full app repo

olebedev commented 7 years ago

@geir54, yup, it would be better. I will point the app in readme.

kockok commented 7 years ago

I'm still struggling to deploy on a VPS(Digitalocean). I used to using Capistrano for that. Any tools similar for this kit? Thanks.

iKonrad commented 7 years ago

@kockok I've used flightplan.js for my previous project. I see no reason why it wouldn't work with this one. It's quite simple to set up and extend.

tanis2000 commented 7 years ago

@olebedev do you know if anyone actually used your repo to create an app with user authentication like you suggested?

llitfkitfk commented 7 years ago

add docker for mac quick start demo #72

tanis2000 commented 7 years ago

@llitfkitfk that's nice. I did something similar for a project I'm working on that spawned from this repo. BTW I have a working app with authentication so if there's anyone who has already made a repo for such an app we could get that running together.

iKonrad commented 7 years ago

@tanis2000 nice! What did you use for authentication?

tanis2000 commented 7 years ago

@iKonrad nothing fancy. It's just standard JWT through the echo framework. I had to do a bit of magic with cookies to keep the authentication working both on the client and with server side rendering, but that's all it takes.

iKonrad commented 7 years ago

Is it session/cookie based or pure API? Since this project is isomorphic I'd like to have authentication that takes advantage of server side rendering.

tanis2000 commented 7 years ago

I initially went for localStorage but then moved to cookies for your same reasons. The good thing about cookies is that I can just grab them from the browser, push it to the server, bridge it to the JSVM and do whatever I need before rendering the page on the server. That way it works both client and server side the same way.

There's only one concern left which is retrieving data from promises of action creators on the server as the current implementation doesn't wait for promises to be resolved before rendering

iKonrad commented 7 years ago

You need redux thunk middleware for that and register it in the router.js file.

` import thunkMiddleware from 'redux-thunk';

// Add state logger if (process.env.NODE_ENV !== 'production') { middlewares.push(require('redux-logger')()); middlewares.push(thunkMiddleware); } `

And then, in your action creator you need to return the dispatch object so thunk can handle it

Change it from: export function setConfig(config) { return { type: SET_CONFIG, config }; }

To:

export function setConfig(config) { return (dispatch) => { $.get('/api/users', (data) => { dispatch({ users: data }); }) } }

So thunk will keep the promise until your API call is finished

tanis2000 commented 7 years ago

I'm already doing that but it's not enough.

here's the equivalent of the function run in onEnter in the /usage route:

export function loadConfig() {
  return function(dispatch) {
    return fetch('/api/v1/system/conf').then((r) => {
      return r.json();
    }).then((conf) => {
      dispatch(setConfig(conf));
    });
  };
}

The promise returned by fetch isn't being waited upon. I'm not sure this is actually an issue with @olebedev implementation of fetch or the way I'm using the thunk middleware that's wrong.

Calling that function from the onEnter like this leads to the fetch function being called but not waited to resolve:

  static onEnter({store, nextState, replaceState, callback}) {
    store.dispatch(loadConfig());
    callback();
  }
tanis2000 commented 7 years ago

Since onEnter() isn't working as expected I thought I'd just do it another way and implement a static function called fetchData on all the components needing some initial data and wrap the renderToString call into a Promise chain.

In the component's class:

  static fetchData({ query, params, store, history }) {
    return store.dispatch(loadConfig());
  }

In toString.js:

          function getReduxPromise () {
            let { query, params } = renderProps;
            let comp = renderProps.components[renderProps.components.length - 1].WrappedComponent;
            let promise = comp.fetchData ?
              comp.fetchData({ query, params, store, /*history*/ }) :
              Promise.resolve();
            return promise;
          }

          getReduxPromise()
          .then(() => {
            result.app = renderToString(
              <Provider store={store}>
                <RouterContext {...renderProps} />
              </Provider>
            );
            const { title, meta } = Helmet.rewind();
            result.title = title.toString();
            result.meta = meta.toString();
            result.initial = JSON.stringify(store.getState());
            return cbk(result);
          });

That way all of my non authenticated routes are actually retrieving the data needed and rendering correctly.

But as you can guess, authenticated components are being composed within this kind of component:

export function requireAuthentication(Component) {

  class AuthenticatedComponent extends React.Component {

    componentWillMount() {
      this.checkAuth(this.props.isAuthenticated);
    }

    componentWillReceiveProps(nextProps) {
      this.checkAuth(nextProps.isAuthenticated);
    }

    checkAuth(isAuthenticated) {
      if (!isAuthenticated) {
        let redirectAfterLogin = this.props.location.pathname;
        this.context.router.push(`/?next=${redirectAfterLogin}`);
      }
    }

    render() {
      return (
                <div>
                    {this.props.isAuthenticated === true
                        ? <Component {...this.props} />
                        : null
                    }
                </div>
      );

    }
  }

  AuthenticatedComponent.contextTypes = {
    router: React.PropTypes.object.isRequired
  };

  const mapStateToProps = (state) => ({
    token: state.auth.token,
    userName: state.auth.userName,
    isAuthenticated: state.auth.isAuthenticated
  });

  return connect(mapStateToProps)(AuthenticatedComponent);

}

And this composed component is wrapping the inner component that needs to be protected from anonymous access and it doesn't expose the fetchData static method of the inner component.

This is where I am at the moment. I'm pretty sure there should be a way to either call the inner fetchData method from within the React markup or some other way to get down there in the chain.

This also rules out @olebedev fetch from the equation as it's definitely working fine.

tanis2000 commented 7 years ago

The solution was easier than expected. I just needed to add a fetchData method to the AuthenticatedComponent that checks if the wrapped component has one and returns it or eventually returns an empty promise.

Here's the code:

    static fetchData({ query, params, store, history }) {
      let promise = Component.fetchData ? Component.fetchData({ query, params, store, history }) : Promise.resolve();
      return promise;
    }

That's basically all that's needed to have auth work on the server side apparently.

skydiator commented 7 years ago

@tanis2000 I am also playing around with the implementation of authentication part. Do you have the complete code in a repo to share with us?

tanis2000 commented 7 years ago

@skydiator I have everything in a private repo as I'm using that for a private project. If there's enough interest and more contributors I'd be happy to create an open one to share with everybody :)

iKonrad commented 7 years ago

@tanis2000 I'm having the same issue now (with rendering data from fetch on first call).

Would you mind posting your toString.js file and fetchData methods?

EDIT: I got it working - thanks for suggesting a solution with fetchData static method.

I just had to refactor some code as it didn't like when fetchData wasn't present in Component:

`let { query, params } = renderProps; let comp = renderProps.components[renderProps.components.length - 1].WrappedComponent; let promise; if (typeof(comp.fetchData) !== 'undefined') { promise = comp.fetchData({ query, params, store, /history/ }); promise.then(() => { renderComponent(); }); } else { renderComponent(); }

function renderComponent() { result.app = renderToString(

            );
            const { title, meta } = Helmet.rewind();
            result.title = title.toString();
            result.meta = meta.toString();
            result.initial = JSON.stringify(store.getState());
            return cbk(result);
        }

`

iKonrad commented 7 years ago

Now I have a different issue.

I'm in the middle of writing a custom cookie/session based authentication.

I've got a cookie "SESSION_ID" which is retrieved on the server and processed. But when I render the react template on the server, and it executes the 'fetchData' function, it doesn't pass any cookie to the /api endpoint and therefore I can't authenticate the request. How did you solve that?

kockok commented 7 years ago

@iKonrad Can you write a litter tutorial on how to use flightplan.js to do continuous deployment with this golang kit? Thanks.

softgripper commented 7 years ago

And I can't get this building on windows - and I'm no makefile guru, but it's doing some wizardry.

I'd love to be able to install this and run through an app/tutorial.

Right now, I think I'm going to just write my own :(

It was painless to get up and running on Linux in a VM, but I hate using that OS for development. Linux GUI still isn't quite at Windows/Mac level for developing - and my mac is old and dusty.