Coder Social home page Coder Social logo

rustless's Introduction

Gitter

Table of Contents

What is Rustless?

Build Status

Rustless is a REST-like API micro-framework for Rust. It's designed to provide a simple DSL to easily develop RESTful APIs on top of the Iron web framework. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.

Rustless in a port of Grape library from Ruby world. Based on hyper - an HTTP library for Rust.

Like Rust itself, Rustless is still in the early stages of development, so don't be surprised if APIs change and things break. If something's not working properly, file an issue or submit a pull request!

# Cargo.toml
[dependencies.rustless]
git = "https://github.com/rustless/rustless"

API docs

See also

Usage warning

Rustless is based on Iron, which is based on Hyper, which is synchronous. Hyper has a lot of limitations right now, and can't handle many simultaneous connections, especially with keep-alive. So it is highly recommended to use light asynchronous web server such as Nginx as a reverse proxy server with Rustless.

Basic Usage

Below is a simple example showing some of the more common features of Rustless.

extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate rustc_serialize as serialize;
extern crate valico;

use valico::json_dsl;
use rustless::server::status::StatusCode;
use rustless::{
    Application, Api, Nesting, Versioning
};
use rustless::json::ToJson;

fn main() {

    let api = Api::build(|api| {
        // Specify API version
        api.version("v1", Versioning::AcceptHeader("chat"));
        api.prefix("api");

        // Create API for chats
        api.mount(Api::build(|chats_api| {

            chats_api.after(|client, _params| {
                client.set_status(StatusCode::NotFound);
                Ok(())
            });

            // Add namespace
            chats_api.namespace("chats/:id", |chat_ns| {

                // Valico settings for this namespace
                chat_ns.params(|params| {
                    params.req_typed("id", json_dsl::u64())
                });

                // Create endpoint for POST /chats/:id/users/:user_id
                chat_ns.post("users/:user_id", |endpoint| {

                    // Add description
                    endpoint.desc("Update user");

                    // Valico settings for endpoint params
                    endpoint.params(|params| {
                        params.req_typed("user_id", json_dsl::u64());
                        params.req_typed("id", json_dsl::string())
                    });

                    endpoint.handle(|client, params| {
                        client.json(&params.to_json())
                    })
                });

            });
        }));
    });

    let app = Application::new(api);

    iron::Iron::new(app).http("0.0.0.0:4000").unwrap();
    println!("On 4000");

    println!("Rustless server started!");
}

To easily build the example, you can set your Cargo.toml file approximately as follows:

[package]
name = "rustless-example"
version = "0.1.0"

[dependencies]
rustless = "0.10.0"
hyper = "0.10.5"
rustc-serialize = "0.3"
valico = "1"

[dependencies.iron]
version = "*"

Complex example

If you want to see how you can write some complex application using Rustless, please see the example.

In the example, please note the following aspects:

  • Complex nested API with versioning.
  • CRUD operations with rust-postgres.
  • Swagger 2.0 intergration.
  • JSON Schema validations.
  • Error reporting.
  • Serializers.
  • File structure.
  • Integration with docopt.
  • Integration with deuterium-orm. Database migrations.

Mounting

In Rustless you can use three core entities to build your RESTful app: Api, Namespace and Endpoint.

  • Api can mount Api, Namespace and Endpoint
  • Namespace can mount Api, Namespace and Endpoint
