Giter Site home page Giter Site logo

gax-nodejs's Introduction

Google Inc. logo

Google API Extensions for Node.js

Release Level npm version

Google API Extensions for Node.js (gax-nodejs) is a set of modules which aids the development of APIs for clients and servers based on gRPC and Google API conventions.

Application code will rarely need to use most of the classes within this library directly, but code generated automatically from the API definition files in Google APIs can use services such as page streaming and request bundling to provide a more convenient and idiomatic API surface to callers.

Installation

$ npm install google-gax

Supporting older version of Node.js

This library uses grpc-js package for communicating with API server, and it uses HTTP/2 functionality that is only available in Node.js v8.13.0 or newer. If you need to use this library with older versions of Node.js, you need to make your code depend on a legacy gRPC library (grpc) and pass the instance of gRPC to the client constructor:

const grpc = require('grpc');
const client = new APIClient({ grpc }); // APIClient is the client class you use, e.g. SpeechClient, etc.

Contributing

Contributions to this library are always welcome and highly encouraged. See the CONTRIBUTING documentation for more information on how to get started.

Details

For detailed documentation of the modules in gax-nodejs, please check out the docs.

License

BSD - See LICENSE for more information.

gax-nodejs's People

Contributors

alexander-fenster avatar alicejli avatar avaksman avatar bcoe avatar callmehiphop avatar crwilcox avatar danieljbruce avatar dpebot avatar feywind avatar fhinkel avatar gcf-owl-bot[bot] avatar greenkeeper[bot] avatar jkwlui avatar jmuk avatar justinbeckwith avatar kjin avatar landrito avatar leahecole avatar lukesneeringer avatar ramya-ramalingam avatar release-please[bot] avatar renovate-bot avatar renovate[bot] avatar saicheems avatar schmidt-sebastian avatar sofisl avatar stephenplusplus avatar summer-ji-eng avatar xiaozhenliu-gg5 avatar yoshi-automation 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  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

gax-nodejs's Issues

Do not return Promise when callback is supplied

ApiCallable returns a Promise always, but that's probably not great. When callback is specified, do not return Promise itself, just pass everything to the callback and returns nothing (i.e. undefined).

Update construct_settings to support overriding from JSON

Based on the discussion in googleapis/gax-python#95, we are going to accept a parameter config_overrides to construct_settings, and retire retrying_overrides and bundling_overrides.

config_overrides is in the same structure as client_config is, and expected to be supplied from the API users to override the config. Typically, I expect that users will copy the default JSON config file and modify it and pass it.

The returned config has to fall back to the default config, so the config_overrides can be a subset of client_config. The new construct_settings will merge these configs before constructing the config.

See also: googleapis/gax-python#103

Reconsider the interface to suppress page-streaming for an invocation

Right now, the paged responses will be unrolled into a single stream (i.e. page-streaming), but we allow to suppress this by specifying page_token into the call options (for the first page, specify FIRST_PAGE object).

The sample usage would be:

api.listLogEntries([projectId], new gax.CallOptions({page_token: gax.FIRST_PAGE})).on('data', function(resp) {
   // resp is a listLogEntriesResponse.
});

Some random ideas:

  • probably better to avoid creating gax.CallOptions instance. Instead, simply passing an object would be better.
    • the current design can come with a complication, because the generated class will notice about gax.CallOptions instance through arguejs, and arguejs could fail to recognize an argument if multiple installations of gax exists in the user environment.
  • instead of having gax.FIRST_PAGE, accepting both autoPaginate boolean flag and pageToken flag might be more JS-ish?
  • could it be a normal callback style instead of a stream of response object?
    • it was, but I switched to this design because changing the response type upon the argument style might not be good.

Remove `bind`, use closure, if possible

I got internal feedback saying that bind is really slow when version < 7, that would justify replacing bind by closures. bind might be justified when it's much clearer and not used so repeatedly.

Allow other implementations of Promise

