diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index aafef048d6..2bba0b8957 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -249,6 +249,7 @@ All definitions files include a header with the author and editors, so at some p
* [msgpack.js](https://github.com/uupaa/msgpack.js) (by [Shinya Mochizuki](https://github.com/enrapt-mochizuki))
* [Mustache.js](https://github.com/janl/mustache.js) (by [Boris Yankov](https://github.com/borisyankov))
* [nconf](https://github.com/flatiron/nconf) (by [Jeff Goddard](https://github.com/jedigo))
+* [needle](https://github.com/tomas/needle) (by [San Chen](https://github.com/bigsan))
* [noble](https://github.com/sandeepmistry/noble) (by [Seon-Wook Park](https://github.com/swook))
* [nock](https://github.com/pgte/nock) (by [bonnici](https://github.com/bonnici))
* [Node.js](http://nodejs.org/) (from TypeScript samples)
diff --git a/needle/needle-tests.ts b/needle/needle-tests.ts
new file mode 100644
index 0000000000..24d9701b51
--- /dev/null
+++ b/needle/needle-tests.ts
@@ -0,0 +1,108 @@
+///
+
+import needle = require("needle");
+
+function Usage() {
+ // using callback
+ needle.get('http://ifconfig.me/all.json', function (error, response) {
+ if (!error)
+ console.log(response.body.ip_addr); // JSON decoding magic. :)
+ });
+
+ // using streams
+ var out: any; // = fs.createWriteStream('logo.png');
+ needle.get('https://google.com/images/logo.png').pipe(out);
+}
+
+function ResponsePipeline() {
+ needle.get('http://stackoverflow.com/feeds', { compressed: true }, function (err, resp) {
+ console.log(resp.body); // this little guy won't be a Gzipped binary blob
+ // but a nice object containing all the latest entries
+ });
+
+ var options = {
+ compressed: true,
+ follow: true,
+ rejectUnauthorized: true
+ };
+
+ // in this case, we'll ask Needle to follow redirects (disabled by default),
+ // but also to verify their SSL certificates when connecting.
+ var stream = needle.get('https://backend.server.com/everything.html', options);
+
+ stream.on('readable', function () {
+ var data: any;
+ while (data = this.read()) {
+ console.log(data.toString());
+ }
+ });
+}
+
+function API_head() {
+ var options = {
+ timeout: 5000 // if we don't get a response in 5 seconds, boom.
+ };
+
+ needle.head('https://my.backend.server.com', function (err, resp) {
+ if (err) {
+ console.log('Shoot! Something is wrong: ' + err.message);
+ }
+ else {
+ console.log('Yup, still alive.');
+ }
+ });
+}
+
+function API_get() {
+ needle.get('google.com/search?q=syd+barrett', function (err, resp) {
+ // if no http:// is found, Needle will automagically prepend it.
+ });
+}
+
+function API_post() {
+ var options = {
+ headers: { 'X-Custom-Header': 'Bumbaway atuna' }
+ };
+
+ needle.post('https://my.app.com/endpoint', 'foo=bar', options, function (err, resp) {
+ // you can pass params as a string or as an object.
+ });
+}
+
+function API_put() {
+ var nested = {
+ params: {
+ are: {
+ also: 'supported'
+ }
+ }
+ };
+
+ needle.put('https://api.app.com/v2', nested, function (err, resp) {
+ console.log('Got ' + resp.bytes + ' bytes.') // another nice treat from this handsome fella.
+ });
+}
+
+function API_delete() {
+ var options = {
+ username: 'fidelio',
+ password: 'x'
+ };
+
+ needle.delete('https://api.app.com/messages/123', null, options, function (err, resp) {
+ // in this case, data may be null, but you need to explicity pass it.
+ });
+}
+
+function API_request() {
+ var data = {
+ q: 'a very smart query',
+ page: 2,
+ format: 'json'
+ };
+
+ needle.request('get', 'forum.com/search', data, function (err, resp) {
+ if (!err && resp.statusCode == 200)
+ console.log(resp.body); // here you go, mister.
+ });
+}
diff --git a/needle/needle.d.ts b/needle/needle.d.ts
new file mode 100644
index 0000000000..741cf5fe3b
--- /dev/null
+++ b/needle/needle.d.ts
@@ -0,0 +1,88 @@
+// Type definitions for needle 0.7.8
+// Project: https://github.com/tomas/needle
+// Definitions by: San Chen
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+///
+
+declare module Needle {
+ interface ReadableStream extends NodeJS.ReadableStream {
+ }
+
+ interface Callback {
+ (error: Error, response: any, body: any): void;
+ }
+
+ interface RequestOptions {
+ timeout?: number;
+ follow?: any; // number | string
+ multipart?: boolean;
+ proxy?: string;
+ agent?: string;
+ headers?: any;
+ auth?: string; // auto | digest | basic (default)
+ json?: boolean;
+ }
+
+ interface ResponseOptions {
+ decode?: boolean;
+ parse?: boolean;
+ output?: any;
+ }
+
+ interface HttpHeaderOptions {
+ compressed?: boolean;
+ username?: string;
+ password?: string;
+ accept?: string;
+ connection?: string;
+ user_agent?: string;
+ }
+
+ interface TLSOptions {
+ pfx?: any;
+ key?: any;
+ passphrase?: string;
+ cert?: any;
+ ca?: any;
+ ciphers?: any;
+ rejectUnauthorized?: boolean;
+ secureProtocol?: any;
+ }
+
+ interface NeedleOptions extends RequestOptions, ResponseOptions, HttpHeaderOptions, TLSOptions {
+ }
+
+ interface NeedleStatic {
+ defaults(options?: any): void;
+
+ head(url: string): ReadableStream;
+ head(url: string, callback?: Callback): ReadableStream;
+ head(url: string, options?: RequestOptions, callback?: Callback): ReadableStream;
+
+ get(url: string): ReadableStream;
+ get(url: string, callback?: Callback): ReadableStream;
+ get(url: string, options?: RequestOptions, callback?: Callback): ReadableStream;
+
+ post(url: string, data: any): ReadableStream;
+ post(url: string, data: any, callback?: Callback): ReadableStream;
+ post(url: string, data: any, options?: RequestOptions, callback?: Callback): ReadableStream;
+
+ put(url: string, data: any): ReadableStream;
+ put(url: string, data: any, callback?: Callback): ReadableStream;
+ put(url: string, data: any, options?: RequestOptions, callback?: Callback): ReadableStream;
+
+ delete(url: string, data: any): ReadableStream;
+ delete(url: string, data: any, callback?: Callback): ReadableStream;
+ delete(url: string, data: any, options?: RequestOptions, callback?: Callback): ReadableStream;
+
+ request(method: string, url: string, data: any): ReadableStream;
+ request(method: string, url: string, data: any, callback?: Callback): ReadableStream;
+ request(method: string, url: string, data: any, options?: RequestOptions, callback?: Callback): ReadableStream;
+ }
+}
+
+declare module "needle" {
+ var needle: Needle.NeedleStatic;
+ export = needle;
+}