Git Product home page Git Product logo

moleculer's Introduction

Moleculer logo

CI test Coverage Status Maintainability Codacy Badge Known Vulnerabilities Discord chat

Downloads FOSSA Status Patreon

Moleculer NPM version Twitter URL

Moleculer is a fast, modern and powerful microservices framework for Node.js. It helps you to build efficient, reliable & scalable services. Moleculer provides many features for building and managing your microservices.

Website: https://moleculer.services

Documentation: https://moleculer.services/docs

Top sponsors

What's included

  • Promise-based solution (async/await compatible)
  • request-reply concept
  • support event driven architecture with balancing
  • built-in service registry & dynamic service discovery
  • load balanced requests & events (round-robin, random, cpu-usage, latency, sharding)
  • many fault tolerance features (Circuit Breaker, Bulkhead, Retry, Timeout, Fallback)
  • plugin/middleware system
  • support versioned services
  • support Streams
  • service mixins
  • built-in caching solution (Memory, MemoryLRU, Redis)
  • pluggable loggers (Console, File, Pino, Bunyan, Winston, Debug, Datadog, Log4js)
  • pluggable transporters (TCP, NATS, MQTT, Redis, NATS Streaming, Kafka, AMQP 0.9, AMQP 1.0)
  • pluggable serializers (JSON, Avro, MsgPack, Protocol Buffer, Thrift, CBOR, Notepack)
  • pluggable parameter validator
  • multiple services on a node/server
  • master-less architecture, all nodes are equal
  • built-in parameter validation with fastest-validator
  • built-in metrics feature with reporters (Console, CSV, Datadog, Event, Prometheus, StatsD)
  • built-in tracing feature with exporters (Console, Datadog, Event, Jaeger, Zipkin, NewRelic)
  • official API gateway, Database access and many other modules...

Installation

$ npm i moleculer

or

$ yarn add moleculer

Create your first microservice

This example shows you how to create a small service with an add action which can add two numbers and how to call it.

const { ServiceBroker } = require("moleculer");

// Create a broker
const broker = new ServiceBroker();

// Create a service
broker.createService({
    name: "math",
    actions: {
        add(ctx) {
            return Number(ctx.params.a) + Number(ctx.params.b);
        }
    }
});

// Start broker
broker.start()
    // Call service
    .then(() => broker.call("math.add", { a: 5, b: 3 }))
    .then(res => console.log("5 + 3 =", res))
    .catch(err => console.error(`Error occurred! ${err.message}`));

Try it in your browser

Create a Moleculer project

Use the Moleculer CLI tool to create a new Moleculer based microservices project.

  1. Create a new project (named moleculer-demo)

    $ npx moleculer-cli -c moleculer init project moleculer-demo
  2. Open the project folder

    $ cd moleculer-demo
  3. Start the project

    $ npm run dev
  4. Open the http://localhost:3000/ link in your browser. It shows a welcome page that contains more information about your project & you can test the generated services.

๐ŸŽ‰ Congratulations! Your first Moleculer-based microservices project is created. Read our documentation to learn more about Moleculer.

Welcome page

Official modules

We have many official modules for Moleculer. Check our list!

Supporting

Moleculer is an open source project. It is free to use for your personal or commercial projects. However, developing it takes up all our free time to make it better and better on a daily basis. If you like Moleculer framework, please support it.

Thank you very much!

For enterprise

Available as part of the Tidelift Subscription.

The maintainers of moleculer and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

Documentation

You can find here the documentation.

Changelog

See CHANGELOG.md.

Security contact information

To report a security vulnerability, please use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.

Contributions

We welcome you to join in the development of Moleculer. Please read our contribution guide.

Project activity

Alt

License

Moleculer is available under the MIT license.

3rd party licenses

Contact

Copyright (c) 2016-2023 MoleculerJS

@moleculerjs @MoleculerJS

moleculer's People

Contributors

0x0a0d avatar alvaroinckot avatar andremaz avatar artur-krueger avatar chrvadala avatar dependabot[bot] avatar dkuida avatar faeron avatar ffkl avatar fugufish avatar gadi-manor avatar glenn-p-ryan avatar icebob avatar intech avatar jordanpawlett avatar kesslerdev avatar kthompson23 avatar molobala avatar mrdroid1 avatar nathan-schwartz avatar ngraef avatar nllahat avatar rmccallum81 avatar shawnmcknight avatar tinchoz49 avatar urossmolnik avatar wallacyf avatar wolfulus avatar xvld avatar zllovesuki 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  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

moleculer's Issues

Move REPL to a separated optional module `moleculer-repl`

This package looks awesome! Great work so far, excited to see what else will come of it!

