The adapter interface specification is currently under active development and may change.
e.g.
RestAPI
orMySQL
Stability: 3 - Stable
Implementing the basic semantic interface (CRUD) is really a step towards a complete implementation of the Queryable interface, but with some services/datasources, about as far as you'll be able to get using native methods.
By supporting the Semantic interface, you also get the following:
- if you write a
find()
function, developers can also use all of its synonyms, including dynamic finders andfindOne()
. When they're called, they'll automatically be converted into the appropriate criteria object for the basicfind()
definition in your adapter. - as long as you implement basic
where
functionality (seeQueryable
below), Waterline can derive a simplistic version of associations support for you. To optimize the default assumptions with native methods, override the appropriate methods in your adapter.
All officially supported Sails.js database adapters implement the
Semantic
interface.
Model.create()
Model.find()
Model.findOne()
Model.update()
Model.destroy()
henry.save()
Stability: 3 - Stable
Query building features are common in traditional ORMs, but not at all a guarantee when working with Waterline. Since Waterline adapters can support services as varied as Twitter, SMTP, and Skype, traditional assumptions around structured data don't always apply.
If query modifiers are enabled, the adapter must support Model.find()
, as well as the complete query interface, or, where it is impossible to do so, at least provide helper notices. If coverage of the interace is unfinished, it's still not a bad idea to make the adapter available, but it's important to clearly state the unifinished parts, and consequent limitations, up front. This helps prevent the creation of off-topic issues in Sails/Waterline core, protects developers from unexpected consequences, and perhaps most importantly, helps focus contributors on high-value tasks.
All officially supported Sails.js database adapters implement this interface.
Query modifiers include filters:
where
limit
skip
sort
select
distinct
Boolean logic:
and
or
not
As well as groupBy
and the aggregators:
count
sum
min
max
average
IN
queries:
Adapters which implement where
should recognize a list of values (e.g. name: ['Gandalf', 'Merlin']
) as an IN
query. In other words, if name
is either of those values, a match occured.
Sub-attribute modifiers:
You are also responsible for sub-attribute modifiers, (e.g. { age: { '>=' : 65 } }
) with the notable exception of contains
, startsWith
, and endsWith
, since support for those modifiers can be derived programatically by leveraging your definition of like
.
like
(SQL-style, with % wildcards)'>'
(you can also opt to use the more verbose.greaterThan()
, etc.)'<'
'>='
'<='
- TODO: range queries (e.g.
{ '<':4, >= 2 }
)
Stability: 2 - Unstable
Adapters which implement the Migratable interface are usually interacting with SQL databases. This interface enables the migrate
configuration option on a per-model or adapter-global basis, as well as access to the prototypal/class-level CRUD operations for working with tables.
Adaper.define()
Adaper.describe()
Adaper.alter()
Adaper.drop()
"alter"
(default)"drop"
"safe"
Stability: 1 - Experimental
Communicating with another server via messages/packets is the gold standard of performance-- network latency is the slowest I/O operation computers deal with, yet ironically, the standard methodology used by most developers/frameworks/libraries outside of Node.js is detrimental to performance.
In the Node community, you might say we're in the midst of a bit of an I/O renaissance.
The standard approach to communicating with another server (or a disk) involves loading a message into memory from the source, and then sending the entire object to the destination at once.
This is like trying to transport a heavy bag of gold over a river by wading across with it on your back. Even if you're very strong, with enough gold, you will drown. This is analogous to your server running out of RAM as it buffers data in memory, and the resulting scalability problem.
Using Node streams is a different ball game. It's like splitting up the big bag into smaller containers, then floating them across one by one. This way, no matter how much gold you end up with, you never drown.
A huge advantage of using Node.js is the ease with which you can parse and manipulate streams of data. Instead of pulling an entire dataset into RAM, you can inspect it a little at a time. This unlocks a level of performance that is unachievable using conventional approaches.
The most common use case is taking advantage of the available HTTP response stream to pipe the output byte stream from the database directly back to the user. i.e. to generate a dynamic sitemap, you might need to respond with a huge set of data (far too large to fit in memory on a commodity server) and simultaneously transform it into XML.
Implementing the Streaming CRUD interface is actually pretty simple-- you just need to get comfortable with Node.js streams. You can mutate streams as they come in-- you just need to find or design a mapping function designed for streams, where you don't have all the data at once.
Stability: 1 - Experimental
e.g. sails-local-fs
, sails-s3
Implementing the Blob interface allows you to upload and download binary data (aka files) to the service/database. These "blobs" might be MP3 music files (~5MB) but they could also be data-center backups (~50TB). Because of this, it's crucial that adapters which implement this interface use streams for uploads (incoming, into data source from Sails) and downloads (outgoing, from data source to Sails).
write()
read()
Stability: 1 - Experimental
Adapters which implement one-way messages should do so using send()
or a suffixed send*()
method. This lets developers know that it's not safe to assume that these operations are reversible. An example of one such adapter is SMTP, for sending email, or APNS for sending Apple push notifications.
send()
Stability: 1 - Experimental
Adapters implementing the pubsub interface report changes from the service/database back up to the app.
They should emit an event on the sails
object.
Examples:
- Twitter streaming API (see new tweets as they come in)
- IRC (see new chats as they come in)
- Stock prices (visualize the latest market data as soon as it is available)
- Hardware scanners (see new data as it comes in)