Giter Site home page Giter Site logo

tunnckocore / koa-rest-router Goto Github PK

View Code? Open in Web Editor NEW
67.0 3.0 6.0 629 KB

Most powerful, flexible and composable router for building enterprise RESTful APIs easily!

License: MIT License

JavaScript 100.00%
restful rest-api koa2 middleware router fast powerful flexible composable restful-api

koa-rest-router's Introduction

koa-rest-router NPM version NPM monthly downloads npm total downloads

Most powerful, flexible and composable router for building enterprise RESTful APIs easily!

codeclimate codestyle linux build windows build codecov dependency status

You might also be interested in gibon - a minimal & functional 600 bytes client-side router.

Highlighs

  • production: ready for and used in
  • composability: grouping multiple resources and multiple routers
  • flexibility: overriding controller and request methods, plus custom prefixes
  • compatibility: accepts both old and modern middlewares without deprecation messages
  • powerful: multiple routers on same koa app - even can combine multiple routers
  • light: not poluting your router instance and app - see .loadMethods
  • backward compatible: works on koa v1 - use .legacyMiddleware
  • maintainability: very small, beautiful, maintainable and commented codebase
  • stability: strict semantic versioning and very well documented, based on koa-better-router
  • open: love PRs for features, issues and recipes - Contribute a recipe? See the recipes of koa-better-router

Table of Contents

(TOC generated by verb using markdown-toc)

ProTip: Checkout koa-better-router API too to know what more methods comes with this.

Quickstart

This router uses koa-better-router, so you should review its API documentation to get more info how the things are working and what more methods are exposed.

Controller methods mapping

In addition this router allows you to override the controller methods which will be used in certain route path.

Defaults

Request method Route path Controller method
GET /users index
GET /users/new new
POST /users create
GET /users/:user show
GET /users/:user/edit edit
PUT /users/:user update
DELETE /users/:user remove

Example

let Router = require('koa-rest-router')
let router = Router()

router.resource('users', {
  // GET /users
  index: (ctx, next) => {},

  // GET /users/new
  new: (ctx, next) => {},

  // POST /users
  create: (ctx, next) => {},

  // GET /users/:user
  show: (ctx, next) => {},

  // GET /users/:user/edit
  edit: (ctx, next) => {},

  // PUT /users/:user
  update: (ctx, next) => {},

  // DELETE /users/:user
  remove: (ctx, next) => {}
})

let users = router.getResource('users')

console.log(users.length) // => 7
console.log(users) // => Array Route Objects

console.log(router.routes.length) // => 7
console.log(router.resources.length) // => 1

Note: Multiple middlewares can be passed on each. Also combining old and modern koa middlewares, so both generator functions and normal functions.

Overriding controller methods

You easily can override the defaults by passing options.map object with key/value pairs where the key represents the original, and value is a string containing the wanted override.

Example

let router = require('koa-rest-router')()

let options = {
  map: {
    index: 'foo',
    new: 'bar',
    create: 'baz',
    show: 'qux',
  }
}

router.resource('users', {
  // GET /users
  foo: (ctx, next) => {},

  // GET /users/new
  bar: (ctx, next) => {},

  // POST /users
  baz: (ctx, next) => {},

  // GET /users/:user
  qux: (ctx, next) => {},

  // ... etc
}, options)

Overriding request methods

In some cases in guides the REST routes uses different request methods and that field is not clear enough. So every sane router should allow overriding such things, so we do it. By default for updating is used PUT, for deleting/removing is DELETE. You can override this methods to use POST instead, so ...

Example

let router = require('koa-rest-router')()

let options = {
  methods: {
    put: 'POST'
  }  
}

router.resource('cats', {
  // POST /cats/:cat
  update: (ctx, next) => {}
}, options)

And you can combine both overriding variants, of course

Example

let router = require('koa-rest-router')()

let options = {
  methods: {
    put: 'POST'
  },
  map: {
    update: 'foobar'
  }
}

router.resource('cats', {
  // POST /cats/:cat
  foobar: (ctx, next) => {}
}, options)