One thing I noticed is that a large chunk (~2/3) of the dependencies are only used for the REPL (vorpal, table, clui)
(collected using cost-of-modules)

screen shot 2017-06-12 at 4 31 32 pm

I haven't looked deeply enough into the source code to see if it would be possible. But I think it may be worth exploring making moleculer-repl a separate package that developers can add as a devDepenency and reduce final size of the production build.

If version is not a number, do not prefix with v

Would this be something that would align with the gameplan for Moleculer? Right now if version exists its being prefixed with a 'v' which makes sense for numerical versions. In our use case we might want to have different versions of a microservice running for various features we are working on. In that case i have suggested something like 'staging.serviceName.actionName'. Allowing individual teams to deploy microservices to our infrastructure that can be targeted via the api gateway for that specific environment/feature.

Services/actions goes offline and never come back

I don't know if this is NATS specific and I couldn't figure out why this happens, but can be easily reproduced:

  1. Start a service connected to a NATS endpoint
  2. Connect a cli instance with moleculer connect nats://localhost:4222
  3. Stop the service and start it again.
  4. On the cli type actions and you'll see all actions offline.
  5. Start a new cli instance the same way and execute actions too. All actions are online.
  6. Old cli is still in "failed" mode.

https://files.gitter.im/ice-services/moleculer/Dna6/image.png

NATS Transporter: unhandled promise rejection upon disconnect

Upon sending SIGINT to an application, the NATS transporter sends a disconnect packet, every now and then resulting in an unhandled promise rejection:

Unhandled rejection TypeError: Cannot read property 'publish' of null
    at NatsTransporter.publish (\node_modules\moleculer\src\transporters\nats.js:151:14)
    at Transit.publish (\node_modules\moleculer\src\transit.js:494:18)
    at Transit.sendDisconnectPacket (\node_modules\moleculer\src\transit.js:162:8)
    at Transit.disconnect (\node_modules\moleculer\src\transit.js:145:16)
    at Promise.resolve.then.catch.then (\node_modules\moleculer\src\service-broker.js:291:26)
    at runCallback (timers.js:800:20)
    at tryOnImmediate (timers.js:762:5)
    at processImmediate [as _immediateCallback] (timers.js:733:5)
From previous event:
    at ServiceBroker.stop (\node_modules\moleculer\src\service-broker.js:289:5)
    at process.ServiceBroker._closeFn (\node_modules\moleculer\src\service-broker.js:159:9)
    at emitOne (events.js:115:13)
    at process.emit (events.js:210:7)

Cannot get nodeID in context

According to the documentation, we could get nodeID in context
image
But I only get null.

