________
| Hello! |
--------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
This repo demonstrates:
- Realtime development, using live Docker containers
- GraphQL access to a backend API
- JSON web token authentication
- Persistent data storage, using MongoDB
This repo is comprised of two main directories:
- cowsay-app is a front-end React web application
- cowsay-api is a backend Express API, using express-graphql
A third directory, mongodb_data, is required for the persistant storage of a MongoDB database
🐮 You need to create the mongodb_data
directory and give it the correct permissions. See Setup step 3 below.
All services are provisioned via Docker, using Docker Compose.
- Clone this repo to the directory of your choice:
git clone https://github.com/kimfucious/cowsay.git
-
Change into the newly cloned directory
-
Create
mongodb_data
directory and set permissions on it:
🐮 There is no initial database. You need to first create a new directory mongodb_data
and set permissions on it, like so:
mkdir mongodb_data \
&& sudo chown -hR 1001:1001 ./mongodb_data/
- After you've done the above, navigate to the root of this cloned repo
- Run
docker-compose up
- The first time run will setup MongoDB. You should see something like the following amidst the output:
mongodb_1 | 18:02:30.89 INFO ==> Deploying MongoDB from scratch...
MongoDB is up when you see waiting for connections on port 27017
🐮 If you see a MongoDB timeout error, search the log for the following:
db | mkdir: cannot create directory '/bitnami/mongodb': Permission denied
Most likely, there's a problem with that directory and/or it's permissions. See step 3 in Setup above.
-
Docker-compose will pull images, build them, and run them in three discrete containers (this can take a while on the first run):
- The web app will run on port 80 at http://localhost
- The api will run on port 4000. You can access
graphiql
at http://localhost:4000/graphql - The database will run on port 27017. You can access it, using Compass, with mongodb://user:secret@localhost:27017/cowsay?authSource=cowsay
This is all configured in
docker-compose.yml
and the Dockerfile files in the app and api directories. -
Changes made to the app or api code will restart the respective services within the running containers on save, thanks to nodemon. Changes to npm packages will not work without rebuilding the affected Docker image. See here for details.
-
The db gets created on first run of
docker-compose
, but again, you need to ensure that the file permissions are set correctly onmongodb_data
, as just stated, in order for things to work. See note here for details. -
An initial
db
root and admin user (user) are created on the first run ofdocker-compose
. These are set in the.env
file in the root directory of this repo.
🐮 Subsequent starts will not create db users, if/when you change environement variables! If you want to do that, rebuild the database. See here for help on that.
-
An initial
web-app
admin user,[email protected]
, is created when the API is first run (when there are no users). The initial password is set toPassw0rd123
. These can be changed incowsay-api/src/start.js
. -
Env files are found in each directory, including the root directory. These files have been intentionally not excluded from this repo, but should be, if/when you ever fork a copy of this repo for anything going forward. See here for details.
-
Yes, the Auth0 client secret has been rotated; thanks.
-
The
app.listen()
andmongoose.connect()
functions have been separated out ofapp.js
and placed instart.js
so as to separate facilitate testing. -
The token authentication is "manual", for demonstration purposes, rather than using a third-party solution, though I may add something else later.
-
To test the API using
graphiql
, temporarily comment out the authorization lines incowsay_api/src/graphql/resolvers.js
, like this:
getCows: (args, req) => {
// if (!req.isAuthorized) throw newError(403, "Forbidden");
return cows;
};
If you add npm packages to the app or api, these changes will cause the apps running in their containers to fail, because they are relying on an old verions of package.json
.
To get them running again, do the following:
- Optional - run Docker with the prune option:
docker system prune
- Run Docker Compose with the build option:
docker-compose up --build
If you want to change the user configuration of the MongoDB database, it's easiest to just delete the db and rebuild it.
CTRL-C
in the terminal where your randocker-run-compose
to stop all running containers- Delete the
mongodb
directory inside themongodb_data
folder without deleting the parent directory. Usesudo rm -rf mongodb_data/mongodb/
, as permissions on folder aren't yours. - Change the
.env
variables in the root of this repo as desired. - Run
docker-compose up
- Clean up unfinished async calls in React useEffect hooks, like this.
- Debouncing form value validation is interesting, but probably not worth the complexity. I prefer Formik and onBlur with Yup.
- While homespun authentication is good to understand, I think others have already built better wheels. I like Auth0 and would implement a more sophisticated approach with both Access and Refresh tokens.
- GraphQl error handling with
express-graphql
is not fun, I'll probably use Apollo on future projects, as I've read that it deals with this better. - If I were to deploy this to production, I'd probably use AWS Fargate, but before I did that, I'd consider converting the API to a serverless Lambda function and serve it up using AWS API gateway.
- It's nigh impossible to prevent password managers, like 1Password, from grabbing at your form fields.
- Using the useReducer React hook for state flow is doable, but I'm thinking that going full Redux would be better when working with lots of actions and/or more complex apps.