Api::build(|api| {

    // Api inside Api example
    api.mount(Api::build(|nested_api| {

        // Endpoint definition
        nested_api.get("nested_info", |endpoint| {
            // endpoint.params(|params| {});
            // endpoint.desc("Some description");

            // Endpoint handler
            endpoint.handle(|client, _params| {
                client.text("Some usefull info".to_string())
            })
        });

    }))

    // The namespace method has a number of aliases, including: group,
    // resource, resources, and segment. Use whichever reads the best
    // for your API.
    api.namespace("ns1", |ns1| {
        ns1.group("ns2", |ns2| {
            ns2.resource("ns3", |ns3| {
                ns3.resources("ns4", |ns4| {
                    ns4.segment("ns5", |ns5| {
                        // ...
                    );
                })
            })
        })
    })
})

Parameters validation and coercion

You can define validations and coercion options for your parameters using a DSL block inside Endpoint and Namespace definition. See Valico for more info about what you can do.

api.get("users/:user_id/messages/:message_id", |endpoint| {
    endpoint.params(|params| {
        params.req_typed("user_id", Valico::u64());
        params.req_typed("message_id", Valico::u64());
    });

    // ...
})

Use JSON Schema

Also you can use JSON Schema (IETF's draft v4) to validate your parameters. To use schemes in your application you need to use the following setup:

use valico::json_schema;
use rustless::batteries::schemes;

let scope = json_schema::Scope::new();

// ... You can insert some external schemes here ...

schemes::enable_schemes(&mut app, scope).unwrap();

See Valico for more info about JSON Scheme usage inside DSL blocks.

Query strings

Rustless is intergated with queryst to allow smart query-string parsing end decoding (even with nesting, like foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb). See queryst for more info.

API versioning

There are three strategies in which clients can reach your API's endpoints:

  • Path
  • AcceptHeader
  • Param

Path versioning strategy

api.version("v1", Path);

Using this versioning strategy, clients should pass the desired version in the URL.

curl -H http://localhost:3000/v1/chats/

Header versioning strategy

api.version("v1", AcceptHeader("chat"));

Using this versioning strategy, clients should pass the desired version in the HTTP Accept head.

curl -H Accept:application/vnd.chat.v1+json http://localhost:3000/chats

Accept version format is the same as Github (uses)[https://developer.github.com/v3/media/].

Param versioning strategy

api.version("v1", Param("ver"));

Using this versioning strategy, clients should pass the desired version as a request parameter in the URL query.

curl -H http://localhost:9292/statuses/public_timeline?ver=v1

Respond with custom HTTP Status Code

By default Rustless returns a 200 status code for GET-Requests and 201 for POST-Requests. You can use status and set_status to query and set the actual HTTP Status Code

client.set_status(NotFound);

Use parameters

Request parameters are available through the params: JsonObject inside Endpoint handlers and all callbacks. This includes GET, POST and PUT parameters, along with any named parameters you specify in your route strings.

The request:

curl -d '{"text": "hello from echo"}' 'http://localhost:3000/echo' -H Content-Type:application/json -v

The Rustless endpoint:

api.post("", |endpoint| {
    endpoint.handle(|client, params| {
        client.json(params)
    })
});

In the case of conflict between either of:

  • route string parameters
  • GET, POST and PUT parameters
  • the contents of the request body on POST and PUT

route string parameters will have precedence.

Redirecting

You can redirect to a new url temporarily (302) or permanently (301).

client.redirect("http://google.com");
client.redirect_permanent("http://google.com");

Errors firing

You can abort the execution of an API method by raising errors with error.

Define your error like this:

use rustless::errors::{Error, ErrorRefExt};

#[deriving(Show)]
pub struct UnauthorizedError;

impl std::error::Error for UnauthorizedError {
    fn description(&self) -> &str {
        return "UnauthorizedError";
    }
}

And then throw:

client.error(UnauthorizedError);

Errors handling

By default Rustless wil respond all errors with status::InternalServerError.

Rustless can be told to rescue specific errors and return them in the custom API format.

api.error_formatter(|err, _media| {
    match err.downcast::<UnauthorizedError>() {
        Some(_) => {
            return Some(Response::from_string(StatusCode::Unauthorized, "Please provide correct `token` parameter".to_string()))
        },
        None => None
    }
});

Before and After callbacks

Blocks can be executed before or after every API call, using before, after, before_validation and after_validation.

Before and after callbacks execute in the following order:

  1. before
  2. before_validation
  3. validations
  4. after_validation
  5. the API call
  6. after

Steps 4, 5 and 6 only happen if validation succeeds.

The block applies to every API call within and below the current nesting level.

Secure API example

Api::build(|api| {
    api.prefix("api");
    api.version("v1", Versioning::Path);

    api.error_formatter(|err, _media| {
        match err.downcast::<UnauthorizedError>() {
            Some(_) => {
                return Some(Response::from_string(StatusCode::Unauthorized, "Please provide correct `token` parameter".to_string()))
            },
            None => None
        }
    });

    api.namespace("admin", |admin_ns| {

        admin_ns.params(|params| {
            params.req_typed("token", Valico::string())
        });

        // Using after_validation callback to check token
        admin_ns.after_validation(|&: _client, params| {

            match params.get("token") {
                // We can unwrap() safely because token in validated already
                Some(token) => if token.as_string().unwrap().as_slice() == "password1" { return Ok(()) },
                None => ()
            }

            // Fire error from callback is token is wrong
            return Err(Box::new(UnauthorizedError) as Box<Error>)

        });

        // This `/api/admin/server_status` endpoint is secure now
        admin_ns.get("server_status", |endpoint| {
            endpoint.handle(|client, _params| {
                {
                    let cookies = client.request.cookies();
                    let signed_cookies = cookies.signed();

                    let user_cookie = Cookie::new("session".to_string(), "verified".to_string());
                    signed_cookies.add(user_cookie);
                }

                client.text("Everything is OK".to_string())
            })
        });
    })
})

JSON responses

Rustless includes JsonWay library to offer both complex JSON building DSL and configurable serializers for your objects. See API docs for details.

Also feel free to use any other serialization library you want.

Swagger 2.0

Rustless has a basic implementation of Swagger 2.0 specification. It is not fully complete and in future we need to implement:

  • JSON Schema support (when some appropriate JSON Schema library will appear);
  • Security parts of the specification;

But now you can already use Swagger 2.0:

let mut app = rustless::Application::new(rustless::Api::build(|api| {
    // ...

    api.mount(swagger::create_api("api-docs"));

    // ...
}))

swagger::enable(&mut app, swagger::Spec {
    info: swagger::Info {
        title: "Example API".to_string(),
        description: Some("Simple API to demonstration".to_string()),
        contact: Some(swagger::Contact {
            name: "Stanislav Panferov".to_string(),
            url: Some("http://panferov.me".to_string()),
            ..std::default::Default::default()
        }),
        license: Some(swagger::License {
            name: "MIT".to_string(),
            url: "http://opensource.org/licenses/MIT".to_string()
        }),
        ..std::default::Default::default()
    },
    host: "localhost:4000".to_string(),
    ..std::default::Default::default()
});

After that you can use /api-docs path in Swagger UI to render your API structure.

Integration with PostgreSQL

We have an annotated example of such integration in postgres_example. Please try it and feel free to say your opinion.

Integration with Deuterium ORM

TODO: Example

rustless's People

Contributors

atte avatar badboy avatar bt avatar cswindle avatar doabit avatar frewsxcv avatar globin avatar killercup avatar mkovalchik avatar mkroli avatar paulkernfeld avatar pythoneer avatar r3c0d3x avatar s-panferov avatar syakhmi 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

rustless's Issues

Rewrite body reader

We need to use some common solution to read Iron's Body. Now there is just a .read_to_end() call that is useless if some middleware have already read the body.

params.req_typed("should_be_string", json_dsl::string()) accept integer values

is it a bug on valico ?

when I require the parameter to a string, the parameter is just "cast" into a string (or am I using the wrong function) ?

here's how to reproduce (I'm going to edit the code in some hours to make it more minimal)

extern crate rustless;
extern crate iron;
extern crate hyper;
extern crate valico;
extern crate rustc_serialize;

use rustc_serialize::json;
use valico::json_dsl;
use hyper::status::StatusCode;
use iron::Iron;
use rustless::{
    Application, Api, Nesting
};

fn main() {
    let api = Api::build(|api| {

        api.post("keywords", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("text", json_dsl::string());
            });
            endpoint.handle(|client, params| {
                let characters: Vec<char> = params.find("text").unwrap().to_string().chars().collect();

                client.text(json::encode(&characters).unwrap())
            })
        });

    });

    let app = Application::new(api);
    Iron::new(app).http("localhost:4000").unwrap();
}