Context {
  id: null,
  broker: 
   ServiceBroker {
     options: 
      { nodeID: 'T4F-MBP-13504.local.24144',
        logger: [Object],
        logLevel: 'info',
        transporter: null,
        requestTimeout: 0,
        requestRetry: 0,
        maxCallLevel: 0,
        heartbeatInterval: 10,
        heartbeatTimeout: 30,
        registry: [Object],
        circuitBreaker: [Object],
        cacher: null,
        serializer: null,
        validation: true,
        metrics: false,
        metricsRate: 1,
        statistics: false,
        internalActions: true },
     Promise: 
      { [Function: Promise]
        TypeError: [Function: TypeError],
        RangeError: [Function: RangeError],
        CancellationError: [Function: SubError],
        TimeoutError: [Function: SubError],
        OperationalError: [Function: OperationalError],
        RejectionError: [Function: OperationalError],
        AggregateError: [Function: SubError],
        _peekContext: [Function],
        onPossiblyUnhandledRejection: [Function],
        onUnhandledRejectionHandled: [Function],
        longStackTraces: [Function],
        hasLongStackTraces: [Function],
        config: [Function],
        getNewLibraryCopy: [Function],
        is: [Function],
        fromCallback: [Function],
        fromNode: [Function],
        all: [Function],
        cast: [Function],
        fulfilled: [Function],
        resolve: [Function],
        rejected: [Function],
        reject: [Function],
        setScheduler: [Function],
        pending: [Function],
        defer: [Function],
        method: [Function],
        try: [Function],
        attempt: [Function],
        bind: [Function],
        PromiseInspection: [Function: PromiseInspection],
        join: [Function],
        Promise: [Circular],
        version: '3.5.0',
        map: [Function],
        using: [Function],
        delay: [Function],
        coroutine: [Object],
        spawn: [Function],
        promisify: [Function],
        promisifyAll: [Function],
        props: [Function],
        race: [Function],
        reduce: [Function],
        settle: [Function],
        some: [Function],
        _SomePromiseArray: [Function: SomePromiseArray],
        filter: [Function],
        each: [Function],
        mapSeries: [Function: PromiseMapSeries],
        any: [Function],
        noConflict: [Function: noConflict] },
     ServiceFactory: [Function: Service],
     ContextFactory: [Function: Context],
     nodeID: 'T4F-MBP-13504.local.24144',
     _loggerCache: 
      { BROKER: [Object],
        'HELLO-SVC': [Object],
        'API-GW-SVC': [Object] },
     logger: 
      { fatal: [Function: bound ],
        error: [Function: bound ],
        warn: [Function: bound ],
        info: [Function: bound ],
        debug: [Function: noop],
        trace: [Function: noop] },
     bus: 
      EventEmitter {
        _events: {},
        newListener: false,
        verboseMemoryLeak: false,
        _conf: [Object],
        _maxListeners: 100,
        wildcard: true,
        listenerTree: {} },
     services: [ [Object], [Object] ],
     serviceRegistry: 
      ServiceRegistry {
        opts: [Object],
        services: [Array],
        actions: [Object],
        broker: [Circular] },
     middlewares: [ [Function: bound validatorMiddleware] ],
     cacher: null,
     serializer: JSONSerializer { broker: [Circular] },
     validator: ParamValidator { validator: [Object], broker: [Circular] },
     sampleCount: 0,
     getNodeHealthInfo: [Function],
     _closeFn: [Function] },
  action: 
   { handler: [Function],
     name: 'hello.hello',
     version: undefined,
     service: 
      Service {
        name: 'hello',
        version: undefined,
        settings: {},
        schema: [Object],
        broker: [Object],
        Promise: [Object],
        logger: [Object],
        actions: [Object],
        created: [Function],
        started: [Function],
        stopped: [Function] },
     cache: false },
  nodeID: null,
  parentID: null,
  metrics: false,
  level: 2,
  timeout: 0,
  retryCount: 0,
  params: {},
  meta: {},
  requestID: null,
  startTime: null,
  startHrTime: null,
  stopTime: null,
  duration: 0,
  cachedResult: false }

MQTT Transport issue service discovery

Having an issue with service discovery on MQTT, probably just a user error but i'd appreciate some guidance.

const Moleculer = require('moleculer');

const { ServiceBroker, Transporters: { MQTT: MqttTransport } } = Moleculer;

const transporter = new MqttTransport('mqtt://localhost:1883');

const broker = new ServiceBroker({
  logger: console,
  transporter,
});

broker.createService({
  name: 'engagedAction',
  actions: {
    create() {
      return 'hello world';
    },
  },
});

broker.start().then(() => broker.call('engagedAction.create')).then(r => console.log(r));

I run this then I switch to terminal where I create an identical ServiceBroker and call $node.services and the service from the other node is never there. I have confirmed that both nodes are connected to my MQTT service and i can see messages posting in the backend. Any thoughts on the step i'm missing?

Refactoring action & service queries

Refactoring the below parts:

  • $node.actions
  • $node.services
  • actions in REPL
  • services in REPL
  • actions in INFO & DISCOVER packets

Other tasks:

  • fix tests
  • update docs

Official API Gateway service

Features

  • native or Expess http server (maybe pluggable adapter for Koa, Hapi, ...etc)
  • exposed path (/api)
  • whitelist handling
  • static folder handling to serve assets
  • SSL support
  • body parsers
  • aliases to shorthand routes (e.g. users.create -> POST /users)

expose event or some trigger on disconnected!

Would be nice to be able to listen on node for disconnected event
(when we are using 'moleculer-web')
so we can do some custom logic in that case
and also some parameter in settings so when some node goes down then show this kind of respnses
instead of hanging request to specific action (this happens after we stop/kill some node)
would be nice also to check if node is down or not
before we call action

{
  "name": "ServiceNotFoundError",
  "message": "Service 'extra.hello' is not available!",
  "code": 501,
  "type": null,
  "data": {
    "action": "extra.hello"
  }
}

or

{
  "name": "NodeNotFoundError",
  "message": "Service 'extra.hello' on node 'server-1' is not available!",
  "code": 501,
  "type": null,
  "data": {
    "action": "extra.hello"
  }
}

yes we can use timeout but then it does not gives us a correct understanding what was happened
in case some node is disconnected
[TRANSIT] Node 'server-1' disconnected!

Broker direct calls throws if targeting itself

I keep doing checks to avoid broker throwing errors. I think broker should be able to detect a call for itself and do the proper actions