Install

Install with npm

$ npm i koa-rest-router --save

Usage

For more use-cases see the tests

let router = require('koa-rest-router')()

// or

let Router = require('koa-rest-router')
let apiRouter = Router({ prefix: '/api/v1' })

API

KoaRestRouter

Initialize KoaRestRouter with optional options, directly passed to koa-better-router and this package inherits it. So you have all methods and functionality from the awesome koa-better-router middleware.

Params

  • [options] {Object}: passed directly to koa-better-router, in addition we have 2 more options here.
  • [options.methods] {Object}: override request methods to be used
  • [options.map] {Object}: override controller methods to be called

Example

let Router = require('koa-rest-router')
let api = Router({ prefix: '/api/v1' })

// - can have multiples middlewares
// - can have both old and modern middlewares combined
api.resource('companies', {
  index: function (ctx, next) {},
  show: function (ctx, next) {},
  create: function (ctx, next) {}
  // ... and etc
})

console.log(api.routes.length) // 7
console.log(api.resources.length) // 1

api.resource('profiles', {
  index: function (ctx, next) {},
  show: function (ctx, next) {},
  create: function (ctx, next) {}
  // ... and etc
})

console.log(api.routes.length) // 14
console.log(api.resources.length) // 2

let Koa = require('koa') // Koa v2
let app = new Koa()

let basic = Router() // prefix is `/` by default
basic.extend(api)

app.use(api.middleware())
app.use(basic.middleware())

app.listen(4444, () => {
  console.log('Open http://localhost:4444 and try')
  // will output 2x14 links
  // - 14 links on `/api/v1` prefix
  // - 14 links on `/` prefix
  api.routes.forEach((route) => {
    console.log(`${route.method} http://localhost:4444${route.path}`)
  })
  basic.routes.forEach((route) => {
    console.log(`${route.method} http://localhost:4444${route.path}`)
  })
})

.createResource

Core method behind .resource for creating single resource with a name, but without adding it to this.routes array. You can override any defaults - default request methods and default controller methods, just by passing respectively opts.methods object and opts.map object. It uses koa-better-router's .createRoute under the hood.

Params

  • name {String|Object}: name of the resource or ctrl
  • ctrl {Object}: controller object to be called on each endpoint, or opts
  • opts {Object}: optional, merged with options from constructor
  • returns {KoaRestRouter} this: instance for chaining

Example

let router = require('koa-rest-router')({
  prefix: '/api'
}).loadMethods()

// The server part
let body = require('koa-better-body')
let Koa = require('koa')
let app = new Koa()

// override request methods
let methods = {
  put: 'POST'
  del: 'POST'
}

// override controller methods
let map = {
  index: 'list',
  show: 'read',
  remove: 'destroy'
}

// notice the body should be invoked explicitly
// with or without options object, no matter
let updateMiddlewares = [body(), (ctx, next) => {
  ctx.body = `This method by default is triggered with PUT requests only.`
  ctx.body = `${ctx.body} But now it is from POST request.`
  return next()
}, function * (next) => {
  this.body = `${this.body} Incoming data is`
  this.body = `${this.body} ${JSON.stringify(this.request.fields, null, 2)}`
  yield next
}]

// create actual resource
let cats = router.createResource('cats', {
  list: [
    (ctx, next) => {
      ctx.body = `This is GET ${ctx.route.path} route with multiple middlewares`
      return next()
    },
    function * (next) {
      this.body = `${this.body} and combining old and modern middlewares.`
      yield next
    }
  ],
  read: (ctx, next) => {
    ctx.body = `This is ${ctx.route.path} route.`
    ctx.body = `${ctx.body} And param ":cat" is ${ctx.params.cat}.`
    ctx.body = `${ctx.body} By default this method is called "show".`
    return next()
  },
  update: updateMiddlewares,
  destroy: (ctx, next) => {
    ctx.body = `This route should be called with DELETE request, by default.`
    ctx.body = `${ctx.body} But now it request is POST.`
    return next()
  }
}, {map: map, methods: methods})