calling with {"text" : 1 } in POST body returns HTTP OK, with

[
   "\"",
   "1",
  "\""
]

Relicense under dual MIT/Apache-2.0

This issue was automatically generated. Feel free to close without ceremony if
you do not agree with re-licensing or if it is not possible for other reasons.
Respond to @cmr with any questions or concerns, or pop over to
#rust-offtopic on IRC to discuss.

You're receiving this because someone (perhaps the project maintainer)
published a crates.io package with the license as "MIT" xor "Apache-2.0" and
the repository field pointing here.

TL;DR the Rust ecosystem is largely Apache-2.0. Being available under that
license is good for interoperation. The MIT license as an add-on can be nice
for GPLv2 projects to use your code.

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback. However, this is not the
primary motivation for me creating these issues. The Apache license also has
protections from patent trolls and an explicit contribution licensing clause.
However, the Apache license is incompatible with GPLv2. This is why Rust is
dual-licensed as MIT/Apache (the "primary" license being Apache, MIT only for
GPLv2 compat), and doing so would be wise for this project. This also makes
this crate suitable for inclusion and unrestricted sharing in the Rust
standard distribution and other projects using dual MIT/Apache, such as my
personal ulterior motive, the Robigalia project.

Some ask, "Does this really apply to binary redistributions? Does MIT really
require reproducing the whole thing?" I'm not a lawyer, and I can't give legal
advice, but some Google Android apps include open source attributions using
this interpretation. Others also agree with
it
.
But, again, the copyright notice redistribution is not the primary motivation
for the dual-licensing. It's stronger protections to licensees and better
interoperation with the wider Rust ecosystem.

How?

To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright, due to not being a "creative
work", e.g. a typo fix) and then add the following to your README:

## License

Licensed under either of

 * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.

and in your license headers, if you have them, use the following boilerplate
(based on that used in Rust):

// Copyright 2016 rustless Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

It's commonly asked whether license headers are required. I'm not comfortable
making an official recommendation either way, but the Apache license
recommends it in their appendix on how to use the license.

Be sure to add the relevant LICENSE-{MIT,APACHE} files. You can copy these
from the Rust repo for a plain-text
version.

And don't forget to update the license metadata in your Cargo.toml to:

license = "MIT OR Apache-2.0"

I'll be going through projects which agree to be relicensed and have approval
by the necessary contributors and doing this changes, so feel free to leave
the heavy lifting to me!

Contributor checkoff

To agree to relicensing, comment with :

I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option.

Or, if you're a contributor, you can check the box in this repo next to your
name. My scripts will pick this exact phrase up and check your checkbox, but
I'll come through and manually review this issue later as well.

Does not compile w/rustc 1.5.0

$ rustc -V
rustc 1.5.0 (3d7cd77e4 2015-12-04)

$ cargo build
   Compiling rustless v0.8.0
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41:32: 41:61 error: mismatched types:
 expected `cookie::jar::CookieJar<'_>`,
    found `cookie::jar::CookieJar<'static>`
(expected struct `cookie::jar::CookieJar`,
    found a different struct `cookie::jar::CookieJar`) [E0308]
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41             .unwrap_or_else(|| cookie::CookieJar::new(token));
                                                                                                                                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41:32: 41:61 help: run `rustc --explain E0308` to see a detailed expla
nation
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41:32: 41:61 note: Perhaps two different versions of crate `cookie` ar
e being used?
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41             .unwrap_or_else(|| cookie::CookieJar::new(token));
                                                                                                                                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43:46: 43:49 error: mismatched types:
 expected `cookie::jar::CookieJar<'static>`,
    found `cookie::jar::CookieJar<'_>`
(expected struct `cookie::jar::CookieJar`,
    found a different struct `cookie::jar::CookieJar`) [E0308]
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43         req.ext_mut().insert::<CookieJarKey>(jar);
                                                                                                                                                                               ^~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43:46: 43:49 help: run `rustc --explain E0308` to see a detailed expla
nation
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43:46: 43:49 note: Perhaps two different versions of crate `cookie` ar
e being used?
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43         req.ext_mut().insert::<CookieJarKey>(jar);
                                                                                                                                                                               ^~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43:13: 43:22 error: cannot infer an appropriate lifetime for lifetime 
parameter `'b` due to conflicting requirements [E0495]
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:43         req.ext_mut().insert::<CookieJarKey>(jar);
                                                                                                                                              ^~~~~~~~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:37:5: 45:6 help: consider using an explicit lifetime parameter as show
n: fn before<'a>(&self, req: &mut iron::Request<'a, 'a>) -> iron::IronResult<()>
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:37     fn before(&self, req: &mut iron::Request) -> iron::IronResult<(
)> {
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:38         let token = &self.secret_token;
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:39         let jar = req.headers().get::<header::Cookie>()
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:40             .map(|cookies| cookies.to_cookie_jar(token))
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:41             .unwrap_or_else(|| cookie::CookieJar::new(token));
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:42  
                                                                                                                                  ...
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:56:68: 56:71 error: mismatched types:
 expected `&cookie::jar::CookieJar<'_>`,
    found `&mut cookie::jar::CookieJar<'_>`
(expected struct `cookie::jar::CookieJar`,
    found a different struct `cookie::jar::CookieJar`) [E0308]
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:56                 res.headers.set(header::SetCookie::from_cookie_jar(
jar));

 ^~~
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:56:68: 56:71 help: run `rustc --explain E0308` to see a detailed expla
nation
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:56:68: 56:71 note: Perhaps two different versions of crate `cookie` ar
e being used?
/home/user/.multirust/toolchains/stable/cargo/registry/src/github.com-0a35038f75765ae4/rustless-0.8.0/src/batteries/cookie.rs:56                 res.headers.set(header::SetCookie::from_cookie_jar(
jar));

 ^~~
error: aborting due to 4 previous errors
Could not compile `rustless`.

before/after handler

hi,

what is the best way to respond with an error in the before handler? the error_response! macro seems to be not exported, so there is no comfortable way to do that?!

allow more data types in json responses

pub fn json(mut self, result: &JsonValue) -> ClientResult<'a> {
self.set_json_content_type();
self.response.replace_body(Box::new(result.to_string()));
Ok(self)
}

some kind of:

use serde_json::ser::to_string;
use serde::ser::Serialize;

fn json<T>(mut self, result: &T) -> ClientResult<'a>
  where
    T: Serialize,
{
  self.set_json_content_type();
  self.response.replace_body(Box::new(to_string(result).unwrap()));
  Ok(self)
}

Future plans

  • Per-server and per-request logging.
  • Hooks for plugins.
  • Auth strategies.
  • Error DSL.
  • More Swagger.

Optional iron dependency?

I'm a little confused with line default = ["iron_backend"] in Cargo.toml. Why is it optional?

Currently it is not working src/lib.rs:13:1: 13:19 error: can't find crate foriron[E0463]:

[dependencies.rustless]
version = "0.7.*"
default-features = false