module.exports = function(router) {

    /**
     * Gets all cluster nodes excluding the current node
     */
    router.get("/cluster/nodes", async function(req, res) {

        const broker = req.app.broker;
        const logger = req.app.logger;

        const nodes = {};

        const allNodes = await broker.call("$node.list", { withEndpoints: true });
        allNodes
            .map(node => {
                if (node.id === null) {
                    node.id = broker.nodeID; // PROBLEM #1 - I have to fill the gaps for null ids
                }
                if (node.services) {
                    delete node.services;
                }
                return node;
            })
            .forEach(node => {
                nodes[node.id] = node;
            });

        for (const nodeID in nodes) {

            // This check doesn't make sense
            const callOptions = nodeID === broker.nodeID ? { } : { nodeID }; // PROBLEM #2 - Broker should detect this scenario

            try {
                nodes[nodeID].stats = await broker.call("$node.stats", {}, callOptions);
            } catch(err) {
                logger.error(err);
                nodes[nodeID].stats = null;
            }

            try {
                nodes[nodeID].health = await broker.call("$node.health", {}, callOptions);
            } catch(err) {
                logger.error(err);
                nodes[nodeID].health = null;
            }

        }

        res.send(nodes);

    });

};

Transporter connect error

If transporter can't connect to server, but reconnect is success, transit doesn't register handlers & doesn't send discover.

Implement real circuit breaker

Current solution: if a request returns back with >=500 error code, broker set the node to "unavailable". It is not too accurate because maybe just an action give errors, the node and node's other actions are working properly. Need to implement CB for actions (in the ServiceBroker).
Leave the node's "unavailable" flag when heartbeat is not received from the node.

Trying to implement as a middleware. Performance limit is -10%. โ—

typescript support

Hello!

Do you have any plan to support typescript?
I didn't see moleculer at @types.

Thanks in advance

Metadata in Context

Metadata in context which will be transported with request. (for user, session, state...etc)

ctx.meta = {
  user: {
    id: req.user.id,
    roles: req.user.roles
  }
}

Logger not wrapped properly

Would be great if we could use something like Pino as the logger:

const logger = require("pino")({ level: "info" });
const { ServiceBroker } = require("moleculer");
const broker = new ServiceBroker({ logger });
broker.logger.info("hi");

TypeError: Cannot read property 'write' of undefined
at LOG (node_modules\pino\lib\tools.js:102:9)
at EventEmitter. (node_modules\moleculer\src\logger.js:63:7)

After all, it's really fast and supports custom log levels and child loggers.

Add reference to validator library in docs

I've found you're using fastest-validator (which rocks!), but it's not documented (or I haven't found it).

It would be helpful to have access to it directly from the documentation.

Keep calling service after stop or restart process

Hi, I like this project concept. I've been created 2 projects a worker and gateway connecting on the Redis transport, but I have some problem when I stop or restart worker. The gateway continues calling the stopped worker, look the gateway log messages:

[BROKER] 'api-gw' service is registered!
[API-GW-SVC] API Gateway created!
[API-GW-SVC] API Gateway listening on http://0.0.0.0:3000
[TX] Redis-sub connected!
[TX] Redis-pub connected!
[BROKER] Broker started. NodeID: GW-HPDEV-24176
[TRANSIT] Node 'WORKER-HPDEV-24155' connected! -> I start the worker
[TRANSIT] Node 'WORKER-HPDEV-24192' connected! -> Restart the worker
[TRANSIT] Node 'WORKER-HPDEV-24155' disconnected! -> detected the disconnection
[API-GW-SVC] Call 'math.teste' action with params: {}
[BROKER] Action 'math.teste' call timed out on 'WORKER-HPDEV-24155'! -> look, he called the killed process
[BROKER] Recall 'math.teste' action (retry: 3)...
[API-GW-SVC] Call 'math.teste' action with params: {}

After some retries he call right service.
This is a bug or wrong configuration?

let broker = new ServiceBroker({
    nodeID: 'GW-' + hostname + '-' + process.pid,
    logger: console,
    metrics: true,
    requestTimeout: 5000,
    requestRetry: 3,
    circuitBreaker: {
        enabled: true,
        maxFailures: 3,
        halfOpenTime: 500,
        failureOnTimeout: true,
        failureOnReject: true
    },
    transporter: new RedisTransporter({
        redis: {
            port: 6379,
            host: 'localhost',
            family: 4,
            db: 1
        }
    })
})

// Load API Gateway
broker.createService(ApiService);

// Start server
broker.start()

Runaway promises in transit module

While using Redis transporter:

(node:10664) Warning: a promise was created in a handler at \node_modules\moleculer\src\transit.js:73:11 but was not returned from it, see http://goo.gl/rRqMUw
    at new Promise (\node_modules\bluebird\js\release\promise.js:79:10)