console.log(cats)
// => array of "Route Objects"

// router.routes array is empty
console.log(router.getRoutes()) // => []

// register the resource
router.addResource(cats)

console.log(router.routes.length) // => 7
console.log(router.getRoutes().length) // => 7
console.log(router.getRoutes()) // or router.routes
// => array of "Route Objects"

app.use(router.middleware())

app.listen(5000, () => {
  console.log(`Server listening on http://localhost:5000`)
  console.log(`Try to open these routes:`)

  router.routes.forEach((route) => {
    console.log(`${route.method}` http://localhost:5000${route.path}`)
  }))
})

.addResource

Simple method that is alias of .addRoutes and .addResources, but for adding single resource. It can accepts only one resource object.

Params

  • resource {Array}: array of route objects, known as "Resource Object"
  • returns {KoaRestRouter} this: instance for chaining

Example

let Router = require('koa-rest-router')
let api = new Router({
  prefix: '/'
})

console.log(api.resources.length) // 0
console.log(api.routes.length) // 0

api.addResource(api.createResource('dragons'))

console.log(api.resources.length) // 1
console.log(api.routes.length) // 7

console.log(api.getResource('dragons'))
// array of route objects
// => [
//   { prefix: '/', route: '/dragons', path: '/dragons', ... }
//   { prefix: '/', route: '/dragons/:dragon', path: '/dragons/:dragon', ... }
//   ... and 5 more routes
// ]

.getResource

Get single resource by name. Special case is resource to the / prefix. So pass / as name. See more on what are the "Route Objects" in the koa-better-router docs. What that method returns, I call "Resource Object" - array of "Route Objects"

Params

  • name {String}: name of the resource, plural
  • returns {Array|Null}: if resource with name not found `null, otherwise array of route objects - that array is known as Resource Object

Example

let api = require('koa-rest-router')({
  prefix: '/api/v2'
})

let frogs = api.createResource('frogs')
let dragons = api.createResource('dragons')

console.log(api.getResource('frogs'))
// array of route objects
// => [
//   { prefix: '/api/v2', route: '/frogs', path: '/api/v2/frogs', ... }
//   { prefix: '/api/v2', route: '/frogs/:frog', path: '/api/v2/frogs/:frog', ... }
//   ... and 5 more routes
// ]

console.log(api.getResources().length) // 2

.resource

Creates a resource using .createResource and adds the resource routes to the this.routes array, using .addResource. This is not an alias! It is combination of two methods. Methods that are not defined in given ctrl (controller) returns by default 501 Not Implemented. You can override any defaults - default request methods and default controller methods, just by passing respectively opts.methods object and opts.map object.

Params

  • name {String|Object}: name of the resource or ctrl
  • ctrl {Object}: controller object to be called on each endpoint, or opts
  • opts {Object}: optional, merged with options from constructor
  • returns {KoaRestRouter} this: instance for chaining

Example

let Router = require('koa-rest-router')

let api = new Router({ prefix: '/api/v3' })
let router = new Router() // on `/` prefix by default

// All of the controller methods
// can be remap-ed. using `opts.map`
// try to pass `{ map: { index: 'home' } }` as options

api.resource('users', {
  // GET /users
  index: [(ctx, next) => {}, (ctx, next) => {}],

  // GET /users/new
  new: (ctx, next) => {},

  // POST /users
  create: (ctx, next) => {},

  // GET /users/:user
  show: [(ctx, next) => {}, function * (next) {}],

  // GET /users/:user/edit
  edit: (ctx, next) => {},

  // PUT /users/:user
  // that `PUT` can be changed `opts.methods.put: 'post'`
  update: (ctx, next) => {},

  // DELETE /users/:user
  // that `DELETE` can be changed `opts.methods.delete: 'post'`
  remove: (ctx, next) => {}
})

// notice the `foo` method
router.resource({
  // GET /
  foo: [
    (ctx, next) => {
      ctx.body = `GET ${ctx.route.path}`
      return next()
    },
    function * (next) {
      ctx.body = `${this.body}! Hello world!`
      yield next
    }
  ],
  // GET /:id, like /123
  show: (ctx, next) => {
    ctx.body = JSON.stringify(ctx.params, null, 2)
    return next()
  }
}, {
  map: {
    index: 'foo'
  }
})

api.routes.forEach(route => console.log(route.method, route.path))
router.routes.forEach(route => console.log(route.method, route.path))

// Wanna use only one router?
let fooRouter = new Router()
let Koa = require('koa')
let app = new Koa()

fooRouter.addRoutes(api.getResources(), router.getRoutes())

console.log(fooRouter.routes)
console.log(fooRouter.routes.length) // 14

app.use(fooRouter.middleware())

app.listen(4433, () => {
  console.log('Cool server started at 4433. Try these routes:')

  fooRouter.routes.forEach((route) => {
    console.log(`${route.method} http://localhost:4433${route.path}`)
  })
})

