go-starter-kit icon indicating copy to clipboard operation
go-starter-kit copied to clipboard

A sample app would be really helpful

Open charl opened this issue 8 years ago • 35 comments

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.

charl avatar Oct 10 '15 14:10 charl

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?

olebedev avatar Oct 12 '15 05:10 olebedev

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.

charl avatar Oct 14 '15 13:10 charl

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?

olebedev avatar Oct 15 '15 16:10 olebedev

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:

  • How to render two templates (login and home page) using the react render. I know I could render the whole app without making a redirect, but I want to reduce the amount of js and css in the login page.
  • Use boostrap with react-boostrap. Even though the css can be included in the html template, I want to explore the best way to include boostrap's css classes (I might import the styles and let webpack do it's magic or append the css and user classnames to handle class injection, I really don't know yet).
  • Public and private API (using JWT tokens)
  • IF POSSIBLE: Generate swagger files to document the API

Thing I'm not interested in testing:

  • Database storage (or any storage layer), there are plenty of tutorials (and sample projects) that tackle these technologies.
  • Old browsers. If it runs on the latest Google Chrome, Safari and Firefox, it will be good enough for me.

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

javiercbk avatar Nov 25 '15 14:11 javiercbk

+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.

Kielan avatar Jan 24 '16 14:01 Kielan

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

olebedev avatar Jan 24 '16 15:01 olebedev

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.

whatisgravity avatar Jan 24 '16 18:01 whatisgravity

@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?

Kielan avatar Jan 25 '16 14:01 Kielan

@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

widnyana avatar Feb 14 '16 13:02 widnyana

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

kockok avatar May 24 '16 13:05 kockok

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.

olebedev avatar May 24 '16 21:05 olebedev

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

kockok avatar May 27 '16 08:05 kockok

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

geir54 avatar Oct 18 '16 20:10 geir54

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

olebedev avatar Oct 19 '16 03:10 olebedev

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

geir54 avatar Oct 19 '16 10:10 geir54

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

olebedev avatar Oct 19 '16 10:10 olebedev

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

kockok avatar Oct 25 '16 06:10 kockok

@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.

iKonrad avatar Nov 26 '16 13:11 iKonrad

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

tanis2000 avatar Mar 14 '17 08:03 tanis2000

add docker for mac quick start demo #72

llitfkitfk avatar Mar 21 '17 04:03 llitfkitfk

@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.

tanis2000 avatar Mar 21 '17 09:03 tanis2000

@tanis2000 nice! What did you use for authentication?

iKonrad avatar Mar 22 '17 01:03 iKonrad

@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.

tanis2000 avatar Mar 22 '17 07:03 tanis2000

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.

iKonrad avatar Mar 24 '17 00:03 iKonrad

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

tanis2000 avatar Mar 24 '17 07:03 tanis2000

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

iKonrad avatar Mar 24 '17 08:03 iKonrad

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 avatar Mar 24 '17 10:03 tanis2000

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 avatar Mar 24 '17 10:03 tanis2000

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.

tanis2000 avatar Mar 24 '17 11:03 tanis2000

@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?

skydiator avatar Mar 30 '17 17:03 skydiator