An extensible REST server template
-
Swagger (Pending deployment)
-
JSDoc (Pending deployment)
-
Example deployment (TODO)
File Tree
.
├── config
│ ├── jsdoc.config.json
│ └── swagger.json
├── .env
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── index.js
├── LICENSE
├── .nvmrc
├── package.json
├── package-lock.json
├── .prettierrc.js
├── Procfile
├── README.md
├── server
│ ├── controllers
│ │ └── index.js
│ ├── index.js
│ ├── middleware
│ │ ├── 404.js
│ │ ├── 500.js
│ │ ├── model-finder.js
│ │ └── __tests__
│ │ ├── 404.test.js
│ │ ├── 500.test.js
│ │ └── model-finder.test.js
│ ├── models
│ │ ├── book
│ │ │ ├── book.js
│ │ │ └── book.model.js
│ │ └── resty-wrapper.js
│ ├── routes
│ │ ├── index.js
│ │ └── __tests__
│ │ └── index.test.js
│ └── __tests__
│ ├── index.test.js
│ └── supergoose.js
└── .travis.yml
10 directories, 30 files
This is a vanilla Express server template. It is intended to be forked to provide a basis for more complex Express servers that include authentication, logging, or other features.
This project's most interesting feature is that it provides a mechanism for dynamically importing new Mongoose models into the project and wrapping them in basic controller methods. This feature makes the template imminently reusable.
Potential future features are listed in the TODO section below.
Prerequisites
Downloads
-
Fork this project to your GitHub account.
-
Clone the repo to you local filesystem using the appropriate links for your username.
Using ssh:
git clone git@github.com:[Your-Username]/express-pug-server.git
Using https:
git clone https://github.com/[Your-Username]/express-pug-server.git
You can also download and uppack the zip
file.
- Navigate into the project directory.
cd express-pug-server
- Install the project's dependencies
Using npm:
npm i
Using yarn:
yarn install
Environment
In the project folder, create a .env
file to hold private environmental variables and an empty data
folder to hold your local database. Do not commit either to version control.
Create entries in your .env
file for:
-
MONGODB_URI
-
PORT
Good default values for the current server (which uses a books
model) are:
# .env
MONGODB_URI=mongodb://localhost:27017/books
PORT=3000
Running the server
- Start the MongoDB database in your project root. The command will probably look like:
mongod --dbpath=./data --port 27017
-
Start the Node process with
npm run start
ornpm run watch:server
. -
If you want to use BrowserSync, start it with
npm run watch:ui
.
Output in the terminal should confirm the processes are running.
REST API
API routes for this server look like /api/v1/{model}
.
router.get('/', c.index);
router.get('/api/v1/:model', c.getRecords);
router.get('/api/v1/:model/:id', c.getRecords);
router.post('/api/v1/:model', c.createRecord);
router.put('/api/v1/:model/:id', c.updateRecord);
router.patch('/api/v1/:model/:id', c.patchRecord);
router.delete('/api/v1/:model/:id', c.deleteRecord);
The dynamic :model
in these routes is automatically interpolated by the model-finder
middleware at ./server/middleware/model-finder.js
.
The RESTy Wrapper at ./server/models/rest-wrapper.js
, when instantiated on the model ./server/models/{model}/{model}.js
, will allow a number of operations that correspond to the methods by which they are accessed in the API routes.
These methods are as follows:
-
get(id?)
→ If anid
argument is provided, this method will return a Promise that resolves to a single document object. If noid
argument is provided, this method will return a Promise that resolves to an array of all the collection's documents. -
post(obj)
→ This method takes a document object and returns a Promise that resolves to the posted document, which has been added to the database. -
patch(id, obj)
→ This method takesid
andobj
arguments and updates a document with the givenid
with theobj
in the collection. No new documents will be created. The method returns a Promise that resolves to the updated document. -
put(id, obj)
→ This method takesid
andobj
arguments and updates the document with the givenid
in the collection with theobj
. If an object with the givenid
does not exist, it will be created. The method returns a Promise that resolves to the updated document. -
delete(id)
→ The method takes anid
argument, deletes the document from the collection, and returns a Promise that resolves to the deleted document.
Customizing the server
Because routes interpolate the names of their models when accessed, it is necessary to name the model and its parent folder to match the intended routes.
Example: Adding a book
model
-
Create a folder for your model inside
./server/models/
. It should be named for the route that should be used to access it. For example, abook
model that should be accessed on the route/api/v1/book
requires the folderbook
at./server/models/book
. -
Within the
book
folder, create a Mongoose schema and instantitate a Mongoose model from it. This might be done in a file named./server/models/book/book.model.js
, but the name here isn't as important. -
To wrap the model in Mongoose-ready controller methods named for the REST methods by which they are accessed, instantiate an instance of the RESTy wrapper (exported from
./server/models/resty-wrapper.js
) with the Mongoose model and export it. That will look like:
const book = new RESTyWrapper(BookModel);
module.exports = book;
The book
model can now be accessed at /api/v1/book
and will respond to REST requests as described above.
Running tests
The current setup with the book
model can be tested with the following commands:
npm run test
npm run watch:test
npm run lint
Custom models will require their own tests, but those tests will likely be very similar! It might be possible to make testing more modular or automatic (based on interpolation from the ./server/models/{model}
) in the future.
These items might eventually be addressed in this or a fork of this project:
-
Continue to build up and clarify this README.
-
Move model-specific tests to
./server/models/{model}
for reusability. -
The project should be given a default view engine, particularly one that takes advantage of the
mongoose-schema-jsonschema
to dynamically create forms based on Mongoose schemas. -
Improve Swagger documentation
-
Improve JSDoc documentation
-
Enable event logging using Socket.io
-
Consider use-cases/security implications of packages like
cors
andmethod-override
. -
Add Basic and Bearer authentication via OAuth or Auth0
-
Add an alternative RESTy Wrapper intended to work with a PostgreSQL database and an environmental variable to toggle which database type is used.