Currently it uses the builtin Promise as-is, it is not configurable at all, but sometimes users may prefer others like Bluebird. It would be great if it's configurable. (see also: #71)

error code handling and conversion

Right now GAX returns the error which gRPC returns. It has error message and grpc error code.

@google-cloud/common package has the logic to map the error code to HTTP status code to handle the failures more universally -- we want to port it to GAX layer as well.

EventEmitter2 Module Causing Issues with Typescript

The EventEmitter2 package contains a Typescript definition file which causes compilation failure when using the current version of Typescript.

A fix was pushed in #2.0.2

Can the package.json reference be updated?

Expose a method to create an Operation.

What

Having a way to create an gax.Operation object from an operation name would help the google-cloud-node guys move forward in eliminating their grpcOperation object.

Stop unhandled promise rejection when callback is specified

When the callback is provided and something went wrong on the API, the errors should be provided to the callback, and that should not cause promise rejection (because the callback is called as a part of Promise -- see https://github.com/googleapis/gax-nodejs/blob/master/lib/api_callable.js#L74).

It's also better to clarify their relationship probably. Currently when an error is thrown in the callback, the error will be captured within the promise, which would be unexpected.

Allow other types of authentication

Right now GAX uses 'getApplicationDefault' function of google-auth-library, it might not be so obvious how to specify a customized authentication method, and might not be so consistent with configurations with gcloud-node.

GAX should allow customizing the authentication function itself.

Unboxing error objects

Sorry if I already wrote an issue about this somewhere... I can't find it

This is how an error looks currently (borrowed from here):

{ Error: Exception occurred in retry method that was not classified as transient
    at Object.callback (/home/ricardoungureanu/speech-to-text-api/node_modules/google-gax/lib/api_callable.js:147:19)
    at /home/ricardoungureanu/speech-to-text-api/node_modules/grpc/src/node/src/client.js:420:14
  cause: 
   { Error: {"created":"@1479751616.944318983","description":"Secure read failed","file":"../src/core/lib/security/transport/secure_endpoint.c","file_line":157,"grpc_status":14,"referenced_errors":[{"created":"@1479751616.944288215","description":"EOF","file":"../src/core/lib/iomgr/tcp_posix.c","file_line":235}]}
       at /home/ricardoungureanu/speech-to-text-api/node_modules/grpc/src/node/src/client.js:417:17 code: 14, metadata: Metadata { _internal_repr: {} } } }
    at Promise.all.then.all.then.all.then.catch (/home/ricardoungureanu/speech-to-text-api/src/app.js:186:32)
    at tryCatcher (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/promise.js:687:18)
    at Async._drainQueue (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/async.js:138:16)
    at Async._drainQueues (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/async.js:148:10)
    at Immediate.Async.drainQueues (/home/ricardoungureanu/speech-to-text-api/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:637:20)
    at tryOnImmediate (timers.js:610:5)
    at processImmediate [as _immediateCallback] (timers.js:582:5)

The error itself is buried a layer deep, under error.cause:

{ Error: {"created":"@1479751616.944318983","description":"Secure read failed","file":"../src/core/lib/security/transport/secure_endpoint.c","file_line":157,"grpc_status":14,"referenced_errors":[{"created":"@1479751616.944288215","description":"EOF","file":"../src/core/lib/iomgr/tcp_posix.c","file_line":235}]}

That is the error that will help the user debug the most, so I think it should get promoted to being the error, instead of being inside error.cause.

google-cloud-node idioms, for consideration

Originally brought up: googleapis/google-cloud-node#1801 (comment)

google-cloud-node (GCN) has some conventions which ideally could be used here. It would be great to achieve a common UX that can be re-used by a developer when hopping between APIs.

Resource hierarchy

The GCN library layers its classes so that if you need a GCS "Object", for example, you go through 2 parent layers, the GCS class, and then a bucket:

var gcs = require('@google-cloud/storage')({authInfo});
var myBucket = gcs.bucket('my-bucket');
var myFile = myBucket.file('my-file');

myFile.getMetadata(function(err, metadata) {});

I can't speak for certain how this would look with the generated layer, but a simple interpretation would be something like:

var gcs = require('generated-storage-client')({authInfo});

gcs.getObject({
  bucketName: 'my-bucket',
  objectName: 'my-file'
}, function(err, resp) {});

The hierarchy used by GCN is nice because it lets you cache one resource that you intend to make multiple calls with; myFile.delete(), myFile.createReadStream(), etc.

Accessor methods

GCN distinguishes between two types of objects: a "Service" (Google Cloud Storage), and a "ServiceObject" (a Bucket). A consistent set of methods are exposed on a ServiceObject:

  • ServiceObject#create({ config options }, function(err, serviceObjectInstance, apiResponse) {})
  • ServiceObject#delete(function(err, apiResponse) {})
  • ServiceObject#exists(function(err, exists, apiResponse) {})
  • ServiceObject#get(function(err, serviceObjectInstance, apiResponse) {})
  • ServiceObject#getMetadata(function(err, metadata, apiResponse) {})
  • ServiceObject#setMetadata({ new metadata }, function(err, apiResponse) {})

*Methods that don't apply for a specific ServiceObject are removed, e.g. you can't delete a Compute Engine Region, but you can get its metadata.

Streaming methods / naming conventions

The upstream API has its own implementation of logical naming patterns, and in the generated layer, those probably shouldn't be tampered with. However, it might be appreciated by Node.js developers to recognize some names they know from other libraries, for example, createReadStream() and createWriteStream() where there are readable and writable streams.

In GCN's Bigtable API, we expose a createReadStream() to access the proto service's "ReadRows" method.

We've also seen naming conflicts between JavaScript/Node.js definitions and the language-agnostic API terminology. Having a small handwritten map of convenience/conventional names to upstream names might be a big help.

// cc: @bjwatson @jgeewax @callmehiphop @jmdobry

normal APIs should return an event emitter

Right now, normal API returns nothing -- the API invocation will be notified through the callback.

To support the cancellation and keep the consistent behavior with page-streaming, they should return an event emitter -- which should implement 'cancel' method, and emits the response on 'data' event.

Objects over multiple parameters

Another JS pattern favors accepting a configuration option from a user as opposed to multiple parameters, eg:

// currently:
Language.annotateText(document, features, encodingType, options, function(err, resp) {});

// proposed:
Language.annotateText({
  document: document,
  features: features,
  encodingType: encodingType
}, options, function(err, resp) {});

The problems with the multiple parameters:

  • Remembering the order of arguments
  • Length of method call lines quickly exceed the common guideline 80 character rule
  • Conditionally compiling arguments involves creating a temp null variable (demo'd below)
var encodingType = null;

if (encodingTypeDetected) {
  encodingType = encodingTypeDetected;
}

Language.annotateText(document, features, encodingType, options, function(err, resp) {});

// vs.

var request = {
  document: document,
  features: features
};

if (encodingTypeDetected) {
  request.encodingType = encodingTypeDetected;
}

Language.annotateText(request, options, function(err, resp) {});

Would it be possible to have generated methods accept properties this way?

Remove 0.12 from Travis

As googleapis/google-cloud-node#1855 (comment)

Node 0.12 has ended its official support at the end of 2016, and gRPC has stopped its distributions of precompiled binary packages for 0.12. This caused the failures on Travis -- because node tries to compile gRPC and its old C++ compiler fails to deal with some language features there.

Instead of adding new C++ compiler, we should be aligned with google-cloud-node repository, that means:

  • specify the minimum version in the package.json to be 4
  • remove 0.12 from Travis
  • however, this does not mean to drop the support of 0.12. For example, we can't use new ES6 features which were added to Node v4.

Custom Endpoints

Is it possible to set a custom endpoint, for example, if trying to access an emulator?

Support longrunning operations

Some methods in Google APIs are in a pattern of longrunning operations (LRO). In this pattern, the proto defines to return a google.longrunning.Operation object, and Operations service takes care of the status of operations (see https://github.com/googleapis/googleapis/blob/master/google/longrunning/operations.proto).

Within the current code base, the code generator for NodeJS will generate a method which invokes the callback with an instance of Operation, which isn't quite useful. The goal is to allow the following style:

api.longRunningMethod(request, function(err, response, metadata) {
  // err is set when something goes wrong.
  // response is the final result, not an Operation.
  // metadata is the field in Operation object.
});

Or in the Promise style:

api.longRunningMethod(request).then(function(response) {
  // response is the final result.
});

To achieve this, we'll need the following changes:

  • have the GAPIC generator result for Operations service to GAX, so that each of the operations are nicely retryable.
  • introduce LongRunningController (or whatever its name is), which controls the status of an Operation by using gRPC method.
  • fix the code generator to use the new LongRunningController so that the users don't have to care about the intermediate status of the controller.

Note that we need to be careful about the file dependency -- Node.JS prohibits file-level circular dependencies.

Allow skipping auth

Some API devs want to connect a testing servers which have different auth story; right now gax-nodejs codebase is troublesome for such environment, because it always attempts to obtain the auth token and invokes combineChannelCredentials.

bundling does not work properly on logging

I'm doing verification of logging API, and noticed a buggy behavior; when the logging client does not specify all of the descriminator_fields https://github.com/googleapis/googleapis/blob/master/google/logging/v2/logging_gapic.yaml#L356, it fails to bundle the request, and it rejects to create API calls. Those fields are said optional in the proto file (https://github.com/googleapis/googleapis/blob/master/google/logging/v2/logging.proto#L85).

There are multiple bugs here:

  • when discriminator fields are partially specified, it should bundle the requests upon the request parameters as much as possible.
  • when such fields are not specified at all, it should not cause any errors. It should fall back to a normal API call instead.

client-side streaming: add 'response' event instead of assuming callback

From: #75

For client-side streaming, currently we simply assumes a callback parameter to receive the final result. This means:

var s = client.streamingMethod(function(err, resp) { ... });
s.write(req1);
s.write(req2);
s.write(req3);
s.end(); // => the specified callback is called.

The recommendation will be to make this callback as the parameter of the stream, thus:

var s = client.streamingMethod()
  .on('response', function(resp) { ... })
  .on('error', function(err) { ... });
s.write(req1);
s.write(req2);
s.write(req3);
s.end();

(Note that this is hypothetical, right now there are no usage of client-side streaming on Google APIs. But I believe some will introduce eventually).

Bug: stream version of paged iteration can't stop in the middle properly

Paged iteration can generate the stream version of the response. Instead of callback / Promise, it returns a Node.JS Stream which emits elements on data event. Reported that this stream can't stop properly in the middle, which causes unnecessary method calls which can damage usage quota.

User Agent

Is it possible to set a user agent? In google-cloud, we follow a specific format, but with requests going through gax, I think I'll need to set a new config option. We'd been doing it like this:

new ProtoService('{{baseUrl}}', {{grpc credentials}}, {
  'grpc.primary_user_agent': '...custom UA...'
});

Manual edits on operations_api.js

lib/operations_api.js is automatically generated file from our own toolkit, however, it needs some hand-edits to fit into the GAX library itself.

This issue tracks the list of hand-edits in case we want to regenerate everything, and also eventually we may need some quick scripts to automate the edits.

  • require('google-gax') does not work
 var extend = require('extend');
-var gax = require('google-gax');
+var gax = require('./gax');
+extend(gax, require('./api_callable');
+extend(gax, require('./path_template');
+gax.version = require('../package').version;
  • require('gax-google-longrunning') in the example of the constructor also does not work.
  * @example
- * var googleLongrunning = require('gax-google-longrunning')({
+ * var googleLongrunning = require('google-gax').lro({
  *   // optional auth parameters.
  * });

request for comments: gRPC-streaming API surfaces

gRPC streaming support has been done in GAPIC layer for nodejs, and it's in Speech API (as streamingRecognize method), but I'd like to know if there're some things to be polished for language-idiomaticity.

Note that this is for autogen layers, so API-specific conversions might not be possible (for example, separating audio config and audio data is very specific to streamingRecognize method, I think it's hard to generalize this pattern to other methods).

Cc: @stephenplusplus @callmehiphop @jmdobry

Here's what GAPIC is currently doing.

Bidi streaming

Both client and server has multiple objects. Typical example is the speech recognition -- client pushes the audio data asynchronously and server pushes the recognition results asynchronously.

This method does not take request parameter, but returns a readable/writable object stream.

var s = client.streamingRecognize()
  .on('data', function(data) { /* called every time a new item arrives. */ })
  .on('error', function(err) { /* called when an error happens. */ })
  .on('end', function() { /* called at the end of the stream. */ });

// writing requests.
s.write(req1);
s.write(req2);

// or pipe from another stream.
requestStream.pipe(s);

Response streaming (server-side streaming)

The client sends a single request object, and returns multiple objects. Typical example is query method in database like APIs (bigtable API has one, for example).

The method will take a request object as usual, but it returns a readable object stream instead of promise (or having callbacks).

var s = client.query(request)
  .on('data', function(data) { /* called every time a new item arrives. */ })
  .on('error', function(err) { /* called when an error happens. */ })
  .on('end', function() { /* called at the end of the stream. */ });

// has 'end' method to finish the stream in the middle.
s.end();

Request streaming (client-side streaming)

The client sends multiple request objects, the server returns a single response when it's done. Currently it's not used among Google APIs, but some may use in the future.

This does not take a request parameter but takes a callback, and returns a writable object stream.

var s = client.method(function(err, response) {
  if (err) {
    console.error(err);
    return;
  }
  // doThings(response);
});

// writing requests
s.write(req1);
s.write(req2);

// or pipe from another stream.
requestStream.pipe(s);

Automatic project ID insertion

Hello!

The code on GCN has gotten a bit complicated due to one missing feature from this library; automatic project ID insertion.

What we want is:

var requestOptions = {
  resourceName: 'projects/{{projectId}}/zones/zone-1/things/thing-name'
}
makeRequestWithGax(requestOptions, gaxOptions, callback)

This library would find and replace the {{projectId}} placeholder with the correct value:

  1. The projectId that was given to gax when it was instantiated
  2. The detected project ID from the environment (googleAutoAuth.getProjectId() has this feature)

Is this possible to implement here?

Thanks!

cc @lukesneeringer @landrito

Redesign constructSettings, callSettings, and createApiCall

From googleapis/gapic-generator#650 (comment):

I actually agree with @landrito, I first attempted to do that, but then switched back to adding an extra argument for backward compatibility -- the older codegen can work with the newer gax. But now I'm feeling I should redesign the structure of the arguments.

Here is the sketch of the new design.

  • remove descriptors (pageDescriptor, bundleDescriptor, and the new streamDescriptor) from the arguments for constructSettings.
  • hence CallSettings won't have those descriptions. It mostly contains options like retries, timeout, etc.
  • Instead, createApiCall will receive optional descriptor, so previously
var apiCall = gax.createApiCall(stub[methodName], defaults[methodName]);

now it got

var apiCall = gax.createApiCall(stub[methodName], defualts[methodName], descriptor);

descriptor will be either of PageDescriptor, BundleDescriptor, or StreamDescriptor.

Each of the descriptor has correspondence with implementation of ApiCaller to conduct the api call and manage the return value (either Promise or Stream).

For the suppression of certain type of method call (like isBundling: false for bundling methods) would be taken care of by the individual ApiCaller.

This would make gax-nodejs design apart from gax-python / gax-ruby, but I believe that would help the code health. cc: @geigerj @bjwatson @swcloud

Let ApiCallables return Promise directly

Right now ApiCallables return EventEmitter, which has the ability to cancel ongoing API calls and has custom result field which is a promise to the API call result.

Instead of this, we prefer to return a promise itself, because most users care about the result, cancelation is optional. The returned promise should have a reference to the event emitter or canceling method itself, so we don't lose the cancellation ability.

This will also need updating the code generator (especially the doc comments need to be updated).

cc: @stephenplusplus, @callmehiphop, @jmdobry

maxResults for paged iterations, both stream version and non-stream version

It is probably handy to support maxResults optional parameter for the paged iterations to cap the number of elements.

This is useful specifically for NodeJS because paged iteration invokes the method calls to reach to the end of the list and then convert it to an array -- other languages like Python or Ruby invoke the method lazily when the newer elements are required.

avoid unhandled promise rejection on wrongly initialized stub

Reported as googleapis/google-cloud-node#2086:

We should avoid unhandled promise rejections. Right now each method holds a promise for the method of the grpc client instance; this should be avoided.

We should:

  • catch the error on createStub and throw an error
  • or probably we should avoid using promises at all, stub creation and authentication should be delayed and done for the first time a method is called

Evaluate dorusu-js for a substitute for grpc

dorusu is a gRPC implementation written purely in JS: https://github.com/google/dorusu-js

As a quick look, the interface is different from the grpc library, it may need some rewrites on the code base. We want to explore the possibility to substitute our current usage of grpc, and evaluate the performance and the size of the changes to support it.

It is unclear the direction -- this issue simply tracks the evaluation of dorusu, i.e.

  • the size of the changes to be needed
  • feature parity (like streaming supports)
  • performance

cc: @omaray @ofrobots

Refine "page-streaming" interface yet again

We got some internal feedback about the return value for page-streaming. Current thing (an object Stream) would not be quite intuitive nor easy-to-use for many NodeJS programmers.

Here are suggestions:

Returns a promise which resolves to an Array of results

api.pagedMethod(req).then(function(results) {
  for (var i = 0; i < results.length; ++i) {
    var result = results[i];
    ...
  }
});

The promise will resolve to a response object when 'autoPaginate' is specified to false

This means we are going to rename flattenPages to autoPaginate (autoPaginate is the name google-cloud-node developers chose, and would be better to be consistent with this rather than the name used in other languages).

This would also be better that the sample code fetches the next page.

api.pagedMethod(req, {autoPaginate: false}).then(function(response) {
  console.log(response);
  return api.pagedMethod(req, {autoPaginate: false, pageToken: response.nextPageToken});
}).then(function(response) {
  // the next page.
  console.log(response);
});

(also -- probably better to specify a bit more call options here, like pageSize).

Generate another method with -Stream suffix for returning stream

This might be optional, but a different method to return a stream instead of Promise.

api.pagedMethodStream(req)
  .on('data', function(result) {
    // invoked for each result.
  }).on('error', function(err) { ... })
  .on('end', function() { ... });

Stream method with autoPaginate: false will generate an object Stream of response objects

api.pagedMethodStream(req, {autoPaginate: false})
  .on('data', function(response) {
    // invoked for each page.
  }).on('error', function(err) { ... })
  .on('end', function() { ... });

stop using errors.cause, simply return the error itself

Based on googleapis/google-cloud-node#1721 (comment):

There might not be a clear benefit of using 'Error' with cause property in NodeJS. This design originally came by respecting the design of gax-python, and that's good for Python because it fits with try...catch pattern.

gax-nodejs doesn't expect try...catch pattern at all, and we always use bare Error, thus it turns out this design does not fit to the actual usage quite well. Let's remove this.

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.