.addResources

Just an alias of koa-better-router's' .addRoutes method.

Params

  • ...args {Array}: any number of arguments (arrays of route objects)
  • returns {KoaRestRouter} this: instance for chaining

.getResources

As we have .getRoutes method for getting this.routes, so we have .getResources for getting this.resources array, too. Each .createResource returns array of route objects with length of 7, so 7 routes. So if you call .createResource two times the this.resources (what this method returns) will contain 2 arrays with 7 routes in each of them.

  • returns {Array}: array of arrays of route objects

Example

let router = require('koa-rest-router')().loadMethods()

console.log(router.routes.length)          // 0
console.log(router.getRoutes().length)     // 0

console.log(router.resources.length)       // 0
console.log(router.getResources().length)  // 0

router.get('/about', (ctx, next) => {})
router.resource('dogs')
router.resource('cats')

console.log(router.routes.length)          // 15
console.log(router.getRoutes().length)     // 15

console.log(router.resources.length)       // 2
console.log(router.getResources().length)  // 2

.groupResources

Powerful method for grouping couple of resources into one resource endpoint. For example you have /cats and /dogs endpoints, but you wanna create /cats/:cat/dogs/:dog endpoint, so you can do such things with that. You can group infinite number of resources. Useful methods that gives you what you should pass as arguments here are .createResource, .createRoute, .getResources, .getResource and .getRoutes. Note: Be aware of that it replaces middlewares of dest with the middlewares of last src.

Params

  • dest {Array}: array of "Route Objects" or "Resource Object" (both are arrays)
  • src1 {Array}: array of "Route Objects" or "Resource Object" (both are arrays)
  • src2 {Array}: array of "Route Objects" or "Resource Object" (both are arrays)
  • returns {Array}: new array with grouped resources

Example

let router = require('koa-rest-router')({ prefix: '/api/v3'})

let departments = router.createResource('departments')
let companies = router.createResource('companies')
let profiles = router.createResource('profiles')
let clients = router.createResource('clients')
let users = router.createResource('users')
let cats = router.createResource('cats')
let dogs = router.createResource('dogs')

// endpoint: /companies/:company/departments/:department
let one = router.groupResources(companies, departments)

// endpoint: /profiles/:profile/clients/:client/cats/:cat
let two = router.groupResources(profiles, clients, cats)

// crazy? huh, AWESOME!
// endpoint: /companies/:company/departments/:department/profiles/:profile/clients/:client/cats/:cat
let foo = router.groupResources(one, two)

// but actually just "register" `one` and `foo`
// so you WON'T have `/profiles/:profile/clients/:client/cats/:cat`
// endpoint in your API
router.addRoutes(one, foo)

// Server part
let Koa = require('koa')
let app = new Koa()

app.use(router.middleware())

app.listen(4000, () => {
  console.log(`Mega API server on http://localhost:4000`)
  console.log(`Checkout these routes:`)

  // it will output 14 links
  router.getRoutes().forEach((route) => {
    console.log(`${route.method} http://localhost:4000${route.path}`)
  })
})

