forked from restify/node-restify
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.restdown
2124 lines (1629 loc) · 72.8 KB
/
index.restdown
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: API Guide | restify
markdown2extras: wiki-tables, code-friendly
---
# About restify
restify is a node.js module built specifically to enable you to build correct
REST web services. It intentionally borrows heavily
from [express](http://expressjs.com/)
as that is more or less the de facto API for writing
web applications on top of node.js.
## Why use restify and not express?
I get asked this more than anything else, so I'll just get it out of
the way up front.
Express' use case is targeted at browser applications and contains a
lot of functionality, such as templating and rendering, to support that.
Restify does not.
Restify exists to let you build "strict" API
services that are maintanable and observable. Restify comes with automatic
[DTrace](http://en.wikipedia.org/wiki/DTrace) support for all your
handlers, if you're running on a platform that supports DTrace.
In short, I wrote restify as I needed a framework that
gave me absolute control over interactions with HTTP and full
observability into the latency and characteristics of my
applications. If you don't need that, or don't care about those
aspect(s), then it's probably not for you.
For real time chat, discussion and support, join the `#restify` IRC channel on
`irc.freenode.net`.
## About this guide
This guide provides comprehensive documentation on writing a REST api (server)
with restify, writing clients that easily consume REST APIs, and on
the DTrace integration present in restify.
Note this documentation refers to the 2.x version(s) of restify;
these versions are not backwards-compatible with the 0.x and 1.x versions.
If you're migrating from an earlier version of restify, see
[1.4 to 2.0 Migration Tips](https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips)
## Conventions
Any content formatted like this:
$ curl localhost:8080
is a command-line example that you can run from a shell. All other examples
and information is formatted like this:
GET /foo HTTP/1.1
# Installation
$ npm install restify
# Server API
The most barebones echo server:
var restify = require('restify');
function respond(req, res, next) {
res.send('hello ' + req.params.name);
next();
}
var server = restify.createServer();
server.get('/hello/:name', respond);
server.head('/hello/:name', respond);
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
Try hitting that with the following curl commands to get a feel for
what restify is going to turn that into:
$ curl -is http://localhost:8080/hello/mark -H 'accept: text/plain'
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 10
Date: Mon, 31 Dec 2012 01:32:44 GMT
Connection: keep-alive
hello mark
$ curl -is http://localhost:8080/hello/mark
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 12
Date: Mon, 31 Dec 2012 01:33:33 GMT
Connection: keep-alive
"hello mark"
$ curl -is http://localhost:8080/hello/mark -X HEAD -H 'connection: close'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 12
Date: Mon, 31 Dec 2012 01:42:07 GMT
Connection: close
Note that by default, curl uses `Connection: keep-alive`. In order to make the HEAD
method return right away, you'll need to pass `Connection: close`.
Since curl is often used with REST APIs, restify provides a plugin to work around
this idiosyncrasy in curl. The plugin checks whether the user agent is curl. If it
is, it sets the Connection header to "close" and removes the "Content-Length" header.
server.pre(restify.pre.userAgentConnection());
See the [pre](#pre) method for more information.
## Creating a Server
Creating a server is straightforward, as you simply invoke the
`createServer` API, which takes an options object with the options listed
below (and `listen()` takes the same arguments as node's
[http.Server.listen](http://nodejs.org/docs/latest/api/http.html#http_server_listen_port_hostname_backlog_callback)):
var restify = require('restify');
var server = restify.createServer({
certificate: ...,
key: ...,
name: 'MyApp',
});
server.listen(8080);
||**Option**||**Type**||**Description**||
||certificate||String||If you want to create an HTTPS server, pass in the PEM-encoded certificate and key||
||key||String||If you want to create an HTTPS server, pass in the PEM-encoded certificate and key||
||formatters||Object||Custom response formatters for `res.send()`||
||handleUncaughtExceptions||Boolean||When true (the default) restify will use a domain to catch and respond to any uncaught exceptions that occur in it's handler stack||
||log||Object||You can optionally pass in a [bunyan](https://github.com/trentm/node-bunyan) instance; not required||
||name||String||By default, this will be set in the `Server` response header, default is `restify` ||
||spdy||Object||Any options accepted by [node-spdy](https://github.com/indutny/node-spdy)||
||version||String|Array||A default version to set for all routes||
||handleUpgrades||Boolean||Hook the `upgrade` event from the node HTTP server, pushing `Connection: Upgrade` requests through the regular request handling chain; defaults to `false`||
||httpsServerOptions||Object||Any options accepted by [node-https Server](http://nodejs.org/api/https.html#https_https). If provided the following restify server options will be ignored: spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and ciphers; however these can all be specified on httpsServerOptions.||
## Common handlers: server.use()
A restify server has a `use()` method that takes handlers of
the form `function (req, res, next)`. Note that restify runs
handlers in the order they are registered on a server, so if you want
some common handlers to run before any of your routes, issue calls to
`use()` before defining routes.
Note that in all calls to `use()` and the routes below, you can pass
in any combination of direct functions (`function(res, res, next)`) and
arrays of functions (`[function(req, res, next)]`).
## Routing
restify routing, in 'basic' mode, is pretty much identical to express/sinatra,
in that HTTP verbs are used with a parameterized resource to determine
what chain of handlers to run. Values associated with named
placeholders are available in `req.params`. Note that values will be
URL-decoded before being passed to you.
function send(req, res, next) {
res.send('hello ' + req.params.name);
return next();
}
server.post('/hello', function create(req, res, next) {
res.send(201, Math.random().toString(36).substr(3, 8));
return next();
});
server.put('/hello', send);
server.get('/hello/:name', send);
server.head('/hello/:name', send);
server.del('hello/:name', function rm(req, res, next) {
res.send(204);
return next();
});
You can also pass in a [RegExp](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp)
object and access the capture group with `req.params` (which will not
be interpreted in any way):
server.get(/^\/([a-zA-Z0-9_\.~-]+)\/(.*)/, function(req, res, next) {
console.log(req.params[0]);
console.log(req.params[1]);
res.send(200);
return next();
});
Here any request like:
$ curl localhost:8080/foo/my/cats/name/is/gandalf
Would result in `req.params[0]` being `foo` and `req.params[1]` being
`my/cats/name/is/gandalf`. Basically, you can do whatever you want.
Note the use of `next()`. You are responsible for calling `next()` in
order to run the next handler in the chain. As below, you can pass an
Error object in to have restify automatically return responses to the client.
You can pass in `false` to not error, but to stop the handler
chain. This is useful if you had a `res.send` in an early filter, which is
not an error, and you possibly have one later you want to short-circuit.
Lastly, you can pass in a string `name` to `next()`, and restify will lookup
that route, and assuming it exists will run the chain *from where you left
off*. So for example:
var count = 0;
server.use(function foo(req, res, next) {
count++;
next();
});
server.get('/foo/:id', function (req, res, next) {
next('foo2');
});
server.get({
name: 'foo2',
path: '/foo/:id'
}, function (req, res, next) {
assert.equal(count, 1);
res.send(200);
next();
});
Note that `foo` only gets run once in that example. A few caveats:
- If you provide a name that doesn't exist, restify will 500 that request.
- Don't be silly and call this in cycles. restify won't check that.
- Lastly, you cannot "chain" `next('route')` calls; you can only delegate the
routing chain once (this is a limitation of the way routes are stored
internally, and may be revisited someday).
### Chaining Handlers
Routes can also accept more than one handler function. For instance:
server.get(
'/foo/:id',
function(req, res, next) {
console.log('Authenticate');
return next();
},
function(req, res, next) {
res.send(200);
return next();
}
);
### Hypermedia
If a parameterized route was defined with a string (not a regex), you can render it from other places in the server. This is useful to have HTTP responses that link to other resources, without having to hardcode URLs throughout the codebase. Both path and query strings parameters get URL encoded appropriately.
server.get({name: 'city', path: '/cities/:slug'}, /* ... */);
// in another route
res.send({
country: 'Australia',
// render a URL by specifying the route name and parameters
capital: server.router.render('city', {slug: 'canberra'}, {details: true})
});
Which returns:
{
"country": "Australia",
"capital": "/cities/canberra?details=true"
}
### Versioned Routes
Most REST APIs tend to need versioning, and restify ships with support
for [semver](http://semver.org/) versioning in an `Accept-Version`
header, the same way you specify NPM version dependencies:
var restify = require('restify');
var server = restify.createServer();
function sendV1(req, res, next) {
res.send('hello: ' + req.params.name);
return next();
}
function sendV2(req, res, next) {
res.send({hello: req.params.name});
return next();
}
var PATH = '/hello/:name';
server.get({path: PATH, version: '1.1.3'}, sendV1);
server.get({path: PATH, version: '2.0.0'}, sendV2);
server.listen(8080);
Try hitting with:
$ curl -s localhost:8080/hello/mark
"hello: mark"
$ curl -s -H 'accept-version: ~1' localhost:8080/hello/mark
"hello: mark"
$ curl -s -H 'accept-version: ~2' localhost:8080/hello/mark
{"hello":"mark"}
$ curl -s -H 'accept-version: ~3' localhost:8080/hello/mark | json
{
"code": "InvalidVersion",
"message": "GET /hello/mark supports versions: 1.1.3, 2.0.0"
}
In the first case, we didn't specify an `Accept-Version` header
at all, so restify treats that like sending a `*`. Much as not sending
an `Accept` header means the client gets the server's choice. Restify
will choose this highest matching route.
In the second case, we explicitly asked for for V1, which got
us the same response, but then we asked for V2 and got back JSON. Finally,
we asked for a version that doesn't exist and got an error (notably,
we didn't send an `Accept` header, so we got a JSON response). Which
segues us nicely into content negotiation.
You can default the versions on routes by passing in a version field at
server creation time. Lastly, you can support multiple versions in the API
by using an array:
server.get({path: PATH, version: ['2.0.0', '2.1.0', '2.2.0']}, sendV2);
In this case you may need to know more information such as what the
original requested version string was, and what the matching
version from the routes supported version array was. Two methods make
this info available:
var PATH = '/version/test';
server.get({path: PATH, version: ['2.0.0', '2.1.0', '2.2.0']}, function (req) {
res.send(200, {
requestedVersion: req.version(),
matchedVersion: req.matchedVersion()
});
});
Hitting this route:
$ curl -s -H 'accept-version: <2.2.0' localhost:8080/version/test | json
{
"requestedVersion": "<2.2.0",
"matchedVersion": "2.1.0"
}
In response to the above
## Upgrade Requests
Incoming HTTP requests that contain a `Connection: Upgrade` header are treated
somewhat differently by the node HTTP server. If you want restify to push
Upgrade requests through the regular routing chain, you need to enable
`handleUpgrades` when creating the server.
To determine if a request is eligible for Upgrade, check for the existence of
`res.claimUpgrade()`. This method will return an object with two properties:
the `socket` of the underlying connection, and the first received data `Buffer`
as `head` (may be zero-length).
Once `res.claimUpgrade()` is called, `res` itself is marked unusable for
further HTTP responses; any later attempt to `send()` or `end()`, etc, will
throw an `Error`. Likewise if `res` has already been used to send at least
part of a response to the client, `res.claimUpgrade()` will throw an `Error`.
Upgrades and regular HTTP Response behaviour are mutually exclusive on any
particular connection.
Using the Upgrade mechanism, you can use a library like
[watershed](https://github.com/jclulow/node-watershed) to negotiate WebSockets
connections. For example:
var ws = new Watershed();
server.get('/websocket/attach', function upgradeRoute(req, res, next) {
if (!res.claimUpgrade) {
next(new Error('Connection Must Upgrade For WebSockets'));
return;
}
var upgrade = res.claimUpgrade();
var shed = ws.accept(req, upgrade.socket, upgrade.head);
shed.on('text', function(msg) {
console.log('Received message from websocket client: ' + msg);
});
shed.send('hello there!');
next(false);
});
## Content Negotiation
If you're using `res.send()` restify will automatically select the
content-type to respond with, by finding the first registered
`formatter` defined. Note in the examples above we've not defined any
formatters, so we've been leveraging the fact that restify ships with
`application/json`, `text/plain` and `application/octet-stream`
formatters. You can add additional formatters to restify by passing
in a hash of content-type -> parser at server creation time:
var server = restify.createServer({
formatters: {
'application/foo': function formatFoo(req, res, body, cb) {
if (body instanceof Error)
return body.stack;
if (Buffer.isBuffer(body))
return cb(null, body.toString('base64'));
return cb(null, util.inspect(body));
}
}
});
You can do whatever you want, but you probably want to check the type
of `body` to figure out what type it is, notably for
Error/Buffer/everything else. You can always add more formatters
later by just setting the formatter on `server.formatters`, but it's
probably sane to just do it at construct time. Also, note that if a
content-type can't be negotiated, the default is
`application/octet-stream`. Of course, you can always explicitly set
the content-type:
res.setHeader('content-type', 'application/foo');
res.send({hello: 'world'});
Note that there are typically at least three content-types supported by
restify: json, text and binary. When you override or append to this, the
"priority" might change; to ensure that the priority is set to what you
want, you should set a `q-value` on your formatter definitions, which will
ensure sorting happens the way you want:
restify.createServer({
formatters: {
'application/foo; q=0.9': function formatFoo(req, res, body, cb) {
if (body instanceof Error)
return cb(body);
if (Buffer.isBuffer(body))
return cb(null, body.toString('base64'));
return cb(null, util.inspect(body));
}
}
});
Lastly, you don't have to use any of this magic, as a restify response
object has all the "raw" methods of a node
[ServerResponse](http://nodejs.org/docs/latest/api/http.html#http.ServerResponse)
on it as well.
var body = 'hello world';
res.writeHead(200, {
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'text/plain'
});
res.write(body);
res.end();
## Error handling
You can handle errors in restify a few different ways. First, you can
always just call `res.send(err)`. You can also shorthand this in a
route by doing:
server.get('/hello/:name', function(req, res, next) {
return database.get(req.params.name, function(err, user) {
if (err)
return next(err);
res.send(user);
return next();
});
});
If you invoke `res.send()` with an error that has a `statusCode`
attribute, that will be used, otherwise a default of 500 will be used
(unless you're using `res.send(4xx, new Error('blah))`).
Alternatively, restify 2.1 supports a `next.ifError` API:
server.get('/hello/:name', function(req, res, next) {
return database.get(req.params.name, function(err, user) {
next.ifError(err);
res.send(user);
next();
});
});
Sometimes, for all requests, you may want to handle an error condition the same
way. As an example, you may want to serve a 500 page on all
`InternalServerErrors`. In this case you can add a listener for this error that
is always fired when this Error is encountered by Restify as part of a
`next(error)` statement. This gives you a way to handle all errors of the same
class identically across the server.
server.get('/hello/:name', function(req, res, next) {
// some internal unrecoverable error
var err = new restify.errors.InternalServerError('oh noes!');
return next(err);
});
server.on('InternalServerError', function (req, res, err, cb) {
err._customContent = 'something is wrong!';
return cb();
});
### HttpError
Now the obvious question is what that exactly does (in either case).
restify tries to be programmer-friendly with errors by exposing all
HTTP status codes as a subclass of `HttpError`. So, for example, you can
do this:
server.get('/hello/:name', function(req, res, next) {
return next(new restify.ConflictError("I just don't like you"));
});
$ curl -is -H 'accept: text/*' localhost:8080/hello/mark
HTTP/1.1 409 Conflict
Content-Type: text/plain
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, Api-Version
Access-Control-Expose-Headers: Api-Version, Request-Id, Response-Time
Connection: close
Content-Length: 21
Content-MD5: up6uNh2ejV/C6JUbLlvsiw==
Date: Tue, 03 Jan 2012 00:24:48 GMT
Server: restify
Request-Id: 1685313e-e801-4d90-9537-7ca20a27acfc
Response-Time: 1
I just don't like you
Alternatively, you can access the error classes via `restify.errors`. We can do this with a simple change to the previous example:
server.get('/hello/:name', function(req, res, next) {
return next(new restify.errors.ConflictError("I just don't like you"));
});
The core thing to note about an `HttpError` is that it has a numeric
code (statusCode) and a `body`. The statusCode will automatically
set the HTTP response status code, and the body attribute by default
will be the message.
All status codes between 400 and 5xx are automatically converted into
an HttpError with the name being 'PascalCase' and spaces removed. For
the complete list, take a look at the
[node source](https://github.com/joyent/node/blob/v0.6/lib/http.js#L152-205).
From that code above `418: I'm a teapot` would be `ImATeapotError`, as
an example.
### RestError
Now, a common problem with REST APIs and HTTP is that they often end
up needing to overload 400 and 409 to mean a bunch of different
things. There's no real standard on what to do in these cases, but in
general you want machines to be able to (safely) parse these things
out, and so restify defines a convention of a `RestError`. A
`RestError` is a subclass of one of the particular `HttpError` types,
and additionally sets the body attribute to be a JS object with the
attributes `code` and `message`. For example, here's a built-in RestError:
var server = restify.createServer();
server.get('/hello/:name', function(req, res, next) {
return next(new restify.InvalidArgumentError("I just don't like you"));
});
$ curl -is localhost:8080/hello/mark | json
HTTP/1.1 409 Conflict
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, Api-Version
Access-Control-Expose-Headers: Api-Version, Request-Id, Response-Time
Connection: close
Content-Length: 60
Content-MD5: MpEcO5EQFUZ2MNeUB2VaZg==
Date: Tue, 03 Jan 2012 00:50:21 GMT
Server: restify
Request-Id: bda456dd-2fe4-478d-809c-7d159d58d579
Response-Time: 3
{
"code": "InvalidArgument",
"message": "I just don't like you"
}
The built-in restify errors are:
* BadRequestError (400 Bad Request)
* UnauthorizedError (401 Unauthorized)
* PaymentRequiredError (402 Payment Required)
* ForbiddenError (403 Forbidden)
* NotFoundError (404 Not Found)
* MethodNotAllowedError (405 Method Not Allowed)
* NotAcceptableError (406 Not Acceptable)
* ProxyAuthenticationRequiredError (407 Proxy Authentication Required)
* RequestTimeoutError (408 Request Time-out)
* ConflictError (409 Conflict)
* GoneError (410 Gone)
* LengthRequiredError (411 Length Required)
* PreconditionFailedError (412 Precondition Failed)
* RequestEntityTooLargeError (413 Request Entity Too Large)
* RequesturiTooLargeError (414 Request-URI Too Large)
* UnsupportedMediaTypeError (415 Unsupported Media Type)
* RequestedRangeNotSatisfiableError (416 Requested Range Not Satisfiable)
* ExpectationFailedError (417 Expectation Failed)
* ImATeapotError (418 I'm a teapot)
* UnprocessableEntityError (422 Unprocessable Entity)
* LockedError (423 Locked)
* FailedDependencyError (424 Failed Dependency)
* UnorderedCollectionError (425 Unordered Collection)
* UpgradeRequiredError (426 Upgrade Required)
* PreconditionRequiredError (428 Precondition Required)
* TooManyRequestsError (429 Too Many Requests)
* RequestHeaderFieldsTooLargeError (431 Request Header Fields Too Large)
* InternalServerError (500 Internal Server Error)
* NotImplementedError (501 Not Implemented)
* BadGatewayError (502 Bad Gateway)
* ServiceUnavailableError (503 Service Unavailable)
* GatewayTimeoutError (504 Gateway Time-out)
* HttpVersionNotSupportedError (505 HTTP Version Not Supported)
* VariantAlsoNegotiatesError (506 Variant Also Negotiates)
* InsufficientStorageError (507 Insufficient Storage)
* BandwidthLimitExceededError (509 Bandwidth Limit Exceeded)
* NotExtendedError (510 Not Extended)
* NetworkAuthenticationRequiredError (511 Network Authentication Required)
* BadDigestError (400 Bad Request)
* BadMethodError (405 Method Not Allowed)
* InternalError (500 Internal Server Error)
* InvalidArgumentError (409 Conflict)
* InvalidContentError (400 Bad Request)
* InvalidCredentialsError (401 Unauthorized)
* InvalidHeaderError (400 Bad Request)
* InvalidVersionError (400 Bad Request)
* MissingParameterError (409 Conflict)
* NotAuthorizedError (403 Forbidden)
* RequestExpiredError (400 Bad Request)
* RequestThrottledError (429 Too Many Requests)
* ResourceNotFoundError (404 Not Found)
* WrongAcceptError (406 Not Acceptable)
You can always add your own by subclassing `restify.RestError` like:
var restify = require('restify');
var util = require('util');
function MyError(message) {
restify.RestError.call(this, {
restCode: 'MyError',
statusCode: 418,
message: message,
constructorOpt: MyError
});
this.name = 'MyError';
};
util.inherits(MyError, restify.RestError);
Basically, a `RestError` takes a statusCode, a restCode, a message,
and a "constructorOpt" so that V8 correctly omits your code
from the stack trace (you don't *have* to do that, but you probably
want it). In the example above, we also set the name property so
`console.log(new MyError());` looks correct.
## Socket.IO
To use [socket.io](http://socket.io/) with restify, just treat your restify
server as if it were a "raw" node server:
var server = restify.createServer();
var io = socketio.listen(server);
server.get('/', function indexHTML(req, res, next) {
fs.readFile(__dirname + '/index.html', function (err, data) {
if (err) {
next(err);
return;
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end(data);
next();
});
});
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
server.listen(8080, function () {
console.log('socket.io server listening at %s', server.url);
});
## Server API
### Events
Restify servers emit all the events from the node
[http.Server](http://nodejs.org/docs/latest/api/http.html#http_class_http_server)
and has several other events you want to listen on.
##### Event: 'NotFound'
`function (request, response, error, cb) {}`
When a client request is sent for a URL that does not exist, restify
will emit this event. Note that restify checks for listeners on this
event, and if there are none, responds with a default 404 handler. It
is expected that if you listen for this event, you respond to the client.
#### Event: 'MethodNotAllowed'
`function (request, response, error, cb) {}`
When a client request is sent for a URL that does exist, but you have
not registered a route for that HTTP verb, restify will emit this
event. Note that restify checks for listeners on this event, and if
there are none, responds with a default 405 handler. It
is expected that if you listen for this event, you respond to the client.
#### Event: 'VersionNotAllowed'
`function (request, response, error, cb) {}`
When a client request is sent for a route that exists, but does not
match the version(s) on those routes, restify will emit this
event. Note that restify checks for listeners on this event, and if
there are none, responds with a default 400 handler. It
is expected that if you listen for this event, you respond to the client.
#### Event: UnsupportedMediaType'
`function (request, response, error, cb) {}`
When a client request is sent for a route that exist, but has a `content-type`
mismatch, restify will emit this event. Note that restify checks for listeners
on this event, and if there are none, responds with a default 415 handler. It
is expected that if you listen for this event, you respond to the client.
#### Event: 'after'
`function (request, response, route, error) {}`
Emitted after a route has finished all the handlers you registered.
You can use this to write audit logs, etc. The route parameter will be
the `Route` object that ran. Note that when you are using the default
404/405/BadVersion handlers, this event will still be fired, but route will
be `null`. If you have registered your own listeners for those, this event
will not be fired unless you invoke the `cb` argument that is provided with
them.
#### Event: 'uncaughtException'
`function (request, response, route, error) {}`
Emitted when some handler throws an uncaughtException somewhere in the chain and
only when 'handleUncaughtExceptions' is set to true on the restify server. The
restify server has a default handler for this event - which is to just call
`res.send(error)`, and lets the built-ins in restify handle transforming, but
you can override the default handler to do whatever you want.
### Properties
A restify server has the following properties on it:
||**Name**||**Type**||**Description**||
||name||String||name of the server||
||version||String|Array||default version to use in all routes||
||log||Object||[bunyan](https://github.com/trentm/node-bunyan) instance||
||acceptable||Array(String)||list of content-types this server can respond with||
||url||String||Once listen() is called, this will be filled in with where the server is running||
### Other Methods
#### address()
Wraps node's [address()](http://nodejs.org/docs/latest/api/net.html#net_server_address).
#### listen(port, [host], [callback]) or listen(path, [callback])
Wraps node's [listen()](http://nodejs.org/docs/latest/api/net.html#net_server_listen_path_callback).
#### close()
Wraps node's [close()](http://nodejs.org/docs/latest/api/net.html#net_event_close).
#### pre()
Allows you to add in handlers that run *before* routing occurs. This gives you
a hook to change request headers and the like if you need to. Note that
`req.params` will be undefined, as that's filled in *after* routing.
server.pre(function(req, res, next) {
req.headers.accept = 'application/json'; // screw you client!
return next();
});
You can also clean up URLs before routes are matched with the built in
`restify.pre.sanitizePath`. This [re-implements v1.4 behaviour](https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips#trailing--characters)
server.pre(restify.pre.sanitizePath());
#### use()
Allows you to add in handlers that run no matter what the route.
## Bundled Plugins
restify ships with several handlers you can use, specifically:
* Accept header parsing
* Authorization header parsing
* CORS handling plugin
* Date header parsing
* JSONP support
* Gzip Response
* Query string parsing
* Body parsing (JSON/URL-encoded/multipart form)
* Static file serving
* Throttling
* Conditional request handling
* Audit logger
Here's some example code using all the shipped plugins:
var server = restify.createServer();
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.jsonp());
server.use(restify.gzipResponse());
server.use(restify.bodyParser());
server.use(restify.requestExpiry());
server.use(restify.throttle({
burst: 100,
rate: 50,
ip: true,
overrides: {
'192.168.1.1': {
rate: 0, // unlimited
burst: 0
}
}
}));
server.use(restify.conditionalRequest());
### Accept Parser
Parses out the `Accept` header, and ensures that the server can respond to what
the client asked for. You almost always want to just pass in
`server.acceptable` here, as that's an array of content types the server knows
how to respond to (with the formatters you've registered). If the request is
for a non-handled type, this plugin will return an error of `406`.
server.use(restify.acceptParser(server.acceptable));
### Authorization Parser
server.use(restify.authorizationParser());
Parses out the `Authorization` header as best restify can. Currently only
HTTP Basic Auth and
[HTTP Signature](https://github.com/joyent/node-http-signature)
schemes are supported. When this is used, `req.authorization` will be set
to something like:
{
scheme: <Basic|Signature|...>,
credentials: <Undecoded value of header>,
basic: {
username: $user
password: $password
}
}
`req.username` will also be set, and defaults to 'anonymous'. If the scheme
is unrecognized, the only thing available in `req.authorization` will be
`scheme` and `credentials` - it will be up to you to parse out the rest.
### CORS
server.use(restify.CORS());
Supports tacking [CORS](http://www.w3.org/TR/cors) headers into `actual`
requests (as defined by the spec). Note that preflight requests are
automatically handled by the router, and you can override the default behavior
on a per-URL basis with `server.opts(:url, ...)`. To fully specify this plugin,
a sample invocation is:
server.use(restify.CORS({
origins: ['https://foo.com', 'http://bar.com', 'http://baz.com:8081'], // defaults to ['*']
credentials: true, // defaults to false
headers: ['x-foo'] // sets expose-headers
}));
### Date Parser
server.use(restify.dateParser());
Parses out the HTTP Date header (if present) and checks for clock skew (default
allowed clock skew is 300s, like Kerberos). You can pass in a number, which is
interpreted in seconds, to allow for clock skew.
// Allows clock skew of 1m
server.use(restify.dateParser(60));
### QueryParser
server.use(restify.queryParser([OPTIONS]));
Parses the HTTP query string (e.g., `/foo?id=bar&name=mark`) to an object on
`req.query`, e.g. `{id: 'bar', name: 'mark'}`.
By default, query params are merged into `req.params` (which may include
parameters from `restify.bodyParser`). This can be disabled by the `mapParams`
option:
server.use(restify.queryParser({mapParams: false}));
QueryParser options are as follows. All but `mapParams` are directly passed to
the underlying ["qs" module](https://github.com/ljharb/qs#readme).
|| **Option** || **Type** || **Default** || **Description** ||
|| mapParams || bool || true\** || Whether to merge req.query into req.params. ||
|| overrideParams || bool || false || If mapParams=true, this determines if a field in the query params will override an existing value in `req.params`. ||
|| allowDots || bool || true \* || Transform `?foo.bar=baz` to a nested object: `{foo: {bar: 'baz'}}`. ||
|| arrayLimit || num || 20 || Only transform `?a[$index]=b` to an array if `$index` is less than `arrayLimit`. ||
|| depth || num || 5 || The depth limit for parsing nested objects, e.g. `?a[b][c][d][e][f][g][h][i]=j`. ||
|| parameterLimit || num || 1000 || Maximum number of query params parsed. Additional params are silently dropped. ||
|| parseArrays || bool || true || Whether to parse `?a[]=b&a[1]=c` to an array, e.g. `{a: ['b', 'c']}`. ||
|| plainObjects || bool || true \* || Whether `req.query` is a "plain" object -- does not inherit from `Object`. This can be used to allow query params whose names collide with Object methods, e.g. `?hasOwnProperty=blah`. ||
|| strictNullHandling || bool || false || If true, `?a&b=` results in `{a: null, b: ''}`. Otherwise, `{a: '', b: ''}`. ||
\*: The `allowDots` and `plainObjects` options are `true` by default in restify
4.x because of an accident. Restify 4.0 updated from qs@2 to qs@3, which changed
default behavior to this. Subsequent qs major versions reverted that behaviour.
The restify 4.x stream will maintain this default, but all other major versions
of restify (those before v4) and the `queryParser` plugin in [email protected]
and later (restify 5.x no longer bundles the plugins) default to
`allowDots=false` and `plainObjects=false`. The recommended usage (for new
code or to maintain compat across restify major versions is):
server.use(restify.queryParser({allowDots: false, plainObjects: false}));
\**: Note that `mapParams` will default to *false* in restify-plugins (the
module for plugins in restify 5.x).
### JSONP
Supports checking the query string for `callback` or `jsonp` and ensuring that
the content-type is appropriately set if JSONP params are in place. There is
also a default `application/javascript` formatter to handle this.
You *should* set the `queryParser` plugin to run before this, but if you don't
this plugin will still parse the query string properly.
### BodyParser
Blocks your chain on reading and parsing the HTTP request body. Switches on
`Content-Type` and does the appropriate logic. `application/json`,
`application/x-www-form-urlencoded` and `multipart/form-data` are currently
supported.
server.use(restify.bodyParser({
maxBodySize: 0,
mapParams: true,
mapFiles: false,
overrideParams: false,
multipartHandler: function(part) {
part.on('data', function(data) {
/* do something with the multipart data */
});
},
multipartFileHandler: function(part) {