Opinionated structure for a node/express API server. Makes setting up and running
a server a breeze with easy config/extendibility. Automatically read controller
files and modules and build express
routes on your api server instance.
npm i -S node-api-server
const nodeApiServer = require('node-api-server')
nodeApiServer( (nodeApiServer, startServer) => {
// nodeApiServer.api => object
// nodeApiServer.config => object
// @NOTE: for use to do any additional tasks here
// ...setup models/datastore, assign globals, etc.
// be sure to invoke the callback or the server won't start
startServer()
})
Test your api at
localhost:3003/[controllerFileName]/[methodName]
Create this directory structure:
- api/
- controllers/
- myController.js
- services/
- utilService.js
- policies/
- isLoggedIn.js
- models/
- user.js
- controllers/
- config/
- connections.js
- controllers.js
- globals.js
- logger.js
- middleware.js
- models.js
- policies.js
- routes.js
- server.js
- services.js
- session.js
- socket.js
- app.js
Yeah, yeah, it's strongly opinionated... If you think it's a good idea to add the feature to have the ability to change the directory structure, open an issue on github.
The api directory has sub-directories:
- api/controllers
- api/policies
- api/services {optional}
- api/models {optional}
The files in each of these directories is read into module form via commonjs and
available on the api
param passed in the callback.
This is where the "magic" (yeah right) happens. You get API routes based on the
.js
files you put in the api/controllers
dir!
api/controllers/
myController.js // localhost:3003/myController/..[controller methods routes]
anotherRoute.js // localhost:3003/anotherRoute/..[controller methods routes]
All .js
files in the api/controllers/
dir will automatically create routes
from the file name and appending each method in the file's module.exports
object
after that on the route/path.
api/controllers/myController.js
module.exports = {
index: (req, res, next) => {
return res.send('this is the index route, localhost:3003/myController/')
},
randomRoute: (req, res, next) => {
return res.send('this is a random route, localhost:3003/myController/randomRoute')
}
}
Any controller method defined in the
config/routes.js
that have policy(s) assigned to it - will NOT be automatically mounted on the express router
All .js
files in the api/policies
dir will be read into the api
object and
be available to be used in config/routes.js
(see config/routes.js below). A
policy module should export a middleware method.
api/policies/isLoggedIn.js
module.exports = (req, res, next) => {
if (req.cookies.isLoggedIn) {
return next()
} else {
return res.status(403).end()
}
}
All .js
files in the api/services
dir will be read into the api
object as
modules by file name. You can put whatever exports you like in service modules.
A file api/services/utilityService.js
will be available on the api
object as
api.services.utilityService
.
You can make your service modules available globally via
config/services.js
see config below. This is great for making them available in your controllers for utility/helper, etc. operations.
All .js
files in the api/models
dir will be read into the api
object as
modules by file name.
A file api/models/userModel.js
will be available on the api
object as
api.models.userModel
.
You can make your models available globally via
config/models.js
see config below.
You can implement any sort of logic here you would like to be used for datastore operations or database queries, etc.. If you're interested in a quick and extendible solution, checkout our project
js-data-api-server
that has a full datastore setup ready to go
All .js
files in the config directory are loaded as modules and available on
the config
object in the callback. See below for config files use and options
Config files are used to over-ride default node-api-server
configuration and also
provide helper/util meta info for your API. There are no required config files to
use node-api-server
and you can start and run your api server without this directory.
Each config file in the config/
dir will be available on the config
object
by filename, ie: config/connections.js
will be available as config.connections
.
NOTE:
config/
dir modules are loaded into memory first. This means it's available on the global scope in your controllers, policies, services, models, etc..
This module is NOT required. If you do chose to use it, it can be great for defining this like your database connections or even different connections per env.
Example config/connections.js
module.exports = {
mongoDB: {
host: (process.env.NODE_ENV === 'production') ? '198.xx.22.x' : 'localhost',
port: 27017,
user: 'username',
pass: 'password',
database: 'myDB'
}
}
This file is used to over-ride default configuration.
module.exports = {
// global string controller modules will be available as
global: 'Controllers'
}
// @TODO
This is the global logger used (Winston.js) and options here over-ride defaults.
Winston is the logger used in
node-api-server
// @TODO add option to write to a file/dir
// @TODO add option to use specified transport
module.exports = {
// if set to string, will set global[string] = logger
global: 'Log',
// defaults to info
level: 'silly'
}
Define any middleware you would like to use on the express api server.
The default is
body-parser
, if you define any new middleware it will overwritebody-parser
so you must be sure to include it if you plan on parsing form/query data in your api
config/middleware.js
must export an array if you're defining middleware. Each middleware will be used in the order listed in the exports array.
const bodyParser = require('body-parser')
// example custom middleware method
const customLogger = (req, res, next) => {
console.log(`${req.method}:: ${req.path}`)
next()
}
module.exports = [
bodyParser.urlencoded({ extended: true }),
bodyParser.json(),
customLogger
]
This file is used to over-ride default configuration.
module.exports = {
// global string model modules will be available as
global: 'Models'
}
This file is used to over-ride default configuration.
module.exports = {
// global string policy modules will be available as
global: 'Policies'
}
Some more awesome route "magic" (yeah right)! Define custom routes and controller methods to use.
Every key will be a route with the assigned controller
.method
and any policies
applied as middleware before reaching the controller
.method
.
"*
" key is only used if you want to apply middleware to ALL routes on your
api server.
Key definitions take the following:
Object
:
{
controller: [api/controllers/filename],
method: [method name in controller],
policies: {String|Array}
}
String
: Policy name (filename in api/policies dir)
Array
: Policy names
Example:
module.exports = {
// Example: will apply to ALL routes
'*': ['isLoggedIn', 'isAdmin'],
// define a route "localhost:3003/auth"
'/auth': {
controller: 'auth', // the controller filename in api/controllers/ dir
method: 'checkAuthentication', // the name of the method in the controller file
policies: 'isLoggedIn' // policies to run on route before controller.method
},
// policy string for this route
'/auth/isAdmin': 'isAdmin',
// policy string for this route with a wildcard
// policy will be applied to all routes starting with "/auth/"
'/auth/*': 'isLoggedIn',
// multiple policy array for this route
'/auth/both': ['isAdmin', 'isLoggedIn']
}```
## Config `config/server.js` (optional)
This file is used to over-ride default server configuration.
```js
const express = require('express')
module.exports = {
// defaults to '/'
baseRoute: '/api',
// defaults to express()
app: express(),
// defaults to 3003
port: 3003 // also cli arg option: -p XXXX
}
This file is used to over-ride default configuration.
module.exports = {
// global string policy modules will be available as
global: 'Services'
}
// @TODO
// @TODO
// @FEATURE/TODO make directory reading recursive for sub dirs // @TODO add readme.md for each directory and the optional configs that can be used // @TODO add example files and example repo to pull and get started with // @TODO setup sockets for routes