Closed chriscalo closed 4 years ago
Is there [...} working example somewhere
I just created a Vue + Express starter: https://github.com/brillout/goldpage-vue-express-starter.
If you want more: https://github.com/reframejs/goldpage/tree/master/examples.
I tried my best to follow the docs, but I wasn't able to get things running
The docs are fresh — I'm currently working on them and they will improve a lot in the next coming days/weeks.
super-exciting
Thanks & I'm curious; what is it about Goldpage that excites you?
Huge thank you for putting that starter repo together ๐ just gave it a shot, and it seems to be working great.
Thanks & I'm curious; what is it about Goldpage that excites you?
Glad you asked.
SSR + client-side hydration is the only path to a great user experience for fast, interactive web apps. However, I don't want to author components in two different ways, once on the server and once on the client. And the solutions for SSR + client-side hydration in the Vue ecosystem I've tried way overcomplicate things.
I never could quite get Vue SSR working right, and they seem to want to force you to use their Router
and Vuex
, even though I'd prefer to use express
's much simpler routing system. I'd rather treat Vue like any other server-side templating language: fetch data, pass that data to a render function, and return the string I get a string back to the browser. And I don't want an explicit build step or anything beyond very minimal configuration.
Nuxt.js is a huge improvement, especially because of how easily it integrates with express, but they kinda force you to pretend that fetching data from the server and the client are the same by asking you to call API endpoints from the server in their asyncData()
method, which fails for me on the client when there are no Express req
or res
objects. It just feels simpler to fetch data server-side and worry about fetching data from an API on the client when the need arises.
Anyway, Goldpage is the first solution I've seen that hits all of these points, and it even does so without being tightly coupled to any particular ecosystem (React, Vue, express, hapi, etc). I love that.
There are a few things that I would change about Goldpage to make it better match what I'm trying to do. (Do you agree with these? Should I open issues for these or is it too early for that?)
esm
in my server-side code and config files, but that doesn't seem to be possible with goldpage. Might it be possible to include a -r esm
flag to require a module like node
and nodemon
have? Example package.json
file:{
"scripts": {
"start": "goldpage dev -r esm index.js"
},
"dependencies": {
"esm": "^3.2.25",
"goldpage": "^0.5.1"
},
}
nuxt
CLI:import { Nuxt, Builder } from "nuxt";
import express from "express";
import config from "~/nuxt.config.js";
export const server = express();
export default server;
const nuxt = new Nuxt(config);
// render Nuxt routes
server.use(nuxt.render);
// hot-reloading in dev
if (config.dev) {
new Builder(nuxt).build();
}
goldpage
yells at me when more than one page config file has the same name. Here's how I prefer to structure things, but this doesn't work with goldpage because there are two index.page.js
files (even though they're in different folders):/ => pages/index.page.js
/foo => pages/foo/index.page.js
Thanks again for putting effort into this long-standing problem! ๐
Huge thank you for putting that starter repo together
Let me know how the starter works out for you.
they seem to want to force you to use their Router and Vuex
Thanks to Goldpage's domRender
and htmlRender
you have full control over how your pages are rendered. This means that you can easily integrate Goldpage with any Vue library you want.
even though I'd rather to use express's much simpler routing system. I'd rather treat Vue like any other server-side templating language: fetch data, pass that data to a render function, and return the string I get a string back to the browser.
The thing is that Goldpage (or any other tool that does SSR such as Nuxt.js) need to know the routes of your pages. For example, if a page has the route /hello/:name
then Goldpage needs to provide the :name
parameter to your page root Vue component.
And I don't want an explicit build step or anything beyond very minimal configuration.
SSR is tightly coupled with building. A specificity of SSR is that your Vue components run in the browser as well as in your Node.js server. Adding SSR to an app requires changes in the Webpack build config. At that point Goldpage might as well take care of all the build.
Nuxt.js is a huge improvement, especially because of how easily it integrates with express, but they kinda force you to pretend that fetching data from the server and the client are the same by asking you to call API endpoints from the server in their asyncData() method
Something like asyncData
(Goldpage's pendant is addInitialProps
) is required. That's because Nuxt.js (or Goldpage) needs to fetch all the data of your page before rendering it to HTML. Otherwise your data couldn't be rendered to HTML. (Vue renders the initial state of your Vue components to HTML. There is no such thing as stateful components when doing SSR.)
which fails for me on the client when there are no Express req or res objects.
With Goldpage you can render a page only to HTML. Your page is then not loaded nor rendered in the browser. And addInitialProps
is not loaded nor called in the browser.
It just feels simpler to fetch data server-side and worry about fetching data from an API on the client when the need arises.
As far as I see, you are totally free to fetch data as you wish with Goldpage.
Anyway, Goldpage is the first solution I've seen that hits all of these points, and it even does so without being tightly coupled to any particular ecosystem (React, Vue, express, hapi, etc). I love that.
Yea, domRender
& htmlRender
is one of my favorite Goldpage feature. It makes so many things much simpler and much easier.
(Do you agree with these? Should I open issues for these or is it too early for that?)
Yes please let me know what you need. And yes, you can write all of it in this ticket.
I really love using ES modules via esm in my server-side code
Goldpage builds your server-side code thus you can use ES modules for your server-side code.
For example: /examples/es-modules/server.js.
Better yet, I'd prefer to have full control of the server and run it in Node rather than via CLI. Nuxt.js supports this pretty well. I love how easy it is to integrate Nuxt with express without using the nuxt CLI:
You can do that with Goldpage.
Instead of:
$ goldpage dev path/to/server.js
Omit the server path and do:
$ goldpage dev
Same for goldpage build
.
Goldpage then doesn't build your server code. But note that you loose server auto-reload and you'll have to take care of auto-reloading your server yourself.
I'd recommend against going down that path; what's your issue with letting Goldpage build all your code?
Goldpage is based on Webpack but we will use Parcel once Parcel v2 is out. This will mean that you will never ever have to deal with build again; things will just work, that's the nice thing about Parcel.
I like structuring files in web apps in a way that matches URL paths. However, goldpage yells at me when more than one page config file has the same name. Here's how I prefer to structure things, but this doesn't work with goldpage because there are two index.page.js files (even though they're in different folders):
I thought about this as well but didn't implement it yet. I opened a ticket for it: https://github.com/reframejs/goldpage/issues/3.
Thanks again for putting effort into this long-standing problem!
It's fun to build Goldpage :). Let me know if you have any further questions. Happy to talk to you.
I've been very busy the last couple of days — I'll reply more promptly in the future.
No worries about the response delay. I wrote quite a lot there ๐. Take all of this feedback only as far as it's useful for you. And to be clear, everything in the first half of my last response was trying to explain why Goldpage feels like a much better approach than other SSR + client-side hydration approaches I've tried.
Goldpage builds your server-side code thus you can use ES modules for your server-side code.
That is fantastic ๐ I will try this out.
Omit the server path and do:
$ goldpage dev
What does this do? I found examples/static-website/package.json, but it's not clear to me what goldpage dev
does.
what's your issue with letting Goldpage build all your code?
Trust, I guess. If I can be confident that it's flexible enough to meet my future needs and is extendable when I need to do something Goldpage didn't foresee, it could work. But I'm skeptical of this because I haven't seen it work many times in the past; sooner or later, I run into some situation where I need to modify the behavior of a library to do what I need, and if I can't, I remove that library and look for replacements that are more flexible.
I guess for me it really comes down to the Do One Thing Well principle:
Here's how I'd really love to use Goldpage, inspired by Nuxt: (are there issues with this approach?)
// server.js, an express server that I completely control
import { ssr, dev } from "goldpage";
import express from "express";
import { start } from "express-start";
import config from "./goldpage.config.js";
export const server = express();
export default server;
server.use(ssr.express);
// dev build with hot-reloading
if (process.env.NODE_ENV !== "production") {
dev(config);
}
if (process.mainModule === module) {
start(server);
}
I just tried to recreate your example and was able to get it working to my liking. I see now how I'm still in full control of the server even when running goldpage dev ./server.js
.
I think the only thing keeping me from really giving Goldpage a try and fully integrating it into my app is #3. I don't need to do that anytime soon, so I'll just wait to hear any updates on that issue.
Thanks again
What does this do?
goldpage dev ./path/to/your/server.js
starts your server with auto-rebuild, browser auto-reload, and server auto-reload. It's meant to be used while developing.
If I can be confident that it's flexible enough to meet my future needs and is extendable when I need to do something Goldpage didn't foresee
With Parcel v2 everything will just work and you won't have to worry about build anymore. That's why I want to migrate from Webpack to Parcel. Note that Parcel will be zero-config the vast majority of cases but it will still be configurable and extendable. So your back will be covered.
if it uses webpack and it's easy to modify the config
Yes, Goldpage uses webpack and you can easily modify the webpack config. That said, once Goldpage uses Parcel v2, the vast majority of your build problems will disappear.
I see now how I'm still in full control of the server
Yes exactly, from the perspective of the server, Goldpage is just a middleware.
I think the only thing keeping me from really giving Goldpage a try and fully integrating it into my app is #3.
Done & published as 0.5.2
.
Did I answer all your questions?
Please let me know if you have other questions or concerns :)
Done & published as 0.5.2.
Thanks so much for that update. ๐ It seems to be working great.
goldpage dev ./path/to/your/server.js
starts your server with auto-rebuild, browser auto-reload, and server auto-reload. It's meant to be used while developing.
What I meant was: What does goldpage dev
do when a server path is not included?
Yes exactly, from the perspective of the server, Goldpage is just a middleware.
It's actually the server, too, not just middleware. When I point Goldpage to my Express app, it:
Taking care of the build for me (1.) is clearly valuable. But, for what it's worth, I would really prefer to have more control over both how Goldpage integrates with my Express server (2.) and starting the web server (3.).
The ideal workflow (for me) would be:
Because what happens if I need to pass parameters to the node process? I'm not calling node
myself, so I'm not able to do that. Or what if one day I need to move from Goldpage to another solution? It's not as simple as replacing one Express middleware function with another one, and I have to start the server myself.
I'll open a couple issues with suggestions, but obviously close them if you disagree. ๐
What does goldpage dev do when a server path is not included?
goldpage dev
builds only your browser-side code. (More precisely it builds your *.page.js
files to browser-side static assets.)goldpage dev ./path/to/server.js
builds your browser-side code and your server-side code. (More precisely: it additionally builds ./path/to/server.js
). It also starts the server on your behalf with server auto-restart.Goldpage [...] 3. starts a web server
Note that in production you start the server yourself, see the usual Goldpage scripts:
{
"scripts": {
"dev": "goldpage dev ./path/to/your/server.js",
"prod": "npm run build && npm run start",
"build": "goldpage build ./path/to/your/server.js",
"start": "export NODE_ENV='production' && node ./.build/nodejs/server"
}
}
For production you:
goldpage build ./path/to/your/server.js
export NODE_ENV='production' && node ./.build/nodejs/server
In dev, Goldpage starts the server on your behalf in order to auto-restart the sever whenever you change your server code.
I actually dislike that part myself; I'd prefer a Goldpage user to have full control over the server, even in dev. I will think about a solution.
there's a Goldpage build() function that I call myself
You can actually do that today but it's purposely not documented. I'd rather have Goldpage take care of all the build. If you a have a specific use case that require you to have more control over the build, then let me know and I will reconsider.
I would really prefer to have more control over both how Goldpage integrates with my Express server
What additional control would you want?
I'll open a couple issues with suggestions
:+1:
I found a solution which would allow you to do something like this:
{
"scripts": {
"dev": "npm run build-dev && npm run start-dev",
"build-dev": "goldpage build --watch ./path/to/your/server.js",
"start-dev": "nodemon ./.build/nodejs/server"
"prod": "npm run build && npm run start",
"build": "goldpage build ./path/to/your/server.js",
"start": "export NODE_ENV='production' && node ./.build/nodejs/server"
}
}
with https://github.com/remy/nodemon.
@chriscalo Would that be something that would interest you?
Thanks for pushing on this some more.
Small point: in the dev
script I think you'd need to run build-dev
and start-dev
in parallel because build-dev
will never exit when in watch mode (right?):
{
"scripts": {
"dev": "run-p build-dev start-dev",
"build-dev": "goldpage build --watch ./path/to/your/server.js",
"start-dev": "nodemon ./.build/nodejs/server",
"prod": "npm run build && npm run start",
"build": "goldpage build ./path/to/your/server.js",
"start": "export NODE_ENV='production' && node ./.build/nodejs/server"
},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
This approach still doesn't let me run my own server file and forces me to be aware of the implementation details of the build process (that built server is located at ./.build/nodejs/server
).
Would it not be possible for the ssr.express
handler to know to look for build artifacts inside the .build
directory (so I don't have to know that)?
With one additional changeโexposing a dev()
function that runs the goldpage builder in watch modeโthat would enable the following approach (same as from an earlier comment):
import { ssr, dev } from "goldpage";
import express from "express";
import { start } from "express-start";
import config from "./goldpage.config.js";
export const server = express();
export default server;
server.use(ssr.express);
// dev build with hot-reloading
if (process.env.NODE_ENV !== "production") {
dev(config);
}
if (process.mainModule === module) {
start(server);
}
Here's an example of doing something similar in Nuxt:
import { Nuxt, Builder } from "nuxt";
import express from "express";
import config from "~/nuxt.config.js";
export const server = express();
export default server;
const nuxt = new Nuxt(config);
// render Nuxt routes
server.use(nuxt.render);
// hot-reloading in dev
if (config.dev) {
new Builder(nuxt).build();
}
Thoughts on that approach?
I tried to describe this approach in #5 and #6. Hope that's helpful and not just creating more noise.
Goldpage now seems like a pretty good match for my app. Iโll try to convert it and let you know if I run into any issues. Thanks again for indulging all of my questions.
This is a super-exciting repo. I tried my best to follow the docs, but I wasn't able to get things running. Is there a complete, end-to-end, working example somewhere that we can checkout and tinker with?