Related

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Please read the contributing guidelines for advice on opening issues, pull requests, and coding standards.
If you need some help and can spent some cash, feel free to contact me at CodeMentor.io too.

In short: If you want to contribute to that project, please follow these things

  1. Please DO NOT edit README.md, CHANGELOG.md and .verb.md files. See "Building docs" section.
  2. Ensure anything is okey by installing the dependencies and run the tests. See "Running tests" section.
  3. Always use npm run commit to commit changes instead of git commit, because it is interactive and user-friendly. It uses commitizen behind the scenes, which follows Conventional Changelog idealogy.
  4. Do NOT bump the version in package.json. For that we use npm run release, which is standard-version and follows Conventional Changelog idealogy.

Thanks a lot! :)

Contributing Recipes

Recipes are just different use cases, written in form of README in human language. Showing some "Pro Tips" and tricks, answering common questions and so on. They look like tests, but in more readable and understandable way for humans - mostly for beginners that not reads or understand enough the README or API and tests.

  • They are in form of folders in the root recipes/ folder: for example recipes/[short-meaningful-recipe-name]/.
  • In recipe folder should exist README.md file
  • In recipe folder there may have actual js files, too. And should be working.
  • The examples from the recipe README.md should also exist as separate .js files.
  • Examples in recipe folder also should be working and actual.

It would be great if you follow these steps when you want to fix, update or create a recipes. 😎

  • Title for recipe idea should start with [recipe]: for example[recipe] my awesome recipe
  • Title for new recipe (PR) should also start with [recipe].
  • Titles of Pull Requests or Issues for fixing/updating some existing recipes should start with [recipe-fix].

It will help a lot, thanks in advance! 😋

Building docs

Documentation and that readme is generated using verb-generate-readme, which is a verb generator, so you need to install both of them and then run verb command like that

$ npm install verbose/verb#dev verb-generate-readme --global && verb

Please don't edit the README directly. Any changes to the readme must be made in .verb.md.

Running tests

Clone repository and run the following in that cloned directory

$ npm install && npm test

Author

Charlike Mike Reagent

License

Copyright © 2016-2017, Charlike Mike Reagent. Released under the MIT license.


This file was generated by verb-generate-readme, v0.4.1, on February 14, 2017.
Project scaffolded using charlike cli.

koa-rest-router's People

Contributors

adam187 avatar tunnckocore 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

Watchers

 avatar  avatar  avatar

koa-rest-router's Issues

drop support of passing `prefix` to `.middleware/.legacyMiddleware`

Basically it works correctly. But later if you wanna list them all you can't get correct and actual routes.

If you have, for example, one router and use .middleware multiple times with different prefixes all will work, but you can't get all available routes - they works, but you can list them for example, after the server is started (for example list them from the app.listen(port, fn) callback)

let Router = require('koa-rest-router')
let router = Router()

router.resource('users').resource('cats')

let Koa = require('koa')
let app = new Koa()

app.use(router.middleware())
app.use(router.middleware({ prefix: '/api/v1' }))
app.use(router.middleware({ prefix: '/foo/api/v3' }))

router.routes.forEach((route, idx) => {
  console.log(idx + 1, route.path)
})

It will print only 14 routes, only these that are on / prefix, but not the others. They will work. If you access them through web. You will have 42 actual and working routes, but it will output only 14 of them.

So if you wanna do the trick correctly you should do 3 different routers and use the .extend method.

let router2 = Router({ prefix: '/api/v1' })
let router3 = Router({ prefix: '/foo/api/v3' })

// add `router`'s routes on `router2`
router2.extend(router)

// add `router`'s routes on `router3`
router3.extend(router)

// add the three routers on koa server
app.use(router.middleware())
app.use(router2.middleware())
app.use(router3.middleware())

let i = 1

router.routes.forEach((route) => console.log(i++, route.path))
router2.routes.forEach((route) => console.log(i++, route.path))
router3.routes.forEach((route) => console.log(i++, route.path))

renaming "new" to something else?

Hi,

Thanks for making this awesome middleware!

