-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
284 lines (233 loc) · 7.55 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/*jshint laxcomma: true, smarttabs: true*/
'use strict';
/**
### Create a simple Api
```js
var tastypie = require('tastypie')
var Api = tastypie.Api
var hapi = require('hapi')
var server = new hapi.server
var v1 = new Api('api/v1' )
var Resource = tastypie.Resource.extend({
fields:{
lastName:{ type:'char', attribute:'name.first' },
fisrtName:{type:'char', attribute: 'name.last'}
}
})
v1.use('test', new Resource() );
server.connection({port:2000})
server.register( v1, function( ){
server.start(function(){
console.log('server listening localhost:2000')
});
})
```
##### Get Data
```js
// GET /api/v1/test
{
"meta":{
"count":1,
"limit":25,
"next":null,
"previous":null
},
"data":[{
firstName:"Bill",
lastName:"Bucks",
uri:"/api/v1/test/1"
}]
}
```
##### Auto Schema
```js
// GET /api/v1/test/schema
{
"fields": {
"firstName": {
"blank": false,
"default": null,
"help_text": "Forces values to string values by calling toString",
"nullable": false,
"readonly": false,
"type": "string",
"unique": false
},
"lastName": {
"blank": false,
"default": null,
"help_text": "Forces values to string values by calling toString",
"nullable": false,
"readonly": false,
"type": "string",
"unique": false
},
"uri": {
"blank": false,
"default": null,
"help_text": "Forces values to string values by calling toString",
"nullable": false,
"readonly": false,
"type": "string",
"unique": false
}
},
"filtering": {},
"format": "application/json",
"limit": 0,
"methodsAllowed": [
"get",
"put",
"post",
"delete",
"patch",
"head",
"options"
]
}
```
#### Built-in Fields
* field ( ApiField ) - Generic noop field
* object ( ObjectField ) - Generic no-op field
* char ( character / CharField ) - Converts values to strings
* array ( ArrayField ) Converts comma sparated strings into arrays
* int ( int / IntegerField ) converts numeric values into integers using `parseInt`
* float ( FloatField ) Converts values to floating point number using `parseFloat`
* bool ( BooleanField ) Forces values to booleans
* datetime ( DateTimeField ) Attempts to convert date time strings into date objects
* file ( FileField ) A field that pipes a stream to a configured location, and store a path
* filepath ( FilePathField ) A field that handles file locations rather than dealing with streams or binary data
This allows for full HTTP support and basic CRUD operations on a single enpoint - api/v1/test
```sh
curl -XPOST -H "Content-Type: applciation/json" -d '{"test":"fake"}' http://localhost:3000/api/v1/test
curl -XPUT -H "Content-Type: applciation/json" -d '{"test":"real"}' http://localhost:3000/api/v1/test
curl -XDELETE http://localhost:3000/api/v1/test/fake
```
#### HTTP Verbs
This is how tastypie handles the base HTTP Methods
* GET returns a list of resource instance or a specific resource instance
* DELETE removes a specific resource instance
* PUT **REPLACES** a resource instance. This is not a partial update. Any optional fields not define we be set to undefined
* PATCH a **PARTIAL** update to a specific resource instance.
* **OPTIONS**, **HEAD**, **TRACE**, and **CONNECT** are left to implementation on a per resource bases
### Serialization
The base serializer can deal with `xml`, `json` and `jsonp` out of the box. Serialization method is determined by the `Accept` header or a `format` query string param
```sh
curl -H "Accept: text/xml" http://localhost:3000/api/v1/test
curl http://localhost:3000/api/v1/test?format=xml
```
**NOTE:** hapi captures application/foo so for custom serialization, we must use text/foo
#### Example FS resourse
Of course, Tastypie is not tied to Mongo or mongose, you can use the default resource type to create to put a Rest API around anything. The mongo resource just does a lot of the set up for you.
Here is a resource that will asyncronously read a JSON file from disk are respond to GET requests. Supports XML, JSON, paging and dummy cache out of the box.
```js
var hapi = require('hapi');
var fs = require('fs')
var path = require('path')
var Resource = require('tastypie/lib/resource')
var Api = require('tastypie/lib/api')
var fields = require("tastypie/lib/fields")
var Class = require('tastypie/lib/class')
var Options = require('tastypie/lib/class/options')
var Serializer = require('tastypie/lib/serializer')
var debug = require('debug')('tastypie:example')
var app;
// make a simple object template to be populated
// This could be a Model class just as easily
function Schema(){
this.name = {
first: undefined, last: undefined
}
this.age = undefined;
this.guid = undefined;
this.range = []
this.eyeColor = undefined;
};
var Base = Class({
inherits:Resource
,options:{
template: Schema // Set the schema as the Object template
}
,fields:{
// remap _id to id
id : { type:'ApiField', attribute:'_id' }
, age : { type:'IntegerField' }
// can also be a field instance
, eyeColor : new fields.CharField({'null':true})
, range : { type:'ArrayField', 'null': true }
, fullname : { type:"CharField", 'null':true }
// remap the uid property to uuid.
, uuid : { type:'CharField', attribute:'guid'}
, name : { type:'ApiField'}
}
, constructor: function( meta ){
this.parent('constructor', meta )
}
// internal lower level method responsible for getting the raw data
, get_objects: function(bundle, callback){
fs.readFile( path.join(__dirname, 'example','data.json') , callback)
}
// internal low level method reponsible for dealing with a POST request
, create_object: function create_object( bundle, opt, callback ){
bundle = this.full_hydrate( bundle )
// this.save( bundle, callback )
callback && callback(null, bundle )
}
// per field dehydration method - generates a full name field from name.first & name.last
, dehydrate_fullname:function( obj, bundle ){
return obj.name.first + " " + obj.name.last
}
// top level method for custom GET /upload
, get_upload: function( bundle ){
this.respond({data:{key:'value'}})
}
// method that retreives an individual object by id.
// becuase it's in a flat file, read it, filter and return first object
,get_object: function(bundle, callback){
this._get_list(bundle,function(e, objects){
var obj = JSON.parse( objects ).filter(function( obj ){
return obj._id = bundle.req.params.id
})[0]
callback( null, obj )
})
}
// Proxy method for delegeting HTTP methods to approriate resource method
, dispatch_upload: function(req, reply ){
// Do additional magic here.
return this.dispatch('upload', this.bundle( req, reply ) )
}
// adds a custom route for upload in addition to standard crud methods
, prepend_urls:function(){
return [{
route: '/api/v1/data/upload'
, handler: this.dispatch_upload.bind( this )
, name:'upload'
}]
}
});
var api = new Api('api/v1', {
serializer:new Serializer()
})
app.connection({port:process.env.PORT || 2000 });
api.user('data', new Base() );
app.register( api, function(e){
app.start(function(){
console.log('server is ready')
});
});
```
Now you can read data from a file with your rest API
```sh
curl http://localhost:2000/api/v1/test
curl http://localhost:2000/api/v1/test?format=xml
curl http://localhost:2000/api/v1/test/1
curl http://localhost:2000/api/v1/test/2
curl http://localhost:2000/api/v1/test/2?format=xml
```
* Resource based REST Framework built ontop of hapi.js
* @module tastypie
* @author Eric Satterwhite
* @since 0.0.1
* @requires tastypie/lb
*/
module.exports = require('./lib');