// create a base route
route('api/v1/test', function(method) {
// bind a "GET" method to the base
method.get(function(req, res) {
// req and res are from Express
// return plain values
return 'hello world';
});
// interceptors
let alwaysDo = function(req, res) {
// treat this as Express middleware
// must return a promise
return Promse.resolve(true);
};
// intercept every route in this scope
method.beforeEach(alwaysDo);
// interceptor can be a generator and will execute in Fritz.run
let requireLogin = function*() {
return true;
};
// intercept only the next route
method(requireLogin);
// bind a "POST" method to the base
// myroute will be appended to the route route from this scope
// e.g. "/api/v1/test/myroute"
method.post('myroute', function*(req, res) {
// can be a generator and will execute in Fritz.run
// return JSON type
return {
test: 'result'
};
});
// multiple methods on the same route
method.put('myroute', function(req, res) {
// return type Promise - response will extract the resolved value
return Promise.resolve('resolved value');
});
// use Express route parameters
method.delete('myroute/:id', function(req, res) {
// return type Promise - response will extract the resolved value
return Promise.resolve('resolved value');
});
// All of the following are valid methods
method.all
method.checkout
method.connect
method.copy
method.delete
method.get
method.head
method.lock
method.merge
method.mkactivity
method.mkcol
method.move
method['m-search']
method.notify
method.options
method.patch
method.post
method.propfind
method.proppatch
method.purge
method.put
method.report
method.search
method.subscribe
method.trace
method.unlock
method.unsubscribe
});
Interceptors function the same as Express middleware.
- i.e. arguments will be (request, response)
- must be a function
- They will be executed before the folowing route(s)
- They are expected to return a promise
- Rejection will result in an error response
- Resolve will result in continuing the request chain
let MyModel = class extends model({
// This will be your table name in PostgreSQL
table: 'MyModel',
// a list of properties that an instance of
// MyModel will have. Each property key will
// need to have a value that declares how to
// validate the property
properties: {
// any key from the valid object can be used
name: model.valid.name, // 2 characters
email: model.valid.email, // email addresses
pass: model.valid.password, // 8 characters
child: model.valid.id, // validate shortids
description: model.valid.nonempty, // 1 character
achievements: model.valid.nullable, // can be empty
// custom validation objects can also be used
custom: {
test: function(value) {
// a custom function for this property
return true;
},
message: 'a string to display on error'
}
},
// Protect is an array of property names to exclude
// when calling the safe() method on an instance
// of MyModel
protect: [
'pass' // the property "pass" will not be output
],
// Queries are a list of string queries
// that will be turned into prepared statements
// access them with MyModel.{myQueryName}
// e.g. in this example: MyModel.getAll();
queries: {
getAll: `
select ${model.JSONB}
from MyModel;
`,
// query argument is inserted at $1
// arguments are inserted in the order of the array
//
getByEmail: `
select ${model.JSONB}
from MyModel
where data->>'email' = $1;
`
}
}) {
// If you extend the returned class you can set
// the constructor, instance methods, and static methods
constructor(obj) {
super(obj);
}
};
MyModel.validate({
name: 'Rick Sanchez',
email: '[email protected]',
pass: 'supersecret',
child: morty._id,
description: 'The Rickest Rick'
}).then(instance => {
// this is a valid instance
return instance.create();
// call safe() to strip protected properties
}).then(created => created.safe());
// in the queries object create a query called "prepared"
let MyModel = model({
...
queries: {
prepared: `
select ${model.JSONB}
from MyModel
where data->>'email' = $1;
`
}
...
});
// now prepared is available as a function.
let promise = MyModel.prepared(['value1', 'value2']);
// value1 will be inserted into the query at position "$1"
All ORM helpers return a Promise with special functions
// of() transforms the result objects into MyModel type
let ofModel = MyModel.prepared().of();
// of(T) transforms the result objects into type T
let ofType = MyModel.prepared().of(MyOtherModel);
// unique(err) transforms the result to just the first result
// and will reject with err if there is more than one
let uniqueDefault = MyModel.prepared().unique(); // default error
let uniqueNoError = MyModel.prepared().unique(null); // do not error, resolve null
let uniqueCustom = MyModel.prepared().unique(apiError.conflict()); // custom error
// unique() Can chain with of()
let uniqueOf = MyModel.prepared().unique().of();
// required(err) will reject with err if there is no result
let manyDefault = MyModel.prepared().required(); // default error
let many = MyModel.prepared().required(apiError.noContent()); // custom error
// Can chain with of()
let manyOf = MyModel.prepared().required().of();
// required() Can chain with unique()
let requiredUnique = MyModel.prepared().required().unique();
let requiredUniqueOf = MyModel.prepared().required().unique().of();
MyModel.getByEmail(['[email protected]'])
.required()
.unique()
.of()
.then(result => {
// result will be unique and present
// result is also of type MyModel
return result.update({
name: 'The Rickest Rick'
}).then(result => result.safe());
}).catch(err => {
// result was non-unique or not present
});
All apiError
functions return a rejected Promise
with an value of type ApiError
. These promises will
have a property called throwable
that returns the
ApiError. If these promises are returned to a Fritz
route, they will result in a http response with the
appropriate error code and will be formatted by the
function at apiError.handler
.
// custom message and status code
apiError('a message to reject with', 200);
// Bad Request (400)
apiError(); // default message
apiError('a message to reject with'); // custom message
// respond with Internal Server Error (204)
apiError.fatal();
apiError.fatal('a message to log');
apiError(new Error());
// All `apiError` functions accept a single parameter for the message
apiError.badRequest('a message to reject with');
// Additional `apiError` error functions
apiError.noContent(); // No Content (204)
apiError.badRequest(); // Bad Request (400)
apiError.unauthorized(); // Unauthorized (401)
apiError.paymentRequired(); // Payment Required (402)
apiError.forbidden(); // Forbidden (403)
apiError.notFound(); // Not Found (404)
apiError.methodNotAllowed(); // Method Not Allowed (405)
apiError.conflict(); // Conflict (409)
apiError.unsupportedMediaType(); // Unsupported Media Type (415)
apiError.serverError(); // Internal Server Error (500)
Run is heavily based on Co
This allows you to use the yield
keyword to extract results from promises.
let generated = run(function*(arg1) { // notice the "*" in function
// operate on promises in an imperative syntax
// execution is asynchronously halted until the promise is resolved
let result = yield Promise.resolve(arg1);
return result;
});
// run wrapped generators return a promise
// Any rejected promise will reject the resulting promise
generated('Snowball').then(result => console.log(result));
Returns: Promise
- a promise resolved with a returned result
Param | Type | Description |
---|---|---|
runnable | Generator | Runnable |
The generator function to run |
thisArg | Object |
The this instance to bind the function to |
Wrapper for Winston
log('a message to log');
log.info('an info message to log');
log.warn('an warn message to log');
log.error('an error message to log');
// set loggers for Winston
log.setLogger([
{
type: 'Console',
colorize: true,
level: 'debug'
}
])
$ npm run setup
This installs the following globally:
Then install the required Node dependencies with:
$ npm install
Add a file called .env
to the root of the project with the following contents:
{
"node": {
"env": "dev"
}
}
You can now run the development server by running the following commands:
$ npm start
- You can now visit http://localhost:8081/ to view changes live.
- Uses dependency injection from Nodep
$ gulp test
$ gulp autotest
$ gulp docs
$ gulp changelog
- jshint is part of the test suite and should be kept clean
- Commits should have high test coverage
- Docs should be kept up to date
- Additions should come with documentation
- commit messages should follow Angular conventional format
Kind: inner class of model
- ~Model
- new Model()
- instance
- .create() ⇒
Promise
- .update(obj) ⇒
Promise
- .delete() ⇒
Promise
- .safe(override) ⇒
Object
- .create() ⇒
- static
- .createQuery
- .readQuery
- .updateQuery
- .deleteQuery
- .create(obj) ⇒
Promise
- .read(id) ⇒
Promise
- .delete(id) ⇒
Promise
- .validate(obj) ⇒
Promise
The generated Model class output
Create an object in the database from this instance
Kind: instance method of Model
Returns: Promise
- result will contain this object
Update an existing object in the database
Kind: instance method of Model
Returns: Promise
- result will be an instance with updated properties
Param | Type | Description |
---|---|---|
obj | Object |
an object with properties to update in this instance |
Delete this object in the database
Kind: instance method of Model
Generate an object that does not have protected properties
Kind: instance method of Model
Returns: Object
- a consumable object of this instance
Param | Type | Description |
---|---|---|
override | String | Array.<String> |
parameters to allow that are marked protected |
A prepared statement to create this model in the database
Kind: static property of Model
See: db.prepare
A prepared statement to read this model from the database
Kind: static property of Model
See: db.prepare
A prepared statement to update this model in the database
Kind: static property of Model
See: db.prepare
A prepared statement to delete this model from the database
Kind: static property of Model
See: db.prepare
Create an object in the database from a validated instance
Kind: static method of Model
Returns: Promise
- result will contain the validated object
Param | Type | Description |
---|---|---|
obj | Object |
the object to validate and insert into the database |
Read an object with the id from the database
Kind: static method of Model
Returns: Promise
- an object referred to by id
Param | Type | Description |
---|---|---|
id | String |
the id of the object to find |
Delete an object in the database with id
Kind: static method of Model
Param | Type | Description |
---|---|---|
id | String |
the id of the object to delete |
Validate an object against property rules
Kind: static method of Model
Returns: Promise
- the result will be a validated instance of this Model
Param | Type | Description |
---|---|---|
obj | Object |
the object to create |
A table of default validation rules
Kind: inner constant of model
- ~valid :
Object
Validation type requiring a valid shortid
Kind: static constant of valid
Example
let validator = model.valid.id;
Validation type requiring at least single character
Kind: static constant of valid
Example
let validator = model.valid.nonempty;
Validation type requiring at least two characters
Kind: static constant of valid
Example
let validator = model.valid.name;
Validation type requiring a valid email address
Kind: static constant of valid
Example
let validator = model.valid.email;
Validation type requiring at least 8 characters
Kind: static constant of valid
Example
let validator = model.valid.password;
Validation type requiring nothing at all
Kind: static constant of valid
Example
let validator = model.valid.nullable;
Generate a model class
Kind: inner method of model
Returns: Model
- a generated Model class
Param | Type | Description |
---|---|---|
descriptor | Object |
Configuration for the model class to generate |
- ~model(descriptor) ⇒
Model
- .shortid :
Object
- .JSONB :
String
- .createModelTable() ⇒
Object
- .shortid :
A reference to shortid the engine used to generate ids
Kind: static constant of model
Helper text to select JSONB data with an embedded _id
primary key
Kind: static constant of model
Example
let query = `select ${model.JSONB} from MyTable`;
A reference to shortid the engine used to generate ids
Kind: static method of model
Copyright (c) 2015 Beg.in
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.