While i know i can override, but i would suggest to rename the controller new to something else.

It doesn't throw any errors but as a "Js keyword" it gives other syntax highlighting and ....

Would recent do, instead of new? <-- it rhymed

Thank you again :)

TypeError: utils.compose(...)(...).then is not a function

Hi, I just wanna try out koa and when I look on how to route it, I was amaze by how easy the koa-rest-router are. But when I use it with koa version 1, I got this error instead.

koa version: ^1.2.4
koa-rest-router version: ^1.0.0

Error message

TypeError: utils.compose(...)(...).then is not a function
      at C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\koa-better-router\index.js:544:52
      at Object.converted (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\koa-convert\index.js:48:27)
      at next (native)
      at Object.<anonymous> (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\koa-compose\index.js:28:19)
      at next (native)
      at onFulfilled (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\co\index.js:65:19)
      at C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\co\index.js:54:5
      at Object.co (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\co\index.js:50:10)
      at Object.createPromise (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\co\index.js:30:15)
      at Server.handleRequest (C:\Users\Apiep\Documents\Hacking\qiscus-vacation\node_modules\koa\lib\application.js:136:8)

How I "mount" the router:
app.use(userRouter.legacyMiddleware())

How I write the router:

router.resource('users', {
  index: function * (next) {
    this.body = { name: 'index' }
  },
  new: function * (next) {
    this.body = { name: 'new' }
  }
}

If you want to look the whole app you can check my repo: https://github.com/apiep/qiscus-vacation/blob/master/router/user.js

Grouping does not work correctly without Router prefix

Consider example:

const Koa = require('koa');
const Router = require('koa-rest-router');

const router = Router();

const departments = router.createResource('departments');
const companies = router.createResource('companies');

// endpoint: /companies/:company/departments/:department
const one = router.groupResources(companies, departments);
router.addRoutes(companies, one);

// Server part
const app = new Koa();

app.use(router.middleware());

app.listen(4000, () => {
  console.log('Mega API server on http://localhost:4000');
  console.log('Checkout these routes:');
  router.getRoutes().forEach((route) => {
    console.log(`${route.method} http://localhost:4000${route.path}`);
  });
});

This outputs

GET http://localhost:4000/companies
GET http://localhost:4000/companies/new
POST http://localhost:4000/companies
GET http://localhost:4000/companies/:company
GET http://localhost:4000/companies/:company/edit
PUT http://localhost:4000/companies/:company
DELETE http://localhost:4000/companies/:company
GET http://localhost:4000/companies/:company/departments
GET http://localhost:4000/companiesnew/:companiesnew/departments/new
POST http://localhost:4000/companies/:company/departments
GET http://localhost:4000/companies:company/:companies:company/departments/:department
GET http://localhost:4000/companies:company/:companies:company/departments/:department/edit
PUT http://localhost:4000/companies:company/:companies:company/departments/:department
DELETE http://localhost:4000/companies:company/:companies:company/departments/:department

Although, if I initialize Router as

const router = Router({ prefix: '/api/v3' });

It outputs more consciense route paths:

GET http://localhost:4000/api/v3/companies
GET http://localhost:4000/api/v3/companies/new
POST http://localhost:4000/api/v3/companies
GET http://localhost:4000/api/v3/companies/:company
GET http://localhost:4000/api/v3/companies/:company/edit
PUT http://localhost:4000/api/v3/companies/:company
DELETE http://localhost:4000/api/v3/companies/:company
GET http://localhost:4000/api/v3/companies/:company/departments
GET http://localhost:4000/api/v3/companies/:company/departments/new
POST http://localhost:4000/api/v3/companies/:company/departments
GET http://localhost:4000/api/v3/companies/:company/departments/:department
GET http://localhost:4000/api/v3/companies/:company/departments/:department/edit
PUT http://localhost:4000/api/v3/companies/:company/departments/:department
DELETE http://localhost:4000/api/v3/companies/:company/departments/:department

chore: package

Switch to use eslint directly and run link before test script.

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.