Got it running on Heroku deployment as of April 2021 at https://cirosantilli-realworld-express.herokuapp.com/
Includes a working single-process fullstack Heroku deployment by building the https://github.com/gothinkster/react-redux-realworld-example-app frontend statically and serving it from the public folder of the backend.
CSS bundling to use https://demo.productionready.io/main.css from the SCSS source is also setup, but blocked on: gothinkster/conduit-bootstrap-template#5 (comment)
This started as a fork of: https://github.com/sigoden/node-express-realworld-example-app (deleted on 2021-06-21 probably because of fear of backslash from the Chinese communist party after Ciro created some issues there) which was likely a port of https://github.com/gothinkster/node-express-realworld-example-app both of which are backend implementations of the awesome https://github.com/gothinkster/realworld sigoden was a good starting point, but it notably did not implement any of the many-to-many relations properly in SQL, rather hacking it with strings. We have now implemented those properly with relations (and it was not a breeze partly because sequelize is so quirky).
The react-redux-realworld-example-app
is stored at react-redux-realworld-example-app as a submodule, and some small modifications have been made to it from the upstream, they are tracked at: https://github.com/cirosantilli/react-redux-realworld-example-app
Website behaviour is intended to match the front and backend upstreams as closely as possible, minus except possible obvious bugs.
Other versions of this repository include:
-
Next.js instead of React + Redux: https://github.com/cirosantilli/node-express-sequelize-nextjs-realworld-example-app
Tested on an Ubuntu 21.04 development host.
npm install npm run dev
The browser automatically pops the development front-end server at http://localhost:4101 which makes requests to the backend server that runs at http://localhost:3000.
You might also want to generate some test data as mentioned at: Generate demo data.
The SQLite database is located at db.sqlite3
.
npm install npm run build npm start
The website can now be seen at: http://localhost:3000
This setup does not start the front-end development server at http://localhost:4101, but rather compiles the front-end files statically, and serves them on the public folder of the backend server, giving performance representative performance characteristics of the frontend.
This setup is much closer to the final type of setup that will run in production, and it runs a single server.
Running it locally might help debug some front-end deployment issues.
But otherwise you will just normally use the Local run as identical to deployment as possible setup instead for development, because this setup lacks important debug features such as:
-
hot code reloading
This setup still runs on NODE_ENV=development
, which implies that sqlite is used, so no further database setup is needed for PostgreSQL. This also means however that you might experience different performance characteristics or behaviour not ironed out by Sequelize between SQLite vs PotsgreSQL.
Here we use PostgreSQL instead of SQLite with the prebuilt static frontend. Note that optimized frontend is also used on the SQLite setup described at Local optimized frontend).
For when you really need to debug some deployment stuff locally
Setup:
sudo apt install postgresql # Become able to run psql command without sudo. sudo -u postgres createuser -s "$(whoami)" createdb "$(whoami)" createdb node_express_sequelize_realworld psql -c "CREATE ROLE node_express_sequelize_realworld_user with login password 'a'" psql -c 'GRANT ALL PRIVILEGES ON DATABASE node_express_sequelize_realworld TO node_express_sequelize_realworld_user' echo "SECRET=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 256)" >> .env
Run:
npm run build npm run start-prod
then visit the running website at: http://localhost:3000/
To Generate demo data for this instance run:
NODE_ENV=production DATABASE_URL='postgres://node_express_sequelize_realworld_user:a@localhost:5432/node_express_sequelize_realworld' ./bin/generate-demo-data.js --force-production
First time setup:
heroku git:remote -a cirosantilli-realworld-express # Automatically sets DATABASE_URL. heroku addons:create heroku-postgresql:hobby-dev # Otherwise the react build picks up the .eslint from this directory, # which specifies a plugin that is not installed because it is in the # devDependencies of this package.json... For the love of God, this is # a deployment, not a CI. # https://stackoverflow.com/questions/55821078/disable-eslint-that-create-react-app-provides heroku config:set DISABLE_ESLINT_PLUGIN=true # Notably to skip ultra-slow sqlite native build. heroku config:set NPM_CONFIG_PRODUCTION=true YARN_PRODUCTION=true heroku config:set SECRET="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 256)"
Every update:
npm run deploy
Get a PostgreSQL shell:
heroku psql
DELETE ALL DATA IN THE DATABASE and Generate demo data inside Heroku:
heroku run bash ./bin/generate-demo-data.js --force-production
We have to run heroku run bash
instead of heroku ps:exec
because the second command does not set DATABASE_URL
:
Edit a file in Heroku to debug that you are trying to run manually, e.g. by adding print commands, uses https://github.com/hakash/termit minimal nano-like text editor:
heroku ps:exec termit app.js
For the backend, add debugger;
to the point of interest, and run as:
npm run back-inspect
On the debugger, do a c
to continue so that the server will start running (impossible to skip automatically: https://stackoverflow.com/questions/16420374/how-to-disable-in-the-node-debugger-break-on-first-line), and then trigger your event of interest from the browser:
npm run front
If you run as:
VERBOSE=1 npm run dev
this enables the following extra logs:
-
a log line for every request done
DEBUG='sequelize:sql:*' npm run start-prod
Note that this will first erase any data present in the database:
./bin/generate-demo-data.js
You can then login with users such as:
-
user0@mail.com
-
user1@mail.com
and password asdf
.
Test data size can be configured with CLI parameters, e.g.:
./bin/generate-demo-data.js --n-users 5 --n-articles-per-user 8 --n-follows-per-user 3
In case you’ve broken things so bad that the very first GET blows up the website and further requests don’t respond https://stackoverflow.com/questions/61927814/how-to-disable-open-browser-in-cra
BROWSER=none npm run dev
This gives you time to setup e.g. Network recording in Chrome Developer Tools to be able to understand what is going on.
This is a big problem during development, not sure how to solve it: sequelize/sequelize#8199 (comment)
These tests are part of https://github.com/gothinkster/realworld which we track here as a submodule.
Test test method uses Postman, but we feel that it is not a very good way to do the testing, as it uses JSON formats everywhere with embedded JavaScript, presumably to be edited in some dedicated editor like Jupyter does. It would be much better to just have a pure JavaScript setup instead.
They test the JSON REST API without the frontend.
First start the backend server in a terminal:
npm run back-test
npm run back-test
will make our server use a clean one-off in-memory database instead of using the default in-disk development ./db.sqlite3
as done for npm run back
.
Then on another terminal:
npm run test-api
Run a single test called Register
instead:
npm run test-api -- --folder Register
TODO: many tests depend on previous steps, notably register. But we weren’t able to make it run just given specific tests e.g. with:
npmr test-api -- --folder 'Register' --folder 'Login and Remember Token' --folder 'Create Article'
only the last --folder
is used. Some threads say that multiple ones can be used in newer Newman, but even after updating it to latest v5 we couldn’t get it to work:
Ideally, all tests should be API test, so that they will work across any backend implementation more easily, and test the system more fully.
However, setting up full API tests can be annoying, especially the user creation part, as especially since Postman is so clunky.
Furthermore, the API tests can have a slower setup time, since by going directly to the backend API we can call bulkCreate
which can be much faster than creating objects one by one.
So sometimes, especially for things like model relations, we will just revert to a some quick API test:
npm test
To run those tests on PostgreSQL intead, first setup as in Local run as identical to deployment as possible, and then
NODE_ENV=production DATABASE_URL='postgres://node_express_sequelize_realworld_user:a@localhost:5432/node_express_sequelize_realworld' npm test
Methodology:
-
time after click event https://stackoverflow.com/questions/67750849/how-to-filter-by-event-type-in-chrome-devtools-profile-tab-e-g-to-see-mouse-cli/67750850#67750850 up until new page renders, not considering any images on the new page, just text
-
caches warmed by clicking all pages involved just before the experiment
-
hardware: Lenovo ThinkPad P51
-
browser: Chromium 91
Results:
-
click from global feed to article
-
this repo at 98e628a76b4253bb51ff4a8659305fabfda1b1f8,
npm run dev
: 0.2s -
this repo at 98e628a76b4253bb51ff4a8659305fabfda1b1f8,
npm run start
: 0.2s -
this repo at 98e628a76b4253bb51ff4a8659305fabfda1b1f8 + frontend https://github.com/cirosantilli/next-realworld-example-app/tree/d510e33745966618ee95243ad8f7d3d974adcf14
npm run dev
: 0.2s -
this repo at 98e628a76b4253bb51ff4a8659305fabfda1b1f8 + frontend https://github.com/cirosantilli/next-realworld-example-app/tree/d510e33745966618ee95243ad8f7d3d974adcf14
npm run
: 0.2s
-
Notable React Redux upstream bugs:
-
gothinkster/react-redux-realworld-example-app#197 Your Feed pagination is just completely broken. This is not an API bug in this repo.
The naming conventions are meant to be similar to the JavaScript naming conventions:
-
camel case on tables and columns
-
tables start with a capital letter, because they are class-like
-
columns start with a lowercase letter, because they are field-like
-
tables use singular form
Achieving this requires fighting a bit with sequelize, which by default produces inconsistent names on foreign keys.
https://github.com/Varun-Hegde/Conduit_NodeJS/tree/99cc32f19a42d74ff9729765772b4676c537a755 some of the API tests were failing, and some parts of the code didin’t feel as clean as I’d like, so I ended up using https://github.com/sigoden/node-express-realworld-example-app as a basis. However I later learnt they did do/attempt to do the many to many relatioships properly unlike sigoden which just hacked with strings. The critical "hard" querry however https://github.com/Varun-Hegde/Conduit_NodeJS/blob/99cc32f19a42d74ff9729765772b4676c537a755/controllers/articles.js#L271, which finds "posts by users I follow" and which best exercises the ORM’s is not done nicel in a single SQL command as achieved in this repository after a lot of suffering.