It is for the future? Multi-backend?

Got internal server when sending invalid json, how to get more details

I got this very simple api

extern crate rustless;
extern crate iron;
extern crate hyper;
extern crate valico;
extern crate rustc_serialize;

use rustc_serialize::json;
use valico::json_dsl;
use hyper::status::StatusCode;
use iron::Iron;
use rustless::{
    Application, Api, Nesting
};

fn main() {
    let api = Api::build(|api| {

        api.post("keywords", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("text", json_dsl::string());
            });

            endpoint.handle(|client, params| {
                let characters: Vec<char> = params.find("text").unwrap().to_string().chars().collect();

                client.text(json::encode(&characters).unwrap())
            })
        });

    });

    let app = Application::new(api);
    Iron::new(app).http("localhost:4000").unwrap();
}

and when I try

echo '{ "text" :  oops_forgot_quotes }'  | http POST  http://127.0.0.1:4000/keywords

I got nothing in the server side, only a 500 on client side.

Inconsistent dependencies of Cookie and openssl

I just discovered rustless and wanted to give it a try on Linux Ubuntu 16.04. I have rustc 1.13 and cargo 0.13.0-nightly.

The build of rustless fails because of conflicting dependences on two different versions of openssl. As far as I can tell from the Cargo.lock file, these come from dependencies on two different versions of cookie. rustless wants cookie 0.4.0 and hyper wants cookie 0.2.5.

Since I am new to the rust world, I have no clue as to how to resolve this.

Better doc for swagger

I'm trying to use the swagger backend, but I can't find how to associate a handler to an API path?

Error compiling rustless

I've been testing out rustless for a while but the latest version currently does not compile due to the following error:

native library `openssl` is being linked to by more than one package, and can only be linked to by one package

  openssl-sys v0.6.7
  openssl-sys v0.7.0

Does not compile (neither beta nor nightly)

Compiling the complex example fails. I know, there are many problems with dependencies as well, but I this project hadn't had any activity for some time now. I hope rustless is still alive...

Basic examples fail to build

When I try to build the basic example, it gives me this error:

