@ehbraheem/api
A CRUD microservice module.
A CRUD microservice module exposing key features of a RESTful API as injected dependencies.
Installation
$ yarn add @ehbraheem/api
bootstrapping
To create a new CRUD service API, simply bootstrap the application by injecting the dependencies as depicted in the example below.
import server, { mongooseConnect as dbConnect, Api } from '@ehbraheem/api';
import config from './config';
import { schema } from './persistence/mongoose';
import routes from './routes';
import services from './services';
const application = (): Promise<Api> =>
server({
routes,
services,
schema,
config,
dbConnect,
});
Then start the app as shown in the example below
(async (): Promise<void> => {
const app = await application();
const PORT = config.get('server.port');
app.listen(PORT, config.get('server.hostname'), (): void => {
// eslint-disable-next-line no-console
console.log(`Server running on port ${config.get('server.hostname')}:${PORT}`);
});
})();
setting up environment variables
node-convict and dotenv are both used to manage application configuration. It is a requirement to create a file named .env
at root of project and setup as follows:
# Application
PORT=4015
TLS_CERT=/path/to/server.crt
TLS_KEY=/path/to/server..key
# OR... Mongo Database
MONGO_DB=mydb
You can console log process.env
to find out available environment variables. You can also inspect the imported config
object from @ehbraheem/api
.
APIs
Details of each of the exposed APIs will now be explained.
database connectors
import { mongooseConnect, server } from '@ehbraheem/api';
server({
...
dbConnect: mongooseConnect, // DB connection helper
...
});
Simply import mongooseConnect
(uses mongoose) to connect to mongo database.
schema
A schema will need to be implemented to query and manipulate the database.
import schema from 'path/to/my/models';
server({
...
schema,
...
});
The exported models are each expected to receive the db
connector (client) as an argument, for example...
import { Dict, DbClient } from '@ehbraheem/api';
import users from './User/queries';
import books from './Book/queries';
export default (client: DbClient): Dict => ({
users: users(client),
books: books(client),
});
routes
Express is the core building block of this module. All routing and handling of client requests are managed by handlers defined.
import routes from 'path/to/my/routes';
server({
...
routes,
...
});
Firstly, export all handlers of client requests, for example...
import createUser, { destroyUser } from './users/routes';
import createBook from './books/routes';
import { Router } from 'express';
import usersRoutes, { MOUNT_POINT as users } from './users/routes';
import booksRoutes, { MOUNT_POINT as books } from './users/routes';
import { ApiRouter, RouterArgs } from '@ehbraheem/api';
export default ({ services, validator, json, config }: RouterArgs): ApiRouter => ({
[users]: usersRoutes({ router: Router(), services, config, validator, json }),
[books]: booksRoutes({ router: Router(), services, config, validator, json }),
});
Each route handler is an higher order function that will receive in its arguments... This is an example routes file.
import { createUser, ROUTE_NAME } from './controllers';
import { Router } from 'express';
import { RouteArgs } from '@ehbraheem/api';
export const MOUNT_POINT = `/${ROUTE_NAME}`;
export default ({
router,
services, // Service response formatter
validator, // joi validator
config, // Application configuration
json, // used by services to create JSON string format
}: RouteArgs): Router => {
router
.route('/')
.post(createUser({ services, json, validator }));
return router;
};
Each route should have an equivalent controller function like the below.
import { Request, Response, NextFunction } from 'express';
export const createUser = ({
services,
json,
validator,
}): ((req: Request, res: Response, next: NextFunction) => Promise<void>) => async (
req: Request,
res: Response,
next: NextFunction
) => {
...
};
Find out more about the passed in features:
- Application configuration using convict
- Object schema validation with joi
- A JSON string formatted resource object creator using fast-json-stringify
plugins
Prior to routes, Express middlewares can be loaded. In the following example the cookie-session plugin module is configured as follows:
import cookieSession from 'cookie-session';
server({
...
middlewares: [cookieSession],
...
});
services
services
is the layer between the router
and the schema
layers. It's main responsibility is to pass on the payload
from the router
to the schema
. It also creates the JSON string formatted resource response payload coming from the schema
layer.
Firstly define the services; For example...
import { Dict } from '@ehbraheem/api';
import users from './users/services';
import books from './books/services';
export default (db: Dict): Dict => ({
users: users(db),
books: books(db),
...
});
Each service
handler will receive in its arguments (passed down from router
)...
export const create = async ({
db, // db connector
payload, // request payload
config, // app config
payload
}) => {
...
};