(node:10664) Warning: a promise was created in a handler at \node_modules\moleculer\src\transit.js:76:21 but was not returned from it, see http://goo.gl/rRqMUw
    at new Promise (\node_modules\bluebird\js\release\promise.js:79:10)

(node:10664) Warning: a promise was created in a handler at \node_modules\moleculer\src\transit.js:77:21 but was not returned from it, see http://goo.gl/rRqMUw
    at new Promise (\node_modules\bluebird\js\release\promise.js:79:10)

Segmentation

We need a namespace property that we can segment our services.
For example, we are running development & production services (or more production services) on the same transporter, but we need to avoid collisions.

Currently there is a prefix property in the Transporter options (which solve it), but I want to move it to ServiceBroker options (in v0.9.x).

const broker = new ServiceBroker({
  logger: console,
  namespace: "blog" // for production
  namespace: "blog.dev" // for development
  namespace: "blog.test" // for testing
});

And it can be also set via environment variable NAMESPACE.

image

Method/Action groups

It'd be great to have better support for action/method grouping (svc.actionGroup.subAction).
Currently it's achievable using string literal keys:

{
  name: "svc",

  methods: {
    "methodGroup.subMethod": () => 42,
  },

  actions: {
    "actionGroup.subAction"() {
      return this["methodGroup.subMethod"]();
    },
  },
}

Both the pattern and modularizing of service logic based on groups could be more elegant:

{
  name: "svc",

  methods: {
    methodGroup: {
      subMethod: () => 42,
    },
  },

  actions: {
    actionGroup: {
      subAction() {
        return this.methodGroup.subMethod();
      },
    }
  },
}

Improve circuit breaker

  • if state is HALF_OPEN, accept only 1 request. If this request is success, set to CLOSE
  • threshold based measuring

How to integrate it with Netflix Eureka?

Hi everyone,

I'm trying to choose a microservices framework to use with node
but I really need to be able to integrate it with Eureka since we already have a
microservices infrastructure using Eureka :'(

Does anybody know how to use moleculer with Eureka for service register/discovery?

Redundant service registration-Auto service versioning.

What happens if we register multiple times the same service name with different schema?
(currently seems the latest registered schema wins)
Would it be usefull to have the hash of the loaded schema reported for each service?
Is it safe to register services on the fly (without stoping restarting the broker)?

In examples/user.service.js there is a flag latestVersion: true. Any plans to use semantic versioning in order
to implicitly peek a service version (like stable or latest) as default?

Multi/Parallel calls

Solution 1 (with array)

broker.mcall([
	{ action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
	{ action: "users.find", params: {limit: 5, sort: "username"} }
]).then(results => {
	let posts = results[0];
	let users = results[1];
})

Solution 2 (with object)

broker.mcall({
	posts: { action: "posts.find", params: {limit: 5, offset: 0}, options: { timeout: 500 } },
	users: { action: "users.find", params: {limit: 5, sort: "username"} }
}).then(results => {
	let posts = results.posts;
	let users = results.users;
})

moleculer CLI tool

Create a CLI tool to generate moleculer base project & services from templates.

Tasks

  • moleculer init - generate an empty moleculer base project from a template
  • moleculer add service - create a new service file. Prompted values: name, version, action names. Create service file and put to the services folder.
  • moleculer connect <connectionstring> - connect to a transporter or start a local broker. After it switch to REPL mode with vorpal. Available commands: call, event, loadService

Does it support event group?

If two services listen to a same event, when it occurs, both of them will receive the event, I guess.
Does moleculer support event group which is supported natively by nats and amqp?
Say, if the two services listen to a same event but and join a same group, only one of them will receive the event at a time.

Remove unused metrics functions

  • remove auto emit metrics data (metricsSendInterval). It is not neccessary. Anyone can call it via $node.health and $node.stats

Version 0.10 changes

Breaking changes

  • Rewritten strategies #75
  • Moved broker.createNewContext
  • Killed nodeID == null in core modules. #81
  • changed internalActions to an internal service #81

New

  • AMQP transporter #72
  • Better remote call metrics #78
  • Hot reload services

Fixes

  • Broker direct call nodeID #67
  • Runaway promises #71 #77

Docs

  • update CHANGELOG
  • update docs on moleculer site
  • add protocol doc to moleculer site

Who uses it? Usage examples

I was just discovering this project starting out on a new one of my own. It looks nice. but I was wondering where it has been used in production. Or if anyone notable was using it. I think it looks great but if im going to start something new I don't want to start with any random framework.

as a comparison, outside of this i was probably going to go with hapi.js

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.