src/main.rs:19:26: 19:29 error: macro undefined: 'dsl!'
src/main.rs:19     let api = Api::build(dsl!(|api| {

It appears the dsl_macros crate needs to be included... Though after including it, I get:

error: unresolved import `rustc::plugin`. There is no `plugin` in `rustc` ... dsl_macros-0.1.2/src/lib.rs:8 use rustc::plugin;

I wasn't able to get other examples to work, either, using rustc 1.5.0, beta, or nightly.

Also note the example on the front page of rustless.com has extern crate rustless; twice. When building it I get error: box expression syntax is experimental; you can callBox::newinstead. (see issue #27779), and after fixing:

error: The attribute `phase` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
src/main.rs:3 #[phase(plugin)]

Parameter values exist in handler but aren't included in the response

When returning a parameter value as text in a response, I don't see the expected response (e.g. the text value isn't included).

The handler looks like it has a correct setup, however, using a deserialized parameter as the response doesn't appear in the client, but a static string does.

Here's an example, and its setup is in Kawaii's src file. cargo run echo should be fine to compile and run the example.

Below are the requests and responses for the scenarios I mentioned.

$ curl -i -H Accept:application/vnd.chat.v1+json -X POST http://localhost:3000/api/echo_stub/ayy
HTTP/1.1 200 OK
Date: Thu, 18 Aug 2016 01:57:24 GMT
Content-Type: text/plain
Transfer-Encoding: chunked

๐Ÿ˜…
$ curl -i -H Accept:application/vnd.chat.v1+json -X POST http://localhost:3000/api/echo_real/ayy
HTTP/1.1 200 OK
Date: Thu, 18 Aug 2016 01:57:20 GMT
Transfer-Encoding: chunked
Content-Type: text/plain

Move to Serde

It seems like serde (and likewise serde_json) is becoming the canonical way to serialize/deserialize JSON in rust. Even the rustc-serialize docs say:

While this library is the standard way of working with JSON in Rust, there is a next-generation library called Serde that's in the works (it's faster, overcomes some design limitations of rustc-serialize and has more features). You might consider using it when starting a new project or evaluating Rust JSON performance.

So I think that rustless must move to Serde too.

JSONway not working as response

    api.get("meta/test", |endpoint| {
        endpoint.handle(|client, params| {
            let response = jsonway::object(|json| { 
                json.set("hello", "world".to_string()); 
                json.set("world", "hello".to_string());
            });
            client.json(&response)
        })
    });

I try to do this but it doesn't work. How do I use jsonway as response?
This is the error message:

src/main.rs:40:22: 40:31 error: mismatched types:
 expected `&rustc_serialize::json::Json`,
    found `&jsonway::ObjectBuilder`
(expected enum `rustc_serialize::json::Json`,
    found struct `jsonway::ObjectBuilder`) [E0308]
src/main.rs:40          client.json(&response)

Not compiled...

Hello,

In last version iron in Cargo.toml in section dep. set:
iron/iron@26cd9ee

hyper = "0.5"

but in rustless

[dependencies.iron]
version = "*"

And because of this error is obtained:

/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:46:45: 46:58 error: mismatched types:
expected &hyper::header::Headers,
found &hyper::header::Headers
(expected struct hyper::header::Headers,
found a different struct hyper::header::Headers) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:46 fn headers(&self) -> &header::Headers { &self.headers }
^~~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:47:43: 47:55 error: mismatched types:
expected &hyper::method::Method,
found &hyper::method::Method
(expected enum hyper::method::Method,
found a different enum hyper::method::Method) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:47 fn method(&self) -> &method::Method { &self.method }
^~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:66:34: 66:45 error: mismatched types:
expected hyper::status::StatusCode,
found hyper::status::StatusCode
(expected enum hyper::status::StatusCode,
found a different enum hyper::status::StatusCode) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:66 status: Some(resp.status),
^~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:67:30: 67:42 error: mismatched types:
expected hyper::header::Headers,
found hyper::header::Headers
(expected struct hyper::header::Headers,
found a different struct hyper::header::Headers) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:67 headers: resp.headers,
^~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:77:38: 77:53 error: mismatched types:
expected hyper::status::StatusCode,
found hyper::status::StatusCode
(expected enum hyper::status::StatusCode,
found a different enum hyper::status::StatusCode) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:77 status: Some(response.status),
^~~~~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:78:34: 78:50 error: mismatched types:
expected hyper::header::Headers,
found hyper::header::Headers
(expected struct hyper::header::Headers,
found a different struct hyper::header::Headers) [E0308]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/backend/iron.rs:78 headers: response.headers,
^~~~~~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/batteries/cookie.rs:56:29: 56:73 error: the trait hyper::header::Header is not implemented for the type hyper::header::common::set_cookie::SetCookie [E0277]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/batteries/cookie.rs:56 res.headers.set(header::SetCookie::from_cookie_jar(jar));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/batteries/cookie.rs:56:29: 56:73 error: the trait hyper::header::HeaderFormat is not implemented for the
type hyper::header::common::set_cookie::SetCookie [E0277]
/home/denis/.cargo/git/checkouts/rustless-7db87dce98c9dc38/master/src/batteries/cookie.rs:56 res.headers.set(header::SetCookie::from_cookie_jar(jar));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 8 previous errors
Could not compile rustless.

Thank you.

compilation failure: method `call` is not a member of trait `Handler`

/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:52:5: 61:6 error: method `call` is not a member of trait `Handler`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:52     fn call(&self, req: &mut iron::Request) -> iron::IronResult<iron::Response> {
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:53         self.call_with_not_found(req).map(|resp| {
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:54             iron::Response {
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:55                 status: Some(resp.status),
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:56                 headers: resp.headers,
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:57                 body: resp.body,
                                                                                                ...
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/batteries/cookie.rs:39:39: 39:54 error: use of undeclared type name `header::Cookies`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/batteries/cookie.rs:39         let jar = req.headers().get::<header::Cookies>()

Stronger parameter typing

Hi,

Don't you think it would be nice to have the possibility to type the handler parameters more ?

I really like the way rustless checks and coerces a handler parameters, but it seems they are only used to check the parameters, but in the handler, the parameters must be casted again to their right type.

It might be clearer with an example ๐Ÿ˜‰:

ns.get("", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("q", json_dsl::string());
                params.opt_typed("lat", json_dsl::f64());
            });
            endpoint.handle(|client, params| {
                let q = params.find("q").unwrap().as_string().unwrap().to_string(); // <- for required param we need 2 'safe' unwrap + one 'cast'
                let lat = params.find("lat").and_then(|p| p.as_f64()); // for optional params need one cast
               // do some stuff
            })
        });

I think it would be nice to give a struct representing the parameters:

struct MyParams {
    q: String,
    lat: Option<f64>
}

and then use it it in the parameter checking phase and in the handler.

we might use them like this (only a proposal):

ns.get("", |endpoint| {
            endpoint.typed_params<MyParams>();
            // we could also have mechanism to check the validity of the filled params 
            // after the construction (like mutually_exclusive mechanims):
            // endpoint.typed_params<MyParams>(|p| check(p)); // or with a Trait

            endpoint.typed_handle<MyParams>(|client, params| {
               print!("params: {}, {}", params.q, params.lat);
               // do some stuff
            })
        });

It would be really great if there was a magical way to build a MyParams from the parameters, but we could maybe add code (or a macro ?) in the typed_params method to build it.

If you like the idea, I can try to work on this, but my rust level is a bit low for the moment ๐Ÿ˜•

Could not compile `queryst`

When I try to cargo run, I get te following fail:

Build failed, waiting for other jobs to finish...
error: error writing dependencies to `/Users/milanvanschaik/Development/rust-test/target/debug/deps/plugin-ba6ebe376fbbceb7.d`: Permission denied (os error 13)
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15:36: 15:54 error: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15     fn get_type(&self) -> TypeId { TypeId::of::<Self>() }
                                                                                                                                      ^~~~~~~~~~~~~~~~~~
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15:36: 15:54 note: `Self` does not have a constant size known at compile-time
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15     fn get_type(&self) -> TypeId { TypeId::of::<Self>() }
                                                                                                                                      ^~~~~~~~~~~~~~~~~~
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15:36: 15:54 error: the trait `core::marker::Reflect` is not implemented for the type `Self` [E0277]
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/typeable-0.0.9/src/lib.rs:15     fn get_type(&self) -> TypeId { TypeId::of::<Self>() }
                                                                                                                                      ^~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
Could not compile `queryst`.

My Rust version:

rustc 1.0.0-nightly (c89de2c56 2015-03-28) (built 2015-03-28)

Seems to fail at queryst which is also a dependency of Rustless. Any idea what is going wrong?

Update:
With rustc 1.0.0-nightly (6cf3b0b74 2015-03-30) (built 2015-03-30) I get the following:

/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/queryst-0.1.13/src/lib.rs:5:14: 5:31 error: expected ident, found `"rustc-serialize"`
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/queryst-0.1.13/src/lib.rs:5 extern crate "rustc-serialize" as serialize;
                                                                                                               ^~~~~~~~~~~~~~~~~
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/valico-0.7.4/src/lib.rs:8:14: 8:31 error: expected ident, found `"rustc-serialize"`
/Users/milanvanschaik/.cargo/registry/src/github.com-1ecc6299db9ec823/valico-0.7.4/src/lib.rs:8 extern crate "rustc-serialize" as rustc_serialize;

Complete auth example

Do you know a project that uses rustless (for the pure API) in combination with iron (for a usual web interface) that has protected areas?
I'm interested in the authentication part. How would you provide sessions?
Or do you know any other examples (e.g. using JWT?)?

Access iron request object

How can I get access to the iron request?

For example with the persistent crate, I'd do s.th. like this:

let mutex = request.get::<Write<Store>>().unwrap();

but client.request is not the irons request object.

JSON API example?

Looking at the postgres example,
and I am wondering if you would be able to extend this to conform to JSON-API,
with Create, Update, and Delete statements as well.

I have previously attempted something like this using nickel.rs,
as this is a use case that I have to satisfy before I can start writing back end code using rust.
I found it rather convoluted at the time with nickel,
and want to know if it would be any easier using rustless -
I'm certainly hoping so!

Basic usage example doesn't compile on rustc 1.9

When trying to compile the basic usage example, some errors are thrown:

src/main.rs:104:35:` 104:55 error: mismatched types:
 expected `rustless::server::status::StatusCode`,
    found `hyper::status::StatusCode`
(expected enum `rustless::server::status::StatusCode`,
    found enum `hyper::status::StatusCode`) [E0308]
src/main.rs:104                 client.set_status(StatusCode::NotFound);
                                                  ^~~~~~~~~~~~~~~~~~~~
src/main.rs:104:35: 104:55 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:113:44: 113:59 error: mismatched types:
 expected `Box<valico::json_dsl::coercers::Coercer + Send + Sync + 'static>`,
    found `Box<valico::json_dsl::Coercer + Send + Sync + 'static>`
(expected trait `valico::json_dsl::coercers::Coercer`,
    found trait `valico::json_dsl::Coercer`) [E0308]
src/main.rs:113                     params.req_typed("id", json_dsl::u64())
                                                           ^~~~~~~~~~~~~~~
src/main.rs:113:44: 113:59 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:124:53: 124:68 error: mismatched types:
 expected `Box<valico::json_dsl::coercers::Coercer + Send + Sync + 'static>`,
    found `Box<valico::json_dsl::Coercer + Send + Sync + 'static>`
(expected trait `valico::json_dsl::coercers::Coercer`,
    found trait `valico::json_dsl::Coercer`) [E0308]
src/main.rs:124                         params.req_typed("user_id", json_dsl::u64());
                                                                    ^~~~~~~~~~~~~~~
src/main.rs:124:53: 124:68 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:125:50: 125:68 error: mismatched types:
 expected `Box<valico::json_dsl::coercers::Coercer + Send + Sync + 'static>`,
    found `Box<valico::json_dsl::Coercer + Send + Sync + 'static>`
(expected trait `valico::json_dsl::coercers::Coercer`,
    found trait `valico::json_dsl::Coercer`) [E0308]
src/main.rs:125                         params.req_typed("name", json_dsl::string())
                                                                 ^~~~~~~~~~~~~~~~~~
src/main.rs:125:50: 125:68 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to 4 previous errors
Could not compile `beam6`.

The first one is easy, instead of use hyper::status::StatusCode;, use use rustless::server::status::StatusCode;.
I don't understand how to fix the three other errors. Any idea?

Server stops accepting connections after a while

I have very little information regarding the issue yet, and this could be an Iron issue. The issue is basically that after some indeterminate amount of time (typically a few hours to a few days) the server seems to stop accepting new connections. It doesn't panic or return any errors anywhere, as far as I can tell. Any requests I attempt to make to it will time out.

compilation failure: unresolved import `std::io`. There is no `io` in `std`

Due to some change in rust today:

     Running `rustc /home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/router-0.0.7/src/lib.rs --crate-name router --crate-type lib -g -C metadata=6f9dfc155d43f4c6 -C extra-filename=-6f9dfc155d43f4c6 --out-dir /home/xav/toysarust/target/deps --emit=dep-info,link -L dependency=/home/xav/toysarust/target/deps -L dependency=/home/xav/toysarust/target/deps --extern route-recognizer=/home/xav/toysarust/target/deps/libroute-recognizer-83c1c7fcff8156ec.rlib --extern iron=/home/xav/toysarust/target/deps/libiron-56f3523962e6449c.rlib -Awarnings -L native=/usr/lib/x86_64-linux-gnu -L native=/home/xav/toysarust/target/build/time-2215e3ac75eaa2c7/out`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/errors.rs:2:5: 2:12 error: unresolved import `std::io`. There is no `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/errors.rs:2 use std::io;
                                                                                             ^~~~~~~
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/request.rs:2:5: 2:21 error: unresolved import `std::io::net::ip`. Could not find `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/request.rs:2 use std::io::net::ip;
                                                                                                      ^~~~~~~~~~~~~~~~
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/simple_request.rs:1:5: 1:12 error: unresolved import `std::io`. There is no `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/simple_request.rs:1 use std::io;
                                                                                                             ^~~~~~~
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/simple_request.rs:3:5: 3:21 error: unresolved import `std::io::net::ip`. Could not find `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/simple_request.rs:3 use std::io::net::ip;
                                                                                                             ^~~~~~~~~~~~~~~~
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/response.rs:1:5: 1:12 error: unresolved import `std::io`. There is no `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/response.rs:1 use std::io;
                                                                                                       ^~~~~~~
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:2:5: 2:21 error: unresolved import `std::io::net::ip`. Could not find `io` in `std`
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/rustless-0.3.0/src/backend/iron.rs:2 use std::io::net::ip;
                                                                                                   ^~~~~~~~~~~~~~~~
error: aborting due to 6 previous errors
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/router-0.0.7/src/lib.rs:2:10: 2:18 error: unknown `allow` attribute: `unstable`, #[deny(unknown_lints)] on by default
/home/xav/.cargo/registry/src/github.com-1ecc6299db9ec823/router-0.0.7/src/lib.rs:2 #![allow(unstable)]
                                                                                             ^~~~~~~~
Build failed, waiting for other jobs to finish...
error: aborting due to previous error
Could not compile `rustless`.

No longer compiles with nightly

I see this project hasn't had any updates in 4 months; is it still active?
Any interest in patches to get things working with nightly?

src/backend/iron.rs:68:27: 68:36 error: mismatched types:
expected core::option::Option<Box<iron::response::WriteBody + Send + 'static>>,
found core::option::Option<Box<std::io::Read + Send + 'static>>

src/batteries/cookie.rs:41:32: 41:61 error: mismatched types:
expected cookie::jar::CookieJar<'_>,
found cookie::jar::CookieJar<'static>

src/batteries/cookie.rs:56:68: 56:71 error: mismatched types:
expected &cookie::jar::CookieJar<'_>,
found &mut cookie::jar::CookieJar<'_>

Update dependencies

im on the latest rustless version (v0.7.3)

    Name                                   Project Ver  SemVer Compat  Latest Ver
    rustless->bodyparser                      0.0.6          --          0.4.1
    rustless->cookie                          0.2.5          --          0.3.0
    rustless->hyper                           0.8.1        0.9.10        0.9.10
    rustless->iron                            0.3.1        0.4.0         0.4.0
    rustless->jsonway                         0.3.5          --          1.0.0
    rustless->lazy_static                     0.1.16         --          0.2.1
    rustless->queryst                         0.1.16         --          1.0.0
    rustless->traitobject                     0.0.1        0.0.3         0.0.3
    rustless->url                             0.5.9        1.2.0         1.2.0
    rustless->valico                          0.8.2          --          1.0.0

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.