Giter Site home page Giter Site logo

joi-router's Issues

How do I multipart?

Hello everyone,
I saw the docs and I have managed to successfully receive every type of requests except for multipart..

I copied the given code:
``admin.route({
method: 'post'
, path: '/blog'
, validate: { type: 'multipart' }
, handler: function *(){
var parts = yield this.request.parts
var part

while (part = yield parts) {
  // do something with the incoming part stream
  part.pipe(someOtherStream)
}

console.log(parts.field.name) // form data

}
})``

But when I run my server I immediatly get the following error:
TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "undefined"

If I use co-busboy myself meaning:
var parts = parse(this) var part while (part = yield parts) { console.log (part); if (part.length) { // arrays are busboy fields console.log('key: ' + part[0]) console.log('value: ' + part[1]) } else { // otherwise, it's a stream part.pipe(fs.createWriteStream('some file.txt')) } }
It works amazingly perfect

I know it has something to do with generators but don't get why..
Also - am I supposed to validate the fields myself? because I can't do:
validate: { type: 'multipart', body: { username: Joi.string().required(), email: Joi.string().optional() } }

Din.

can I specify base url to router?

this router is cool, but i want to add base url to the router so i can use it like this:

var pub = routuer({baseUrl:'/test'});
pub.post('/wow', ...);

then url '/test/wow' can be visited

can this be possible by some ways?

3q

ability to specify Joi validation options without using Joi objects as schemas

Hey there!

Considering using koa-joi-router on our next few projects.

One issue I ran into is no ability to set options for Joi other than specifying the validation as Joi objects everytime and chaining an options call.
body: joi.object({ email: joi.string().email().required(), password: joi.string().required(), }).options({abortEarly: false}),

This is a bit tedious if you want something set for every validation everywhere.
F.ex. I'd like abortEarly: false to get all validation errors at once.

The way I see it, it could be done 2 ways:

  1. have individual options in the validation spec (abortEarly, convert, etc.)
  2. whole options object in the validation spec

Potential problems:
These "global" options overriding individual validations sent as Joi objects with options specified (like in the example above).

I'd be willing to tackle this and submit a PR if everyone's ok with the changes - already have solution 1. ready.

await-busboy not working properly

Multipart request not returning proper ctx.request.parts object. returns FileStream object without path key which contains file temp path and form-data key-values are also not returning. I tried async-busboy. works fine with the same request. It returns {files, fields}, if you say, I can take a chance to migrate from await-busboy to async-busboy.

ctx.request.parts

like #44

handler: async (ctx) => {
      const parts = ctx.request.parts;
      const file = await parts;
      const ext = file.filename.split('.').pop();
      if (fileExt.indexOf(ext) === -1) {
        ctx.status = 400;
        return;
      }

// passed in, but cannot get

      const type = parts.field.type;
      const types = ['sth'];
      if (types.indexOf(type) === -1) {
        ctx.status = 400;
        return;
      }

      const filename = `sth`;
      const result = await createBlockBlobFromStream(type, filename, file);

      ctx.status = 200;
      ctx.body = {status: 1, data: {filename}};
    }

and i changed it to:

handler: async (ctx) => {
      const parts = ctx.request.parts;
      let part;
      let file;
      /* eslint no-cond-assign:0, no-await-in-loop:0 */
      while (part = await parts) {
        if (part.filename) {
          file = part;
        }
        part.resume();
      }
      const ext = file.filename.split('.').pop();
      if (fileExt.indexOf(ext) === -1) {
        ctx.status = 400;
        return;
      }

// can get form-data fields

      const type = parts.field.type;
      const types = ['sth'];
      if (types.indexOf(type) === -1) {
        ctx.status = 400;
        return;
      }
      const filename = `sth`;

// cannot upload to oss service, hanging without any err

      const result = await createBlockBlobFromStream(type, filename, file);

// never print
console.log('123');

      ctx.status = 200;
      ctx.body = { status: 1, data: { filename } };
    }

p.s.

exports.createBlockBlobFromStream = (container, filename, blob) => {
  const deferred = {};
  deferred.promise = new Promise((resolve, reject)=>{
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  blob.on('error', (err) => {
    deferred.reject(err);
  });
  blob.pipe(blobService.createWriteStreamToBlockBlob(container, filename));
  blob.on('end', () => {
    deferred.resolve(1);
  });
  return deferred.promise;
};

Will joi-router add language support?

As in joi document, the validate method receives 4 params.
validate(value, schema, [options], [callback])

Some options in validation is useful like language - overrides individual error messages. Defaults to no override ({}). Messages apply the following rules :

So will joi-router add options support in the future when use the validate method? ๐Ÿ˜‹

Nested router prefix

How I can have sub router with prefix and add main router with another prefix?

userRoutes.js

import router from 'koa-joi-router';
const userRouter = router();
userRouter.prefix('/user');
const userRoutes = [...];
userRouter.route(userRoutes);
export default userRouter;

mainRoutes.js

import router from 'koa-joi-router';
import userRouter from 'userRoutes';
apiRouter.prefix('/app');
apiRouter.route(userRouter.routes);
export default apiRouter;

server.js

import AppApiRouter from 'mainRoutes';
app.use(AppApiRouter.middleware());

So actually, now my userRouter uri should be '/app/user/path'. Instead it is replacing prefix with new prefix string. In mainRoutes.js , apiRouter.route is directly accessing userRoutes.routes object. We are adding prefix as key in router object, that's why mainRouter is not able to get new path.

How to solve this?

Feature: Allow multiple types

It seems it would make sense to allow clients to request resources with either json, url encoded or form data. All three could be allowed by the API.
Currently the type argument in the validation is only accepting a string.
I could create a PR that would accept an array too.

Update to use newest joi version

Currently date().greater can not be used, cause the bundles joi version is too old.
Could you kindly update the dependencies?

is there a nice way for await-busboy?

handler: async (ctx) => {
    const parts = ctx.request.parts;
    let part;

    try {
      while ((part = await parts)) {
        // handle files at the first place
        part.pipe(someOtherStream);
      }
    } catch (err) {
    }
    // we need to use fields data first
    console.log(parts.field.name);
  }

we need to use fields data first, such as check username/password.

if the request is valid, then upload file to third party file storage service, like Azure-Blob-Storage

here is a solution that can work:

const Koa = require('koa');
const router = require('koa-joi-router');
const { Writable } = require('stream');
const { getDefer } = require('@dwing/common');

const handleFile = (part) => {
  const deferred = getDefer();
  const stream = new Writable();
  stream.buffers = [];
  stream.length = 0;
  stream._write = function (chunk, encoding, next) {
    this.length = this.length + chunk.length;
    this.buffers.push(chunk);
    next();
  };

  stream.once('finish', function () {
    const buffer = (this.buffers.length === 0 ? new Buffer(0) : (this.buffers.length === 1 ? this.buffers[0] : Buffer.concat(this.buffers, this.length)));
    deferred.resolve(buffer);
  });
  stream.once('error', (err) => {
    deferred.reject(err);
  });

  part.pipe(stream);
  return deferred.promise;
};
const multipart = async (parts) => {
  const result = {
  };
  try {
    let part;
    // eslint-disable-next-line
    while ((part = await parts)) {
      if (part.length) {
        result[part[0]] = part[1];
      } else {
        const field = part.fieldname;
        result[field] = {
          filename: part.filename,
          mime: part.mime,
          mimeType: part.mimeType,
          encoding: part.encoding
        };
        const file = await handleFile(part);
        result[field].length = file.length;
        result[field].buffer = file;
      }
    }
  } catch (err) {
    // handle the error
  }
  return result;
};

const app = new Koa();
const routes = router();
routes.route([{
  method: 'post',
  path: '/upload',
  validate: {
    type: 'multipart',
    maxBody: '5mb'
  },
  handler: async (ctx) => {
    const parts = ctx.request.parts;
    const body = {
      ...await multipart(parts),
      ...parts.field
    };
    console.log(body);
    ctx.body = '1';
  }
}]);

app.use(routes.middleware());
app.listen(3000);

but we wonder if there is a better way, that can copy filestream for later use..

fn is not run before specific route when using router.use(path, fn)

Document says:

To run middleware before a specific route, also pass the optional path:

const router = require('koa-joi-router');
const users = router();

users.get('/:id', handler);
users.use('/:id', runThisBeforeHandler);```

but runThisBeforeHandler is actually run after handler

install error with security connect problem ( in China )

I can't install this package, with error: ( in China , it's OK in AWS us)

$ npm -v
5.4.2
$ npm install koa-joi-router
npm WARN deprecated [email protected]: XSS vulnerability fixed in v1.0.3
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/aheckmann/koa-router.git
npm ERR! 
npm ERR! fatal: unable to access 'https://github.com/aheckmann/koa-router.git/': Unable to communicate securely with peer: requested domain name does not match the server's certificate.
npm ERR! 
npm ERR! exited with error code: 128

By the way, I found the dependency version is very old compare to the original, can you upgrade it ?
And I wonder why not depend on the original koa-router ?

  1. dependency in package.json is : "koa-router": "git+https://github.com/aheckmann/koa-router.git#19e60d9"
  2. https://github.com/aheckmann/koa-router is now in version 5.1.2.
  3. https://github.com/alexmingoia/koa-router ( the original ) is now 7.3.0.

Prefix seems to be broken

ubuntu 16.04
Node.js 10.15.1
koa 2.7.0
koa-joi-router 5.2.0

'use strict'

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

const router = require('koa-joi-router')

const orders = router()
orders.get('/', ctx => {
  ctx.body = 'Orders response'
})

orders.prefix('orders')

app.use(orders.middleware())

app.listen(3000)

Actual behavior:

localhost:3000/orders returns 404 Not Found

Expected behavior:

localhost:3000/orders returns 200 'Orders response`

Connected issue created in koa-router

How to use this middleware with koa-bodyparser

Hello and thank you all for the work done on this project.

I'm trying to integrate this middleware into a project that already uses koa-bodyparser (required middleware to koa-passport).

Unfortunately when trying to reach a route when this middleware is declared the road never answers. I guess there is a conflict with co-body somewhere.

Do we have a workaround for using this middleware with koa-bodyparser or am i going wrong ?

const koa = require('koa');
const bodyParser = require('koa-bodyparser');
const router = require('koa-joi-router');
const Joi = router.Joi;

const app = koa();
const helloRouter = router();

app.use(bodyParser()); // <-- comment and the call will work

helloRouter.route({
  method: 'post',
  path: '/hello',
  validate: {
    body: {
      name: Joi.string().required()
    },
    type: 'json'
  },
  handler: function* hello() {
    const { name } = this.request.body;
    this.body = `Hello ${name}`;
  }
});

app.use(helloRouter.middleware());

app.listen(3000);

The call : curl -X POST -H "Content-Type: application/json" -d '{ "name": "John" }' "http://localhost:3000/hello"

body parser err

const Koa = require('koa');
const router = require('koa-joi-router');
const Joi = require('joi');
const parse = require('co-body');

const routes = router();

routes.route([
  {
    method: 'post',
    path: '/signup',
    validate: {
      body: {
        name: Joi.string().max(100),
        email: Joi.string().lowercase().email(),
        password: Joi.string().max(100),
        _csrf: Joi.string().token()
      },
      type: 'form'
    },
    handler: async (ctx) => {
      console.log('sth'); // does not print 
      ctx.status = 200;
      ctx.body = ctx.request.body;
    }
  }
]);

const app = new Koa();

// add this for test.
app.use(async (ctx, next) => {
  ctx.request.body = await parse.form(ctx);
  console.log(ctx.request.body);
  // curl -d "name=test" http://localhost:3000/signup
  // ^@curl: (52) Empty reply from server
  // print: { name: 'test' }
  await next(); // always pendding
});

app.use(routes.middleware());
app.listen(3000);

i need a pre-request-handler to check whether the token is valid.

so i have 3 questions:

  1. how to fix this issue
  2. how can i parse the body before jump into the router
  3. how to support multiple validate.type like ['json','form']

Use recent Joi

The latest Joi version is 9 but this module uses a 1 year old version 6. Any plans to update it?

Link to koa-docs

Hello,

Great job on this project. I really like the declarative route specs you have created and some really good forethought went into exposing the routes for analysis and having a meta property.

I am building a number of micro-services and want some documentation to go along with them so I have created a small project that renders documentation from the route specs provided by koa-joi-router. The project is koa-docs. I would appreciate a link from here so more people can use/contribute to it.

Thanks

Expose the Joi validator as a middleware

Right now the only way to use the Joi middleware is to pass a configuration to the router. It would be more flexible to be able to use the middleware directly.

For example, instead of:

router.get('/user', config, handler)

It should be possible to do:

router.get('/user', joiMiddleware(config), handler)

This is more inline with a middleware based framework, allows the validation to be used after other middlewares, and enables wrapping and testing the validator.

I think this could be done in 2 ways. Either expose the middleware directly from the module, or extract the middleware to a joi-middleware project on which joi-router would depend. The second option is my personal favorite since it would also allow the middleware to be used with other routers. The first option however is definitely easier to implement.

Is this project still maintained?

Noticed that it's almost 10 months without any update. The major version of Joi is already 14 but that of the bundled one is just 10.

Anyway, great package with nice design!

Is there a way to set global joi config?

I want to set joi allowUnknown as a global config, because I don't mind what additional fields are passed in as long as the fields I want are supplied.
I have to set allowUnknown in every Joi valid code, is there a way to set global Joi config like this below?

const router = require('koa-joi-router');
const pub = router({
  validate: {
    allowUnknown: true,
  },
});

Run middleware for a specific path + method

Hi! Thanks for your amazing work on this router!

I was wondering, if the router is able to execute a middleware before hitting a specifc path + method. In the documentation, I saw that is possible for a specific path but without method allocated to it.

var router = require('koa-joi-router');
var users = router();

users.get('/user/:id', handler);
users.use('/:id', runThisBeforeHandlerForGET);

users.post('/user', handler);
users.use('/:id', runThisBeforeHandlerForPOST);

Is it possible?

Cannot find module 'await-busboy'

In the last commit, some changes have been made but it brokes the router. The dependency await-busboy doesn't exist. I used Node v7.0.0 with --harmony flag and I ran npm install twice.

Thanks for the amazing work on this repository!

node --harmony server.js
module.js:474
    throw err;
    ^

Error: Cannot find module 'await-busboy'
    at Function.Module._resolveFilename (module.js:472:15)
    at Function.Module._load (module.js:420:25)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/Users/aurelien/repositories/strapi/packages/strapi/node_modules/koa-joi-router/joi-router.js:9:16)
    at Module._compile (module.js:573:32)
    at Object.Module._extensions..js (module.js:582:10)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at require (internal/module.js:20:19)

Support KoaRouter#param middlewares

Documentation: KoaRouter#param

That is, validate param input before KoaRouter calls the param middleware so that we can get the validated value in the middleware function.

router.route({
  path: '/thing/:id',
  handler: function* () {
    // this.thing is set using router param middleware, below
    this.body = this.thing
  },
  validate: {
    params: {
      id: Joi.number()
    }
  }
})

router.router.param('id', function* (id, next) {
  // imagine db.getThing will throw if id is not a number
  // currently this will fail if id is not a number since it is not yet validated
  this.thing = yield db.getThing(id)
  yield next
})

date().iso() is wrongly claiming input is invalid

Using

validate: {
  type: "form",
  body: {
    start: Joi.date().iso().greater("now"),
  }
}

The form input of start="2018-10-10T10:00" or start="2018-10-10T10:00Z" both lead to

child "start" fails because ["start" must be a valid ISO 8601 date]

However Joi.validate("2018-10-10T10:00", Joi.date().iso().greater("now")) => error === null

Query validator doens't work

Hello, genius, I found that it seems the query validator doesn't work. Here is my code:

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

const router = require('koa-joi-router')
const Joi = router.Joi
const p = router()

p.route({
  path: '/',
  method: 'get',
  validate: {
    query: {
      t: Joi.string().min(3)
    },
    body: {
      email: Joi.string().email()
    },
    type: 'json'
  },
  handler: async ctx => {
    ctx.body = 'hello'
  }

})
p.prefix('/api/v1/public')
app.use(p.middleware())

image

If the email is missed in the request body, it works very well. However, it doesn't throw any error without a t in the request query even with the email in the body. What's wrong?

image

Awaiting multipart "parts" hangs when a file field is followed by text fields

If I get a multipart request that has a file field first with a subsequent text field, the second await call will hang indefinitely.

If I have a text field first, the first await will have the file stream and the second await will still hang but the ctx.request.parts.field array will be populated properly with the text fields.

API versioning based on Accept header

How would one go about building an API where the version is not part of an URL?

Other popular methods include using HTTP Accept header, custom request headers, etc.

It seems like a good way to achieve this would be by introducing condition to .route() options which can simply be a middeware function that returns a boolean. If the true it would continue to validate and execute handlers, if not it would simply skip to the next route(if any) with the same path and method.

This would give us an ability to inspect headers and execute the right validation and handlers based on the API version.

Thoughts?

request hangs if joi-router used together with a body parser

NodeJS: 8.6.0
npm: 5.2.0
OS: Max OS X 10.12.2
koa: 2.4.1
koa-joi-router: 5.0.0
koa-bodyparser: 4.2.0
koa-body: 2.5.0

For the following app:

const koa = require('koa');
const router = require('koa-joi-router');
const bodyParser = require('koa-bodyparser');

const public = router();

public.route({
  method: 'post',
  path: '/foo',
  validate: {
    type: 'json',
  },
  handler: async (ctx) => {
    ctx.status = 201;
  }
});

const app = new koa();
app.use(bodyParser());
app.use(public.middleware());
app.listen(2999);

the request hangs. The same if koa-body used instead if koa-bodyparser.

I need a koa body parser to be able to use request body in the middlewares.

Object/named array in query string

I wanted to validate a object in query string (?deactivated[from]=2019-05-23T08:18:45.283Z&deactivated[to]=2019-06-12T08:18:45.283Z)
So I went ahead and tried:

query: {
  deactivated: {
    from: Joi.date(),
    to: Joi.date(),
  },
}

But the request just prints out ("deactivated[from]" is not allowed).

I have discovered that ?deactivated={"from": "2019-01-01T08:18:45.283Z"} works, but I would like the use the first approach, as that's how objects are converted to query string by default.
Is there any way I can get this behaviour?

From further testings, if I define the query like so:

query: {
  "deactivated[from]": Joi.date(),
  "deactivated[to]": Joi.date(),
}

It seems to work, but that's not a solution, the variable names are unusable.

this.state.route cannot get custom error in validate

function makeSpecExposer(spec) {
  var defn = clone.clonePrototype(spec);
  return function* specExposer(next) {
    this.state.route = defn;
    yield* next;
  };
}

Why use clone to expose specs? It will produce some trouble like 'custom error will transform to undefined'.

default query string options do not work for empty query string

OS: Max OS X 10.12.2
Node version: 8.6.0
npm version: 5.2.0
koa version: 2.4.1
koa-joi-router version: 5.0.0
joi version: 9.2.0

I have the following route:

module.exports = {
  method: 'get',
  path: '/',
  validate: {
    query: Joi.object().keys({
      search: Joi.string(),
      sort: Joi.object().default({ 'name': 1 }),
    }),
  },
  handler: async (ctx) => {
    const { sort } = ctx.query;
    console.log(sort);
    ctx.status = 200;
  },
};

The actual behavior:
when I specify a query string ex. ?search=foo, I get the correct default value for sort parameter, however for no query string I get undefined

The expected behavior:
even for no query string, the sort parameter should have { 'name': 1 } as default value

Setting converted query values is not working correctly

Joi supports converting/casting types but this feature is not working correctly with the koa integration.

If one has a query validator, this line sets request.query to the converted object but it then gets stringified again here which causes numbers, booleans, etc to be converted to string again.

Any plan to use npm package instead of git commit for koa-router

I try to package my node app with docker. I used node alpine for base image, of course for less container size, but node alpine does not contain git to minimise image size and we are using git for aheckmann-koa-router package, so my docker built is throwing an error for alpine version

npm ERR! git clone --template=/root/.npm/_git-remotes/_templates --mirror https://github.com/aheckmann/koa-router.git /ro
ot/.npm/_git-remotes/git-https-github-com-aheckmann-koa-router-git-19e60d9-704f0691: undefined

npm ERR! Linux 4.4.0-72-generic
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "--production"
npm ERR! node v7.9.0
npm ERR! npm  v4.2.0
npm ERR! code ENOGIT

npm ERR! not found: git
npm ERR!
npm ERR! Failed using git.
npm ERR! This is most likely not a problem with npm itself.
npm ERR! Please check if you have git installed and in your PATH.

I verified in my app, this is the only package using git for koa-router, so it will be better if we use it npm package so that we can reduce the container size from 83MB to 14MB.

node docker image link

related #39

Build issue using v 4.0.0 due to dependency on your koa-router fork?

We are still using version 4.0.0 of this package which has a specific commit dependency on your fork of koa-router:

git+https://github.com/aheckmann/koa-router.git#f0c60d374b5903f

This has started breaking in our recent builds. Is it possible you changed something on your fork that would break this?

When we try and npm install we get a git error:

npm info git [ 'rev-list', '-n1', 'f0c60d374b5903f' ]
npm ERR! git rev-list -n1 f0c60d374b5903f: fatal: ambiguous argument 'f0c60d374b5903f': unknown     revision or path not in the working tree.

We can see that commit in github, but it does not seem to appear in any branch?

Can't PUT or POST

Hi,
First, thanks for the awesome package, i just begin to use it.
I make separation between routes, controllers and my handlers.
All my GET routes of my crud works fine, but when i want to make validation on my POST and my PUT routes, it doesn't work.
If i comment validate{}, in my route, it works. I think it's joi. But in the top of my file i have:

var router = require('koa-joi-router');
var Joi = router.Joi;

You can see a simple exemple of the skeleton of my API. The real project have a complete CRUD, if you want to see it, i can share the other pages. I separate my routes definitions in 3 parts, the route.js file :

var router = require('koa-joi-router');
// Controllers
var messageCntrl = require('./api/controllers/message');
module.exports = function(app) {
  var general = router();
  general.prefix('/api');
  general.route(messageCntrl.post);
// others routes
}

The controller:

var router = require('koa-joi-router');
var Joi = router.Joi;

// Handlers
var messageHndlr = require('../handlers/message');

var ctrl = module.exports = {};
// Need to fix
ctrl.post = {
  method: 'post',
  path: '/message',
   validate: {
     body: {
       name: Joi.string().max(100)
     },
     type: 'json',
   },
  handler: messageHndlr.post
};

The handler

var hdlr = module.exports = {};
hdlr.post = function *(next){
  yield next;
  var error, request, result;
  // console.log(this.request.body);
  console.log('dans le handler');
  try {
    var request = new Message(this.request.body);
    result = yield request.save();
    this.status = 200;
    return this.body = result;
  } catch (error) {
    // console.log(error);
    return boom.wrap(error, 400);
  }
};

You can see screen if you want but i don't have a lot of other informations:
With validate in my controller (just loading long time and show this):
with-validation

When i comment validate in my controller:
without-validation

It doesn't works for body validate not for params, and i didn't test for headers. I expose POST route but it's exactly the same for my PUT route.

Thanks for your help

Dates in query string become empty strings after Joi validation

If a query parameter is validated as a Joi date object, the request will pass validation but the param will be an empty string once inside the route.

I believe this is due to the validateInput method relying on setting request['query'] to the validated Joi object. Unfortunately, this.request.query has getter/setter logic inside of Koa:

  get query() {
    var str = this.querystring;
    var c = this._querycache = this._querycache || {};
    return c[str] || (c[str] = qs.parse(str));
  },
  set query(obj) {
    this.querystring = qs.stringify(obj);
  },

The properly validated objects are being stringified by koa, and then parsed again when accessing this.request.query inside the router. It looks like boolean params also turn into strings rather than primitives which is likely the same issue.

Access current route definition

I would like to use my own middleware on the routes. I know how to define it from the documentation, and the middleware is hit, but I would like to access the current route definition in it. Similar to how joi-router adds the validate property to the route definition, I would like to add my own (roles).
How can I access the current route definition from my middleware?

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.