Crudella is a service factory. It creates your service module based on the minimal required configuration you provide. Assume you have an entity repository (or any other CRUD interface for resource) and to make it work, you cannot simply bind it to routes, skipping through controller and service layer. Here are several random problems you face in the mentioned layers:
Crudella implements logic equivalent of the controller and the service layer. It goes as far as possible to help you with menial repeated code, but hopefully not as much as to completely take over your application and magically orchestrate behind your back.
Crudella helps you in various ways:
Install via npm:
npm i crudella
Crudella is tested on several major Node.js versions starting at 6.
// dalmatianService.ts
import { createService } from 'crudella';
const dalmatianService = createService<DalmatianAttributes>({
// each method receives relevant CRUD context
detail: ctx => dalmatianRepository.find(ctx.id, ctx.options),
create: ctx => dalmatianRepository.create(ctx.data, ctx.options),
update: ctx => dalmatianRepository.updateById(ctx.entity.id, ctx.data, ctx.options),
delete: ctx => dalmatianRepository.deleteById(ctx.entity.id),
list: ctx => dalmatianRepository.list(ctx.filters, ctx.options),
});
export const createPupperMiddleware = dalmatianService.createMiddleware;
Based on that Crudella provides an express middleware you can use without further hassle:
// routes.ts
import { createPupperMiddleware } from 'dalmatianService';
const router = createRouter();
router.use(createPupperMiddleware('/api/puppies'))
That saved us some time. We don't have to bind the routes ourselves. More importantly though, we can control the flow using rich contextual data. See authorization.
:information_source: Not using express? You can still use Crudella. See generating handlers.
:information_source: This definition still feels too long and you have consistent repository API? See configuring crudella using repository.
Crudella allows you to decouple pure data manipulation (persisting puppies) from request verification.
Use authorize
option with rich contextual data to reject access due to validation / authorization and do not pollute logic of manipulating objects with unrelated code.
import { Access, createService } from 'crudella';
const dalmatianService = createService<DalmatianAttributes>({
// ...all crud methods
authorize: async ctx => {
// reject anonymous users for all actions (detail, create, update, delete, list)
// ctx.context is type of C, which you can set for your application
if (!ctx.context.user) {
throw new Error('Anonymous cannot see, delete, update or create dalmatians')
}
// reject for selected operations
if (ctx.type === Access.LIST && 'dots' in filters) {
throw new Error('Is rude to filter using dots!')
}
if (ctx.type === Access.CREATE && !isValidPuppy(ctx.data)) {
throw new Error('Your puppy is invalid, sorry.')
}
// ctx contains all you need for validation and authorization
if (ctx.type === Access.UPDATE) {
if (!age in ctx.bareData) { // only data sent by client
throw new Error('Age missing on update')
}
if (!isValidPuppy(ctx.data)) { // new data defaulted to existing entity
throw new Error('Validation failed')
}
}
},
});
For full reference of the contexts, see API docs.
Run npm run build
to compile Typescript into JavaScript.
Project uses Jest testing framework and its snapshot testing.
Run npm run test
to test or npm run test:coverage
to collect coverage.
Travis CI tests PRs, Coveralls collect coverage.
TS lint and prettier
npm run lint
To generate API docs using TypeDoc and preview locally, run npm run docs
.
Output is an ignored docs
folder.
Current API documentation is deployed by Travis via GitHub Pages :octocat:
This project is licensed under MIT.
Generated using TypeDoc