Core high-level methods for interacting with DADI API
DADI API is a high performance RESTful API layer designed in support of API-first development and the principle of COPE.
This package exposes a set of chainable methods that allow developers to compose complex read and write operations using a simplistic and natural syntax, providing a high-level abstraction of the REST architectural style.
It can be used as a standalone module to generate request objects containing information about the HTTP verb, URL and payload, or used in conjunction with DADI API Wrapper to provide a full-featured API consumer, capable of handling authentication and the HTTP communication.
-
Install the
@dadi/api-wrapper-core
module:npm install @dadi/api-wrapper-core --save
-
Add the library and configure the API settings:
const APIWrapper = require('@dadi/api-wrapper-core') const api = new APIWrapper({ uri: 'http://api.example.com', port: 80, version: 'vjoin', database: 'testdb' })
-
Make a query:
// Example: getting all documents where `name` contains "john" and age is greater than 18 const requestObject = api .in('users') .whereFieldContains('name', 'john') .whereFieldIsGreaterThan('age', 18) .find()
Each query consists of a series of chained methods to form the request, always containing a terminator method. Terminators return a Promise with the result of one or more requests to the database and can make use of a series of filtering methods to create the desired subset of documents to operate on.
Creates a document.
// Example
api.in('users')
.create({
name: 'John Doe',
age: 45,
address: '123 Fake St'
})
.then(function (doc) {
console.log('New document:', doc)
})
.catch(function (err) {
console.log('! Error:', err)
})
Output:
{
"body": {
"name": "John Doe",
"age": 45,
"address": "123 Fake St"
},
"method": "POST",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users",
"port": "80",
"protocol": "http:"
}
}
Deletes one or more documents.
api.in('users')
.whereFieldDoesNotExist('name')
.delete()
Output:
{
"body": {
"query": {
"name": {
"$eq": null
}
}
},
"method": "DELETE",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users",
"port": "80",
"protocol": "http:"
}
}
Returns a list of documents.
api.in('users')
.whereFieldIsGreaterThan('age', 21)
.useFields(['name', 'age'])
.find()
Output:
{
"method": "GET",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users?filter={\"age\":{\"$gt\":21}}&fields={\"name\":1,\"age\":1}",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users?filter={\"age\":{\"$gt\":21}}&fields={\"name\":1,\"age\":1}",
"port": "80",
"protocol": "http:"
}
}
Gets the list of collections for the API.
api.getCollections()
Output:
{
"method": "GET",
"uri": {
"href": "http://api.example.com:80/api/collections",
"hostname": "api.example.com",
"path": "/api/collections",
"port": "80",
"protocol": "http:"
}
}
Gets the config for a collection or for the API.
// Gets the collection config
api.in('users')
.getConfig()
Output:
{
"method": "GET",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users/config",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users/config",
"port": "80",
"protocol": "http:"
}
}
// Gets the API config
api.getConfig()
Output:
{
"method": "GET",
"uri": {
"href": "http://api.example.com:80/api/config",
"hostname": "api.example.com",
"path": "/api/config",
"port": "80",
"protocol": "http:"
}
}
Gets a signed URL from a media collection.
api.in('images')
.getSignedUrl({
fileName: "foobar.jpg"
})
Output:
{
"body": {
"fileName": "foobar.jpg"
},
"method": "POST",
"uri": {
"href": "http://api.example.com:80/media/images/sign",
"hostname": "api.example.com",
"path": "/media/images/sign",
"port": "80",
"protocol": "http:"
}
}
Gets collection stats.
api.in('users')
.getStats()
Output:
{
"method": "GET",
"uri": {
"href": "http://api.example.com:80/api/stats",
"hostname": "api.example.com",
"path": "/api/stats",
"port": "80",
"protocol": "http:"
}
}
Gets the the API status.
api.getStatus()
Output:
{
"method": "POST",
"uri": {
"href": "http://api.example.com:80/api/status",
"hostname": "api.example.com",
"path": "/api/status",
"port": "80",
"protocol": "http:"
}
}
Sets the config for a collection or for the API.
// Sets the collection config
var collectionConfig = {cache: false}
api.in('users')
.setConfig(collectionConfig)
Output:
{
"body": {
"cache": false
},
"method": "POST",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users/config",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users/config",
"port": "80",
"protocol": "http:"
}
}
// Sets the API config
var apiConfig = {cors: true}
api.setConfig(apiConfig)
Output:
{
"body": {
"cors": true
},
"method": "POST",
"uri": {
"href": "http://api.example.com:80/api/config",
"hostname": "api.example.com",
"path": "/api/config",
"port": "80",
"protocol": "http:"
}
}
Updates a list of documents.
api.in('users')
.whereFieldIsLessThan('age', 18)
.update({
adult: false
})
Output:
{
"body": {
"query": {
"age": {
"$lt": 18
}
},
"update": {
"adult": false
}
},
"method": "PUT",
"uri": {
"href": "http://api.example.com:80/vjoin/testdb/users",
"hostname": "api.example.com",
"path": "/vjoin/testdb/users",
"port": "80",
"protocol": "http:"
}
}
Filtering methods are used to create a subset of documents that will be affected by subsequent operation terminators.
Defines the page of documents to be used.
// Example
api.goToPage(3)
Defines a maximum number of documents to be retrieved.
// Example
api.limitTo(10)
Selects a field to sort on and the sort direction. Order defaults to ascending (asc
).
// Example
api.sortBy('age', 'desc')
Selects the fields to be returned in the response. Accepts array format.
// Example
api.useFields(['name', 'age'])
Filters documents using a MongoDB query object or a Aggregation Pipeline array. The methods above are ultimately just syntatic sugar for where()
. This method can be used for complex queries that require operations not implemented by any other method.
// Example
api.where({name: 'John Doe'})
Filters documents where field
begins with text
.
// Example
api.whereFieldBeginsWith('name', 'john')
Filters documents where field
contains text
.
// Example
api.whereFieldContains('name', 'john')
Filters documents field
does not contain text
.
// Example
api.whereFieldDoesNotContain('name', 'john')
Filters documents where field starts with text
.
// Example
api.whereFieldEndsWith('name', 'john')
Filters documents that contain a field.
// Example
api.whereFieldExists('name')
Filters documents that do not contain a field.
// Example
api.whereFieldDoesNotExist('address')
Filters documents where field
is equal to value
.
// Example
api.whereFieldIsEqualTo('age', 53)
Filters documents where field
is greater than value
.
// Example
api.whereFieldIsGreaterThan('age', 18)
Filters documents where field
is greater than or equal to value
.
// Example
api.whereFieldIsGreaterThanOrEqualTo('age', 19)
Filters documents where field
is less than value
.
// Example
api.whereFieldIsLessThan('age', 65)
Filters documents where field
is less than or equal to value
.
// Example
api.whereFieldIsLessThanOrEqualTo('age', 64)
Filters documents where the value of field
is one of the elements of matches
.
// Example
api.whereFieldIsOneOf('name', ['John', 'Jack', 'Peter'])
Filters documents where field
is not equal to value
.
// Example
api.whereFieldIsEqualTo('age', 53)
Filters documents where the value of field
is not one of the elements of matches
.
// Example
api.whereFieldIsNotOneOf('name', ['Mark', 'Nathan', 'David'])
Selects the hook with a given name.
// Example
api.whereFieldIsNotOneOf('name', ['Mark', 'Nathan', 'David'])
Defines whether nested documents should be resolved using composition. The default is to let API decide based on the queried collection's settings.
// Example
api.withComposition()
api.withComposition(true) // same as above
api.withComposition(false)
Selects a custom endpoint to use. Please note that unlike collections, custom endpoints do not have a standardised syntax, so it is up to the authors to make sure the endpoint complies with standard DADI API formats, or they will not function as expected.
// Example
api.fromEndpoint('custom-endpoint')
Selects the collection to use.
// Example
api.in('users')
Selects "hook mode", meaning filters and terminators will operate on hooks and not on documents.
// Example
api.inMedia('images')
Selects a media bucket to be used.
// Example
api.inMedia('images')
Selects the database to use. Overrides any database defined in the initialisation options, and is reset when called without arguments.
// Example
api.useDatabase('testdb')
Selects the version to use. Overrides any version defined in the initialisation options, and is reset when called without arguments.
// Example
api.useVersion('1.0')
By default, calling a terminator will return a request object, which is a plain JavaScript object formed of method
, uri
and, optionally, body
. Alternatively, you can choose to specify what exactly terminators will return, by defining a callback that will be executed before they return. This callback will receive the request object as an argument, giving you the option to modify it or wrap it with something else.
A callback is defined by setting a callback
property on the options object used to initialise API wrapper.
const APIWrapper = require('@dadi/api-wrapper-core')
const api = new APIWrapper({
uri: 'http://api.example.com',
port: 80,
version: 'vjoin',
database: 'testdb',
callback: function (requestObject) {
// This callback will return a JSON-stringified version
// of the request object.
return JSON.stringify(requestObject)
}
})
With debug mode, you'll be able to see exactly how the requests made to API look like. This functionality is enabled by setting a debug
property in the config:
const APIWrapper = require('@dadi/api-wrapper-core')
const api = new APIWrapper({
uri: 'http://api.example.com',
port: 80,
version: 'vjoin',
database: 'testdb',
debug: true
})