Code Monkey home page Code Monkey logo

node-rest-client's Introduction

REST Client for Node.js

npm version Build Status

NPM

Features

Allows connecting to any API REST and get results as js Object. The client has the following features:

  • Transparent HTTP/HTTPS connection to remote API sites.
  • Allows simple HTTP basic authentication.
  • Allows most common HTTP operations: GET, POST, PUT, DELETE, PATCH or any other method through custom connect method
  • Allows creation of custom HTTP Methods (PURGE, etc.)
  • Direct or through proxy connection to remote API sites.
  • Register remote API operations as own client methods, simplifying reuse.
  • Dynamic path and query parameters and request headers.
  • Improved Error handling mechanism (client or specific request)
  • Added support for compressed responses: gzip and deflate
  • Added support for follow redirects thanks to great follow-redirects package
  • Added support for custom request serializers (json,xml and url-encoded included by default)
  • Added support for custom response parsers (json and xml included by default)

Installation

$ npm install node-rest-client

Usages

Simple HTTP GET

Client has two ways to call a REST service: direct or using registered methods

var Client = require('node-rest-client').Client;

var client = new Client();

// direct way
client.get("http://remote.site/rest/xml/method", function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// registering remote methods
client.registerMethod("jsonMethod", "http://remote.site/rest/json/method", "GET");

client.methods.jsonMethod(function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

HTTP POST

POST, PUT or PATCH method invocation are configured like GET calls with the difference that you have to set "Content-Type" header in args passed to client method invocation:

//Example POST method invocation
var Client = require('node-rest-client').Client;

var client = new Client();

// set content-type header and data as json in args parameter
var args = {
	data: { test: "hello" },
	headers: { "Content-Type": "application/json" }
};

client.post("http://remote.site/rest/xml/method", args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// registering remote methods
client.registerMethod("postMethod", "http://remote.site/rest/json/method", "POST");

client.methods.postMethod(args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

If no "Content-Type" header is set as client arg POST,PUT and PATCH methods will not work properly.

Passing args to registered methods

You can pass diferents args to registered methods, simplifying reuse: path replace parameters, query parameters, custom headers

var Client = require('node-rest-client').Client;

// direct way
var client = new Client();

var args = {
	data: { test: "hello" }, // data passed to REST method (only useful in POST, PUT or PATCH methods)
	path: { "id": 120 }, // path substitution var
	parameters: { arg1: "hello", arg2: "world" }, // this is serialized as URL parameters
	headers: { "test-header": "client-api" } // request headers
};


client.get("http://remote.site/rest/json/${id}/method", args,
	function (data, response) {
		// parsed response body as js object
		console.log(data);
		// raw response
		console.log(response);
	});


// registering remote methods
client.registerMethod("jsonMethod", "http://remote.site/rest/json/${id}/method", "GET");


/* this would construct the following URL before invocation
 *
 * http://remote.site/rest/json/120/method?arg1=hello&arg2=world
 *
 */
client.methods.jsonMethod(args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

You can even use path placeholders in query string in direct connection:

var Client = require('node-rest-client').Client;

// direct way
var client = new Client();

var args = {
	path: { "id": 120, "arg1": "hello", "arg2": "world" },	
	headers: { "test-header": "client-api" }
};

client.get("http://remote.site/rest/json/${id}/method?arg1=${arg1}&arg2=${arg2}", args,
	function (data, response) {
		// parsed response body as js object
		console.log(data);
		// raw response
		console.log(response);
	});

HTTP POST and PUT methods

To send data to remote site using POST or PUT methods, just add a data attribute to args object:

var Client = require('node-rest-client').Client;

// direct way
var client = new Client();

var args = {
	path: { "id": 120 },
	parameters: { arg1: "hello", arg2: "world" },
	headers: { "test-header": "client-api" },
	data: "<xml><arg1>hello</arg1><arg2>world</arg2></xml>"
};

client.post("http://remote.site/rest/xml/${id}/method", args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// registering remote methods
client.registerMethod("xmlMethod", "http://remote.site/rest/xml/${id}/method", "POST");


client.methods.xmlMethod(args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// posted data can be js object
var args_js = {
	path: { "id": 120 },
	parameters: { arg1: "hello", arg2: "world" },
	headers: { "test-header": "client-api" },
	data: { "arg1": "hello", "arg2": 123 }
};

client.methods.xmlMethod(args_js, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

Request/Response configuration

It's also possible to configure each request and response, passing its configuration as an additional argument in method call.

var client = new Client();

// request and response additional configuration
var args = {
	path: { "id": 120 },
	parameters: { arg1: "hello", arg2: "world" },
	headers: { "test-header": "client-api" },
	data: "<xml><arg1>hello</arg1><arg2>world</arg2></xml>",
	requestConfig: {
		timeout: 1000, //request timeout in milliseconds
		noDelay: true, //Enable/disable the Nagle algorithm
		keepAlive: true, //Enable/disable keep-alive functionalityidle socket.
		keepAliveDelay: 1000 //and optionally set the initial delay before the first keepalive probe is sent
	},
	responseConfig: {
		timeout: 1000 //response timeout
	}
};


client.post("http://remote.site/rest/xml/${id}/method", args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

If you want to handle timeout events both in the request and in the response just add a new "requestTimeout" or "responseTimeout" event handler to clientRequest returned by method call.

var client = new Client();

// request and response additional configuration
var args = {
	path: { "id": 120 },
	parameters: { arg1: "hello", arg2: "world" },
	headers: { "test-header": "client-api" },
	data: "<xml><arg1>hello</arg1><arg2>world</arg2></xml>",
	requestConfig: {
		timeout: 1000, //request timeout in milliseconds
		noDelay: true, //Enable/disable the Nagle algorithm
		keepAlive: true, //Enable/disable keep-alive functionalityidle socket.
		keepAliveDelay: 1000 //and optionally set the initial delay before the first keepalive probe is sent
	},
	responseConfig: {
		timeout: 1000 //response timeout
	}
};


var req = client.post("http://remote.site/rest/xml/${id}/method", args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

req.on('requestTimeout', function (req) {
	console.log('request has expired');
	req.abort();
});

req.on('responseTimeout', function (res) {
	console.log('response has expired');

});

//it's usefull to handle request errors to avoid, for example, socket hang up errors on request timeouts
req.on('error', function (err) {
	console.log('request error', err);
});

Setup client to trust self-signed certificate with custom CA chain

In Internet mostly recommend solution to handle self-signed certificate is to just disable verification of server. NEVER DO THAT IN PRODUCTION! You can do that only for development purpose - never in production because it puts great security risk on your business.

However if you are connecting to a known server using self-signed certificate or a company server signed with corporate CA you can easily setup client to trust them and being secured in same moment. So for example certificate chain:

+-- root-CA (self-signed)
|   +-- department-CA (singed with root-CA)
|       +-- domain (signed with department-CA)

a solution is as follow:

var fs = required('fs');
var trustedCertificates = [
fs.readFileSync('/PATH/TO/DOMAIN/CERTIFICATE'),
fs.readFileSync('/PATH/TO/DEPARTMENT/CA'),
fs.readFileSync('/PATH/TO/ROOT/CA')
];

var options = {
	connection: {
		ca: trustedCertificates
	}
};
var client = new Client(options);

Note that for readability format of certificate are skipped as multiple ones are supported.

Follows Redirect

Node REST client follows redirects by default to a maximum of 21 redirects, but it's also possible to change follows redirect default config in each request done by the client

var client = new Client();

// request and response additional configuration
var args = {
	requestConfig: {
		followRedirects:true,//whether redirects should be followed(default,true) 
		maxRedirects:10//set max redirects allowed (default:21)
	},
	responseConfig: {
		timeout: 1000 //response timeout
	}
};

Response Parsers

You can add your own response parsers to client, as many as you want. There are 2 parser types:

  • Regular parser: First ones to analyze responses. When a response arrives it will pass through all regular parsers, first parser whose match method return true will be the one to process the response. there can be as many regular parsers as you need. you can delete and replace regular parsers when it'll be needed.

  • Default parser: When no regular parser has been able to process the response, default parser will process it, so it's guaranteed that every response is processed. There can be only one default parser and cannot be deleted but it can be replaced adding a parser with isDefault attribute to true.

Each parser - regular or default- needs to follow some conventions:

  • Must be and object

  • Must have the following attributes:

    • name: Used to identify parser in parsers registry

    • isDefault: Used to identify parser as regular parser or default parser. Default parser is applied when client cannot find any regular parser that match to received response

  • Must have the following methods:

    • match(response): used to find which parser should be used with a response. First parser found will be the one to be used. Its arguments are:

      1. response:http.ServerResponse: you can use any argument available in node ServerResponse, for example headers
    • parse(byteBuffer,nrcEventEmitter,parsedCallback) : this method is where response body should be parsed and passed to client request callback. Its arguments are:

      1. byteBuffer:Buffer: Raw response body that should be parsed as js object or whatever you need
      2. nrcEventEmitter:client event emitter: useful to dispatch events during parsing process, for example error events
      3. parsedCallback:function(parsedData): this callback should be invoked when parsing process has finished to pass parsed data to request callback.

Of course any other method or attribute needed for parsing process can be added to parser.

// no "isDefault" attribute defined 
var invalid = {
			   "name":"invalid-parser",
			   "match":function(response){...},
			   "parse":function(byteBuffer,nrcEventEmitter,parsedCallback){...}
			 };

var validParser = {
				   "name":"valid-parser",
				   "isDefault": false,
			   	   "match":function(response){...},
			       "parse":function(byteBuffer,nrcEventEmitter,parsedCallback){...},
			       // of course any other args or methods can be added to parser
			       "otherAttr":"my value",
			       "otherMethod":function(a,b,c){...}
				  };			

function OtherParser(name){
	   this.name: name,
	   this.isDefault: false,
	   this.match=function(response){...};
	   this.parse:function(byteBuffer,nrcEventEmitter,parsedCallback){...};
		
}

var instanceParser = new OtherParser("instance-parser");

//valid parser complete example

client.parsers.add({
						"name":"valid-parser",
						"isDefault":false,
						"match":function(response){
							// only match to responses with  a test-header equal to "hello world!"
							return response.headers["test-header"]==="hello world!";
						},							
						"parse":function(byteBuffer,nrcEventEmitter,parsedCallback){
							// parsing process
							var parsedData = null;
							try{
								parsedData = JSON.parse(byteBuffer.toString());
								parsedData.parsed = true;

								// emit custom event
								nrcEventEmitter('parsed','data has been parsed ' + parsedData);

								// pass parsed data to client request method callback
								parsedCallback(parsedData);
							}catch(err){
								nrcEmitter('error',err);
							};						

						});

By default and to maintain backward compatibility, client comes with 2 regular parsers and 1 default parser:

  • JSON parser: it's named 'JSON' in parsers registry and processes responses to js object. As in previous versions you can change content-types used to match responses by adding a "mimetypes" attribute to client options.
var options = {
				mimetypes: {
						json: ["application/json", "application/my-custom-content-type-for-json;charset=utf-8"]
						
					}
				};

var client = new Client(options);				
  • XML parser: it's named 'XML' in parsers registry and processes responses returned as XML documents to js object. As in previous versions you can change content-types used to match responses by adding a "mimetypes" attribute to client options.
var options = {
				mimetypes: {
						xml: ["application/xml", "application/my-custom-content-type-for-xml"]						
					}
				};

var client = new Client(options);

Additionally in this parser there's an attribute "options" where you can customize xml2js parser options. Please refer to xml2js package for valid parser options.

var client = new Client();

client.parsers.find("XML").options= {"explicitArray":false, "ignoreAttrs":true};
  • Default Parser: return responses as is, without any adittional processing.

Parser Management

Client can manage parsers through the following parsers namespace methods:

  • add(parser): add a regular or default parser (depending on isDefault attribute value) to parsers registry. If you add a regular parser with the same name as an existing one, it will be overwritten

    1. parser: valid parser object. If invalid parser is added an 'error' event is dispatched by client.
  • remove(parserName): removes a parser from parsers registry. If not parser found an 'error' event is dispatched by client.

    1. parserName: valid parser name previously added.
  • find(parserName): find and return a parser searched by its name. If not parser found an 'error' event is dispatched by client.

    1. parserName: valid parser name previously added.
  • getAll(): return a collection of current regular parsers.

  • getDefault(): return the default parser used to process responses that doesn't match with any regular parser.

  • clean(): clean regular parser registry. default parser is not afected by this method.

var client = new Client();

client.parsers.add({
				   "name":"valid-parser",
				   "isDefault": false,
			   	   "match":function(response){...},
			       "parse":function(byteBuffer,nrcEventEmitter,parsedCallback){...},
			       // of course any other args or methods can be added to parser
			       "otherAttr":"my value",
			       "otherMethod":function(a,b,c){...}
				  });

var parser = client.parsers.find("valid-parser");

var defaultParser = client.parsers.getDefault();

var regularParsers = client.parsers.getAll();	

client.parsers.clean();			  

Request Serializers

You can add your own request serializers to client, as many as you want. There are 2 serializer types:

  • Regular serializer: First ones to analyze requests. When a request is sent it will pass through all regular serializers, first serializer whose match method return true will be the one to process the request. there can be as many regular serializers as you need. you can delete and replace regular serializers when it'll be needed.

  • Default serializer: When no regular serializer has been able to process the request, default serializer will process it, so it's guaranteed that every request is processed. There can be only one default serializer and cannot be deleted but it can be replaced adding a serializer with isDefault attribute to true.

Each serializer - regular or default- needs to follow some conventions:

  • Must be and object

  • Must have the following attributes:

    • name: Used to identify serializer in serializers registry

    • isDefault: Used to identify serializer as regular serializer or default serializer. Default serializer is applied when client cannot find any regular serializer that match to sent request

  • Must have the following methods:

    • match(request): used to find which serializer should be used with a request. First serializer found will be the one to be used. Its arguments are:

      1. request:options passed to http.ClientRequest: any option passed to a request through client options or request args, for example headers
    • serialize(data,nrcEventEmitter,serializedCallback) : this method is where request body should be serialized before passing to client request callback. Its arguments are:

      1. data:args data attribute: Raw request body as is declared in args request attribute that should be serialized.

      2. nrcEventEmitter:client event emitter: useful to dispatch events during serialization process, for example error events

      3. serializedCallback:function(serializedData): this callback should be invoked when serialization process has finished to pass serialized data to request callback.

Of course any other method or attribute needed for serialization process can be added to serializer.

// no "isDefault" attribute defined 
var invalid = {
			   "name":"invalid-serializer",
			   "match":function(request){...},
			   "serialize":function(data,nrcEventEmitter,serializedCallback){...}
			 };

var validserializer = {
				   "name":"valid-serializer",
				   "isDefault": false,
			   	   "match":function(request){...},
			       "serialize":function(data,nrcEventEmitter,serializedCallback){...},
			       // of course any other args or methods can be added to serializer
			       "otherAttr":"my value",
			       "otherMethod":function(a,b,c){...}
				  };			

function OtherSerializer(name){
	   this.name: name,
	   this.isDefault: false,
	   this.match=function(request){...};
	   this.serialize:function(data,nrcEventEmitter,serializedCallback){...};
		
}

var instanceserializer = new OtherSerializer("instance-serializer");

// valid serializer complete example

client.serializers.add({
						"name":"example-serializer",
						"isDefault":false,
						"match":function(request){
							// only match to requests with  a test-header equal to "hello world!"
							return request.headers["test-header"]==="hello world!";
						},							
						"serialize":function(data,nrcEventEmitter,serializedCallback){
							// serialization process
							var serializedData = null;

							if (typeof data === 'string'){
								serializedData = data.concat(" I'm serialized!!");
							}else if (typeof data === 'object'){
								serializedData = data;
								serializedData.state = "serialized"
								serializedData = JSON.stringify(serializedData);
							}

							nrcEventEmitter('serialized','data has been serialized ' + serializedData);
							// pass serialized data to client to be sent to remote API
							serializedCallback(serializedData);

						}
	
})

By default client comes with 3 regular serializers and 1 default serializer:

  • JSON serializer: it's named 'JSON' in serializers registry and serialize js objects to its JSON string representation. It will match any request sent exactly with the following content types: "application/json","application/json;charset=utf-8"

  • XML serializer: it's named 'XML' in serializers registry and serialize js objects to its XML string representation. It will match any request sent exactly with the following content types: "application/xml","application/xml;charset=utf-8","text/xml","text/xml;charset=utf-8"

Additionally in this parser there's an attribute "options" where you can customize xml2js serializer options. Please refer to xml2js package for valid builder options.

var client = new Client();

client.serializers.find("XML").options={"renderOpts":{"pretty": true }};
  • URL ENCODE serializer: it's named 'FORM-ENCODED' in serializers registry and serialize js objects to its FORM ENCODED string representation. It will match any request sent exactly with the following content types: "application/x-www-form-urlencoded","multipart/form-data","text/plain"

  • Default serializer: serialize request to its string representation, applying toString() method to data parameter.

serializer Management

Client can manage serializers through the following serializers namespace methods:

  • add(serializer): add a regular or default serializer (depending on isDefault attribute value) to serializers registry.If you add a regular serializer with the same name as an existing one, it will be overwritten

    1. serializer: valid serializer object. If invalid serializer is added an 'error' event is dispatched by client.
  • remove(serializerName): removes a serializer from serializers registry. If not serializer found an 'error' event is dispatched by client.

    1. serializerName: valid serializer name previously added.
  • find(serializerName): find and return a serializer searched by its name. If not serializer found an 'error' event is dispatched by client.

    1. serializerName: valid serializer name previously added.
  • getAll(): return a collection of current regular serializers.

  • getDefault(): return the default serializer used to process requests that doesn't match with any regular serializer.

  • clean(): clean regular serializer registry. default serializer is not afected by this method.

var client = new Client();

client.serializers.add({
						"name":"valid-serializer",
						"isDefault":false,
						"match":function(request){
							// only match to requests with  a test-header equal to "hello world!"
							return request.headers["test-header"]==="hello world!";
						},							
						"serialize":function(data,nrcEventEmitter,serializedCallback){
							// serialization process
							var serializedData = null;

							if (typeof data === 'string'){
								serializedData = data.concat(" I'm serialized!!");
							}else if (typeof data === 'object'){
								serializedData = data;
								serializedData.state = "serialized"
								serializedData = JSON.stringify(serializedData);
							}

							nrcEventEmitter('serialized','data has been serialized ' + serializedData);
							// pass serialized data to client to be sent to remote API
							serializedCallback(serializedData);

						});

var serializer = client.serializers.find("valid-serializer");

var defaultParser = client.serializers.getDefault();

var regularSerializers = client.serializers.getAll();	

client.serializers.clean();			  

Connect through proxy

Just pass proxy configuration as option to client.

var Client = require('node-rest-client').Client;

// configure proxy
var options_proxy = {
	proxy: {
		host: "proxy.foo.com",
		port: 8080,
		user: "proxyuser",
		password: "123",
		tunnel: true
	}
};

var client = new Client(options_proxy);

client has 2 ways to connect to target site through a proxy server: tunnel or direct request, the first one is the default option so if you want to use direct request you must set tunnel off.

var Client = require('node-rest-client').Client;

// configure proxy
var options_proxy = {
	proxy: {
		host: "proxy.foo.com",
		port: 8080,
		user: "proxyuser",
		password: "123",
		tunnel: false // use direct request to proxy
	}
};

var client = new Client(options_proxy);

Basic HTTP auth

Just pass username and password or just username, if no password is required by remote site, as option to client. Every request done with the client will pass username and password or just username if no password is required as basic authorization header.

var Client = require('node-rest-client').Client;

// configure basic http auth for every request
var options_auth = { user: "admin", password: "123" };

var client = new Client(options_auth);

Options parameters

You can pass the following args when creating a new client:

var options = {
	// proxy configuration
	proxy: {
		host: "proxy.foo.com", // proxy host
		port: 8080, // proxy port
		user: "ellen", // proxy username if required
		password: "ripley" // proxy pass if required
	},
	// aditional connection options passed to node http.request y https.request methods 
	// (ie: options to connect to IIS with SSL)	
	connection: {
		secureOptions: constants.SSL_OP_NO_TLSv1_2,
		ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
		honorCipherOrder: true
	},
	// will replace content-types used to match responses in JSON and XML parsers
	mimetypes: {
		json: ["application/json", "application/json;charset=utf-8"],
		xml: ["application/xml", "application/xml;charset=utf-8"]
	},
	user: "admin", // basic http auth username if required
	password: "123", // basic http auth password if required
	requestConfig: {
		timeout: 1000, //request timeout in milliseconds
		noDelay: true, //Enable/disable the Nagle algorithm
		keepAlive: true, //Enable/disable keep-alive functionalityidle socket.
		keepAliveDelay: 1000 //and optionally set the initial delay before the first keepalive probe is sent
	},
	responseConfig: {
		timeout: 1000 //response timeout
	}
};

Note that requestConfig and responseConfig options if set on client instantiation apply to all of its requests/responses and is only overriden by request or reponse configs passed as args in method calls.

Managing Requests

Each REST method invocation returns a request object with specific request options and error, requestTimeout and responseTimeout event handlers.

var Client = require('node-rest-client').Client;

var client = new Client();

var args = {
	requesConfig: { timeout: 1000 },
	responseConfig: { timeout: 2000 }
};

// direct way
var req1 = client.get("http://remote.site/rest/xml/method", args, function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// view req1 options		
console.log(req1.options);


req1.on('requestTimeout', function (req) {
	console.log("request has expired");
	req.abort();
});

req1.on('responseTimeout', function (res) {
	console.log("response has expired");

});


// registering remote methods
client.registerMethod("jsonMethod", "http://remote.site/rest/json/method", "GET");

var req2 = client.methods.jsonMethod(function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
});

// handling specific req2 errors
req2.on('error', function (err) {
	console.log('something went wrong on req2!!', err.request.options);
});

Error Handling

Now you can handle error events in two places: on client or on each request.

var client = new Client(options_auth);

// handling request error events
client.get("http://remote.site/rest/xml/method", function (data, response) {
	// parsed response body as js object
	console.log(data);
	// raw response
	console.log(response);
}).on('error', function (err) {
	console.log('something went wrong on the request', err.request.options);
});

// handling client error events
client.on('error', function (err) {
	console.error('Something went wrong on the client', err);
});

NOTE: _Since version 0.8.0 node does not contain node-waf anymore. The node-zlib package which node-rest-client make use of, depends on node-waf.Fortunately since version 0.8.0 zlib is a core dependency of node, so since version 1.0 of node-rest-client the explicit dependency to "zlib" has been removed from package.json. therefore if you are using a version below 0.8.0 of node please use a versiรณn below 1.0.0 of "node-rest-client". _

node-rest-client's People

Contributors

aacerox avatar adamiak avatar brucexin avatar cnorthwood avatar connorhu avatar elsabo avatar flawless2011 avatar grawk avatar hb9cwp avatar jetume avatar jl102 avatar jwindmuller avatar konczak avatar leedm777 avatar ls-pluriels avatar moander avatar simenb avatar sonyl avatar stephanbertl avatar suattaapiolla avatar wrobbins avatar wwwdata avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-rest-client's Issues

"InternalError: Unexpected token <" returned, even using on('error', func... for HTML content

When calling a resourceURL that is NOT XML nor JSON, but HTML, the client breaks with the following message:

{
    code: "InternalError",
    message: "Unexpected token <"
}

I believe this is because the server is trying to parse an HTML page at the time of the request... I can't see the HTTP Response header and I can't guarantee there were any of the application/json or application/xml, but the fact is that I can't quietly handle this error using the "on('error'"....

self.client.get(resourceUrl, function(responseBody, response) {
    deferred.resolve(JSON.parse(responseBody));

}).on('error',function(err){
    console.log('something went wrong on the request' + err, err.request.options);
    deferred.resolve({status: "ERROR", message: err});
});

Shouldn't the data be returned to the client API in Strings, instead of trying to parse the content? That way, the user can also verify the content type of the response, as it is never guaranteed that the server is serving the content...

Any suggestions for workarounds? My package.json declares the latest version:

"node-rest-client": "^0.8.3",

Thanks
Marcello

Content-Type detection problem

Hi,

creating a simple REST-Service with express and using this lib I encountered the problem, that the result is not parsed as JSON although one is returned and the content type was set.

Currently the code checks for

"jsonctype":["application/json","application/json;charset=utf-8"]

but express created application/json; charset=utf-8, simply adding a third element in the list is helping in that case or changing the detection with a split and comparing each element after having trimmed it.

If you wish I can drop a PR later, depending which solution you prefer or maybe you have another one in mind.

Kind regards,

Michael

Unable to use data if it is not parsed

In order to make that code to work:

var sys = require('util');
var Client = require('node-rest-client').Client;

var client = new Client();

var args ={
path:{"lat":-31.32622,"lon":-64.241352}
};

client.get("https://search.twitter.com/search.json?geocode=${lat},${lon},1mi", args,
function(data, response){
console.log(data.completed_in);
//console.log(response);
}
);

I needed to change that line in the library:
Change:
}else if (self.isJSON(content)){
debug("typeof data es ", typeof data);
callback(JSON.parse(data), res);
}else{
callback(data, res);
}

By:
}else if (self.isJSON(content)){
debug("typeof data es ", typeof data);
callback(JSON.parse(data), res);
}else{
callback(JSON.parse(data), res);
}

Bug introduced

/root/razvan/bc-olympia/node_modules/node-rest-client/lib/node-rest-client.js:14
    self.useProxyTunnel = self.options.proxy.tunnel===undefined?true:self.options
                                            ^
TypeError: Cannot read property 'tunnel' of undefined
    at new exports.Client (/root/razvan/bc-olympia/node_modules/node-rest-client/lib/node-rest-client.js:14:42)
    at Object.module.exports.generateService (/root/razvan/bc-olympia/services/player-server.js:4:18)
    at Object.<anonymous> (/root/razvan/bc-olympia/server.js:34:58)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)

It seems like the last commit broke something

json parsing of response

To perform JSON parsing of response, the code checks of "jsonctype":["application/json","application/json;charset=utf-8"], in response header.

Its failing for me becuase in my case. the rest endpoint returns content header as "application/json; charset=utf-8" (notice the space between encoding directive).

May be the better approach would be to split the string and look for "application/json"

Problem with example

Hi,

I am trying to replicate the example in your Readme file but with no success.

This is my JS code:

function callAPI(){
    var client = require('node-rest-client').Client;

    // direct way
    var client = new exports.Client();


    client.post("http://127.0.0.1:8081/daqvalidator/validate?type=m", function(data, response){
                // parsed response body as js object
                console.log(data);
                // raw response
                console.log(response);
    });

    // registering remote methods
    client.registerMethod("xmlMethod", "http://127.0.0.1:8081/daqvalidator/validate", "POST");


    client.methods.xmlMethod(args,function(data,response){
        // parsed response body as js object
        console.log(data);
        // raw response
        console.log(response);
    });
}

When trying to call the function, I get the following error:
Uncaught Error: Module name "node-rest-client" has not been loaded yet for context: _. Use require([]) http://requirejs.org/docs/errors.html#notloaded

Would you have any idea why this could be happening?

Best Regards,
Jeremy

Handling compression

Hi there,

I'm looking for some guidance on how to handle compressed responses.
Can you provide an example on how to gunzip the response data?

Thanks in advance

Tim

Add support for promises

Would be great to support Promise interface and use client this way:

client.get("/foo").then(...);

Documentation, missing constant

connection:{
secureOptions: constants.SSL_OP_NO_TLSv1_2,
ciphers:'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true
},

when trying to use the client with GET and SSL, using the above, constants <--- is not defined.

Force parse json or xml if no Content-Type in response

Unfortunately there are some services that I have to use which do not specify Content-Type header in the response.
Now It is pared as expected neither to json nor to xml.
Could you please add some option that allows to force to some Content-Type (or direct treat respose as json or xml) if no Content-Type in response.

Error in isXML function of ConnectManager: .toLowerCase on undefined

I had node-rest-client included in my dependencies as latest version, and when I 'npm install'ed today, all of my test cases for one of my projects broke. I started getting errors with a call to .toLowerCase, which actually turned out to be within the node-rest-client isXML function.

As it turns out, not all of my requests to a Restify API actually respond with a content type, because not all of the responses actually have content. When there is no content in a response, the following line 285 in node-rest-client.js in v0.8.8 fails:

result = this.xmlctype[i].toLowerCase() === content.toLowerCase();

I reverted back to v0.8.6, which has this code instead:

result = this.xmlctype[i] === content;

That code works, since it is not trying to call .toLowerCase() on an undefined object. I figure just checking if (content) first, before assigning the result, would clear the issue up.

Regression due to gzip/zlib support

The REST API server returns uncompressed JSON to my node-rest-client which fails to capture and return that JSON data since compression support was added.

My app works fine again if I replace the line if(!encoding || /^\s*$/.test(encoding)){ by if(1). Running with DEBUG shows that encoding= UTF-8. Also the second expression after the || evaluates to false.

Still trying to understand what /^\s*$/.test(encoding) is supposed to do, in order to eventually propose a patch. Any hints? Thanks.

HTTP Basic Auth Not Working

When using node-rest-client with the PushBullet API (just as a test), the http basic auth headers are not properly passed to the server.

var restClient = require('node-rest-client').Client;

var accessToken = "<access code here>";
var auth = { user: accessToken, password: "" };

var rest = new restClient(auth);


var getAvailableDevices = function(callback)
{
    var url = "https://api.pushbullet.com/v2/devices";

    rest.get(url, function(data, response)
    {
        //response gives "Access token is missing or invalid." error
    });
}

The access token is tested and valid with a curl request

Please advise

How to see the data when using node-rest-cliente

Hi,
Iยดm a begginer and i think i have a basic doubt:

When i run the following code in the internet explorer:
http://route.st.nlp.nokia.com/routing/6.2/calculateisoline.json?mode=fastest%3Bcar%3Btraffic%3Adisabled&start=19.419426%2C+-99.217170&time=PT0H01M&app_id=DemoAppId01082013GAL&app_code=AJKnXv84fjrb0KIHawS0Tg

The response is a file in json with the information i need of latitude and longitude points

But when i execute mi node.js application with node-rest-client the only thing i get is the following:

C:>node here.js
{ Response:
{ isolines: [ [Object] ],
MetaInfo:
{ Timestamp: '2014-06-25T23:55:14.472Z',
AdditionalData: [Object] },
Center: { Latitude: 19.4191952, Longitude: -99.2172394 } } }

Why in the isoline part only says [[Object]] and i cant see the same points of the first json file.
Thanks a lot!

Non-tunneled proxy requests not always working

Non-tunneled proxy requests do not always lead to a successful response.

I have narrowed the issue down to the absence of a host header in the request.
It appears there is support for the host header, and it is initially filled correctly. Later on it gets deleted from the options though, leaving the request without the header entirely.

Not getting response with keep-alive

Hi there,

Whenever I'm issuing a http get request with keep-alive enabled I don't seem to be receiving an end or close event & the socket remains open. If I change the Connection header to be close it works fine.

Code snippet below. Any suggestions would be greatly appreciated.

client.get(url, args, function(data, response)
{
// Get response details
localrc = response.statusCode;
localsessKey = response.headers['my-session-key'];
if (doAuth == "yes")
{
if (typeof localsessKey == "undefined")
{
localrc = 1;
localmsg = "Session Key not retrieved";
}
}
}).on('end', function()
{
callback(localrc, localmsg, localsessKey, data);
}).on('close', function()
{
callback(localrc, localmsg, localsessKey, data);
}).on('error', function(err)
{
localrc = response.statusCode;
localmsg = err;
callback(localrc, localmsg, localsessKey, data);
});

Doesn't recognize Content-Type header set by Express

Express sets Content-Type as 'application/json; charset=utf-8' but the module has hardcoded the Content-Type as 'application/json;charset=utf-8'. Please add one with a space or use indexOf to look for application/json instead.

Appears not to parse JSON

Maybe I am doing something stupid, but it looks like the data is not even parsed:

var Client = require('node-rest-client').Client;
client = new Client();
client.get('http://localhost:5050/workbooks/TotalDailyGrossRevenue/last-refresh-time', function(data, response){
    console.log('Data is a', typeof data);
});

Results in:

$ node scripts/test_client.js
Data is a string

Curling looks fine:

$ curl -v http://localhost:5050/workbooks/TotalDailyGrossRevenue/last-refresh-time
* About to connect() to localhost port 5050 (#0)
*   Trying ::1...
* Connection refused
*   Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 5050 (#0)
> GET /workbooks/TotalDailyGrossRevenue/last-refresh-time HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5
> Host: localhost:5050
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 555
< ETag: "1044588208"
< Date: Wed, 19 Feb 2014 19:40:37 GMT
< Connection: keep-alive
<
{
  "id": "TotalDailyGrossRevenue",
  "worksheets": [
    {
      "name": "2013 Goal Tracking Data"
    },
    {
      "name": "2013 Goal Tracking Graph"
    },
    {
      "name": "Best Day"
    },
    {
      "name": "By Product"
    },
    {
      "name": "Data Table"
    },
    {
      "name": "Total"
    },
    {
      "name": "2013 Revenue Goal Tracker"
    },
    {
      "name": "Data"
    },
    {
      "name": "Gross Revenue by Product"
    },
    {
      "name": "Total Gross Revenue"
    }
  ],
  "lastRefresh": "2014-02-19T18:49:45.085Z"
* Connection #0 to host localhost left intact
}* Closing connection #0

use node debug for debug statements

Looks like this project checks if there is any value for process.env.DEBUG, which causes a lot of clutter when trying to debug other parts of the app unrelated to this project.

It would be awesome if you guys could use node debug or check for the a namespaced value instead of just existance in a fashion similar to node debug so it is easier to get fine grained debug statments.

Impossible to match error and request

In case if you subscribe on error event:

client.on('error',function(err){
console.error('Something went wrong', err);
});

It's unfortunately impossible to match which request generated this error. It'd be really helpful when you need to react somehow inside this request and invoke error callback to its originator.

Any idea how to make it work?
May be it makes sence to implement some error callback inside get(), post() etc. functions to catch the error?

delete method is not working well

I'm building this request

DELETE /rest/service/5117 HTTP/1.1
Host: localhost:8080
Authorization: Basic bXl1c2VyOm15cGFzc3dvcmQ==
Cache-Control: no-cache
var Client = require('node-rest-client').Client

var defaultOptions = {
    headers: [{"Content-Type": "application/json"}],
    user: 'myuser',
    password: 'mypassword',
    mimetypes: {
        json: ["application/json", "application/json;charset=utf-8"]
    }
};

var client = new Client(defaultOptions);

client.delete('http://localhost:8080/rest/service/5117', function(data, response) { ... }); // always returns 400

if i use the module request works fine

var options = {
        method: 'DELETE',
        uri: 'http://localhost:8080/rest/service/5117',
        headers: {"Content-Type": "application/json", "Authorization": "Basic bXl1c2VyOm15cGFzc3dvcmQ=="}
};

    request(options, function(err, response, body) {....}); // works fine!

CreateConnectOptions not setting protocol, subsequently assumed to be https

I was getting SSL errors when trying to use the url http://localhost:49664/v1

It looks like the options created in createConnectOptions() don't set a protocol (they do set host, port, path) and so subsequent tests of the options in "normal" and "proxy" assume the request should use https.

from createConnectOptions()

 result ={
            host: url.host.indexOf(":") == -1?url.host:url.host.substring(0,url.host.indexOf(":")),
            port: url.port === undefined?defaultPort:url.port,
            path: url.path
   };

from "normal":function()

 protocol = (options.protocol =="http")?http:https,

add support for promises

I'd really love to see support for Promises. Currently it's getting tedious to chain multiple calls. Support for bluebird promises would be be really nice.

Accessing data details in data js object

Hello,

I am trying to show some log message when a post successfully completes and I am using the data
object passed to the POST callback. If I log the whole of data, then it shows ok, if I log only the data.name property , then it shows undefined. I include the code below.

If this the expected behaviour?

var Client = require('node-rest-client').Client;
var client = new Client();
var args = {
data: { name: "Jose",
fname: "Perez",
email: "[email protected]" , },
headers:{"Content-Type": "application/json"}
};
//console.log(i, rows[i].name);

client.post("http://localhost:3000/player", args, function(data,response) {

// This shows undefined ????
console.log(data.name);

// This shows OK
// {"id":10,"name":"Jose",....}
console.log(data);

});

Content-Type: "application/json; charset=utf-8"

We have modified this part of your code (node-rest-client.js):


296 "isJSON":function(content){
297 content = content.replace(' ', '');


because our restful server replies with a space between chars ';' and 'charset ....':
"application/json; charset=utf-8"
but your code expects:
"application/json;charset=utf-8"

In general your code does not support spaces in the 'Content-Type' response header.

Socket Parse Error

When I try to GET certain URLs, I receive the following error:

Error: Parse Error
    at Socket.socketOnData (http.js:1583:20)
    at TCP.onread (net.js:527:27)

This happens reproducibly when for instance loading Amazon pages:

var rest = new (require('node-rest-client')).Client();

rest.get('http://www.amazon.com/kids-toys-action-figures-accessories/b?ie=UTF8&node=165993011', function (err, response) {
    if (response.statusCode != 200) throw new Error("Response: #{response.statusCode}");
    console.log('done.');
}).on('error', function (err) {
    console.error(err.stack);
});

I could not find anything unusual in the response from the server:

$ curl -X GET -I 'http://www.amazon.com/kids-toys-action-figures-accessories/b?ie=UTF8&node=165993011'
HTTP/1.1 200 OK
Date: Wed, 08 Oct 2014 11:43:15 GMT
Server: Server
x-amz-id-1: 0PX51KDS197ANZRNQCY9
x-amz-id-2: to2jzdxj+C8EUuRJgeEvCrdlThmPkzqiGsxQO1UeLx7VJzEndp280sQmfoIH48M0NsMbA7vGUR8=
X-Frame-Options: SAMEORIGIN
Content-Type: text/html;charset=UTF-8
Vary: Accept-Encoding,User-Agent
Transfer-Encoding: chunked

Is there anything I can do?

Error response not caught by error handler

When getting a 500 or 502 from an API request, the callback is invoked instead of the error handler. What type of incidents are actually meant to invoke the error handler, if not these are? Am I missing something here?

My code just follows the examples from README:

var req = client.get(url, args, function(data,response) {
     // response.statusCode == 500
     // Shouldn't the error handler below deal with this? 
});
req.on('error', function(err) {
    logger.error("Request failed", err);
})

Parse Error when using REST client behind proxy

Hi,

I have experienced an error throwing from Socket.socketOnData method when calling REST client behind a proxy. Here is the code I used:

var RestClient = require('node-rest-client').Client, 
    client = new RestClient({
        proxy:{
            host:"proxyDomain",
            port: 8080,
            user:"username",
            password:"password"
        }
    });

client.registerMethod("jsonMethod", "http://ogcef.one.gov.hk/event-psi/json/calendar/20140215.json", "GET");

client.methods.jsonMethod(function (data, response) {...});

After checking up the REST client code a bit, I found that it will try to connect to proxy via 'CONNECT' method using Node.js http client and then making another connection after this call returned successfully. The issue is that if you use Node.js http client to trigger a call using 'CONNECT' method, the above exception is thrown.

May I suggest to use something similiar below so that REST client can connect to the target URL successfully behind a proxy environment? I have tested in my local and it works:

var request = http.request({
    host: 'proxy',
    port: '8080',
    method: 'GET',
    path: 'http://ogcef.one.gov.hk/event-psi/json/calendar/20140215.json',
    headers: {
        'host': 'ogcef.one.gov.hk',
        'Proxy-Authentication': 'Basic ' + new Buffer(['username','password'].join(":")).toString("base64")
    },
    agent: false
}); 

Is it possible to change ConnectManager.proxy function to use the approach above instead of making a CONNECT call?

Thanks and this package is an awesome package for Node.js :)

Sincerely,
Jason

Unable to instanciate simple Client without Proxy

There is no way to create a simple Client without Proxy.
Seems to be caused by one of the last commits. Error:

TypeError: Cannot read property 'tunnel' of undefined
at new exports.Client (C:\nodejs\node_modules\node-rest-client\lib\node-rest-client.js:14:42)
at Object. (C:\nodejs\testapp\restclient.js:3:14)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3

POST not working?

Hi

I'm having trouble getting POST methods to work - the request arrives at my API, but the req.body element is an empty object (even though I have specified args.data). GET works fine.

I tried with both direct and registered methods.

My API is implemented using express and is currently running on localhost:5000. The API works if I use an HTML form to post...

Any ideas what I might be missing?

Thanks,

Toby

Exception caused by empty response and `Content-Type: application/json`

When those two conditions are met (server sends an empty response, eg. with status 204 NO CONTENT and with response header Content-Type set to application/json) the library will generate an exception. This is caused by function isJSON() that does the check basing only on the Content-Type header. Then, JSON.parse() crashes when trying to parse empty string.

Mocking Client in unit tests

I'm using node-rest-client for my project and I've come to the point where I want to start unit testing my classes that utilize node-rest-client. Is there any examples on how I can mock the Client in my tests? For example, my class might look like this:

SomeClass.prototype.execute = function(orderId)
{
    var deferred = q.defer();
    var self = this;

    this.client.get(endpoint + '/orders/' + orderId, args, function(responseData, response)
    {
        if (response.statusCode === 200)
        {
            deferred.resolve(responseData);
        }
        else
        {
            deferred.reject(new Error('Error occurred'));
        }
    })

    .on('error', function(error)
    {
            deferred.reject(new Error('Error occurred'));
    });

    return deferred.promise;
};

this.client is a Client instance, and I'm using q for promises.

How can I easily mock the clinet so I reach the three scenarios? The three scenarios are:

  • Successful HTTP request with statusCode 200
  • Successful HTTP request with statusCode anything else than 200
  • Unsuccessful HTTP request (which leads to the .on('error', ...) handler

Simple read request failing with ECONNRESET

var docs = function (app) {
var container;
var Client = require('node-rest-client').Client;

client = new Client();

// handling request error events
client.get("http://VM-MOSS01-TEST.cco.cco-cce.org:80/operations/NOPA/_vti_bin/listdata.svc/Certifications?$select=Certification,CertDescription,Id", function(data, response){
// parsed response body as js object
console.log(data);
container = data;
// raw response
console.log(response);
}).on('error',function(err){
//console.log('something went wrong on the request', err.request.options)
console.dir(err);
});

/*

app.get('/api/docs', function (req, res) {
    res.json(container);
});

};

module.exports = docs;


on "error" Ouput
{ [Error: read ECONNRESET]
code: 'ECONNRESET',
errno: 'ECONNRESET',
syscall: 'read',
request:
{ domain: null,
_events: { error: [Function] },
_maxListeners: 10,
options:
{ host: 'vm-moss01-test",
port: '80',
path: '/operations/NOPA/_vti_bin/listdata.svc/Certifications?$select=Certification,CertDescription,Id',
method: 'GET' } } }

Any suggestions on how to troubleshoot this?

JSON type not detected properly

On of the services my app relies upon suddenly changed the mime type to application/json;odata=verbose;charset=utf-8 (probably some redmondish attempt to screw up something not invented by them).

As a result the body of the message is not interpreted as JSON anymore. So we should either match this obscure mimetype, or match on a substring.

possible EventEmitter memory leak detected

This is a warning that an EventEmitter instance has more than the configured listeners set. By default that's 10 with node.

I have some client wrapper classes that call node-rest-client. One in particular is an authentication client.

The "core" services I access through REST all call the auth client first. Given that there are multiple core service calls per module functionality, the auth client can get called more than 10x while serving a user request, each going further down the promise chain. This means that node-rest-client can ultimately be invoked many times before everything unwinds.

I'm pretty sure the Event Emitter warning (and associated stack trace shown below) occurs in node-rest-client but can't figure out how to increase its event emitter listener configuration.

Trace
at Object.addListener (events.js:179:15)
at new exports.Client (E:\src\clients\client1\ztarmobile\rest\node_modules\node-rest-client\lib\node-rest-client.js:326:17)
at RestClientBase.rest (E:\src\clients\client1\ztarmobile\rest\server\components\rest-client\RestClientBase.js:154:30)
at RestClientBase.AuthClient.authorizeResource (E:\src\clients\client1\ztarmobile\rest\server\components\rest-client\AuthClient.js:52:21)
at Object.module.exports.authorizeProductCatalogEntry (E:\src\clients\client1\ztarmobile\rest\server\components\service\authorize.service.js:14:27)
at E:\src\clients\client1\ztarmobile\rest\server\components\service\authorize.service.js:45:47
at _fulfilled (e:\src\clients\client1\components\node_modules\q\q.js:794:54)
at self.promiseDispatch.done (e:\src\clients\client1\components\node_modules\q\q.js:823:30)
at Promise.promise.promiseDispatch (e:\src\clients\client1\components\node_modules\q\q.js:756:13)
at e:\src\clients\client1\components\node_modules\q\q.js:564:44

Error using https protocol with REST Client

I'm trying to call Client using the next code:

Client = require('node-rest-client').Client;
var options = { protocol: "https"};
var client = new Client(options);

But I'm receiving the error below:
http.js:1604
throw new Error('Protocol:' + options.protocol + ' not supported.');
^
Error: Protocol:https: not supported.
at Object.exports.request (http.js:1604:11)

Connection reset when post request in for loop

for(var i=0; i <50; i++)
{

var client = new Client();
var args = {
  data: data,
  headers:{"Content-Type": "application/json"} 
};

client.post("http://parkloco.com:9999/park", args, function(data,response) {
      // parsed response body as js object
  console.log(response);

});

}

The connection is reset by the node app. In unirest node api, it works perfectly fine!

HTTPS request with self signed certificate on the server

If I use the HTTPS client built into node library, I can pass "rejectUnauthorized" to false and node will happily make https request to server with self signed certificate. How can I do this with node-rest-client? I could not see this in the source code.

Allow set rejectUnauthorized

It doesn't look like it is possible to set rejectUnauthorized in the options. This would definitely be helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.