Skip to content

gothinkster realworld app built with Node.js Express.js Sequelize. Deployed full-stack to Heroku as a single process with a statically built React frontend.

Notifications You must be signed in to change notification settings

cirosantilli/node-express-sequelize-realworld-example-app

Repository files navigation

Node.js + Express.js + Sequelize + SQLite/PostgreSQL + React + Redux + Heroku Example Realworld App

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:

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:

Results:

Notable React Redux upstream bugs:

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.

About

gothinkster realworld app built with Node.js Express.js Sequelize. Deployed full-stack to Heroku as a single process with a statically built React frontend.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published