• Stars
    star
    117
  • Rank 301,828 (Top 6 %)
  • Language
    Dart
  • License
    BSD 3-Clause "New...
  • Created almost 10 years ago
  • Updated about 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

RPC package for building server-side RESTful Dart APIs.

RPC

Build Status

Discontinued

NOTE: This package has been discontinued, and is no longer being actively maintained. We recommend you consider other RPC mechanisms, such as our gRPC package.

Description

Light-weight RPC package for creating RESTful server-side Dart APIs. The package supports the Google Discovery Document format for message encoding and HTTP REST for routing of requests.

The discovery documents for the API are automatically generated and are compatible with existing Discovery Document client stub generators (see the "Calling the API" section below for more details). This makes it easy to create a server side API that can be called by any client language for which there is a Discovery Document client stub generator.

Simple Example

Getting started is simple! The example below gives a quick overview of how to create an API and in the following sections a more elaborate description follows of how to build the API and setup an API server.

@ApiClass(version: 'v1')
class Cloud {
  @ApiMethod(method: 'GET', path: 'resource/{name}')
  ResourceMessage getResource(String name) {
    ... find resource of name {resourceName} ...
    return new ResourceMessage()
        ..id = resource.id
        ..name = resource.name
        ..capacity = resource.capacity;
  }

  @ApiMethod(method: 'POST', path: 'resource/{name}/update')
  VoidMessage updateResource(String name, UpdateMessage request) {
    ... process request, throw on error ...
  }
}

class ResourceMessage {
  int id;
  String name;
  int capacity;
}

class UpdateMessage {
  int newCapacity;
}

Two complete examples using respectively dart:io and shelf can be found at Example.

Usage

Terminology

We use the following concepts below when describing how to build your API.

  • Top-level class - This is the API entry-point. It describes the API name and version. The top-level class is defined via the ApiClass annotation.
  • Resource - Resources are used to group methods together for a cleaner API structure. Class fields annotated with @ApiResource are exposed as resources.
  • Method - Methods are what can be invoked. Only methods annotated with @ApiMethod are exposed as remotely accessible methods.
  • Schema - Schemas are used to describe response and the request messages passed in the body of the HTTP request.
  • Properties - A schema contains properties. Each property can optionally be further restricted by the ApiProperty annotation.
Main API Class

Defining an API starts with annotating a class with the @ApiClass annotation. It must specify at least the version field. The API name can optionally be specified via the name field and will default to the class name in camel-case if omitted.

@ApiClass(
  name: 'cloud',  // Optional (default is 'cloud' since class name is Cloud).
  version: 'v1',
  description: 'My Dart server side API' // optional
)
class Cloud  {
  (...)
}

The above API would be available at the path /cloud/v1. E.g. if the server was serving on http://localhost:8080 the API base url would be http://localhost:8080/cloud/v1.

Methods

Inside of your API class you can define public methods that will correspond to methods that can be called on your API.

For a method to be exposed as a remote API method it must be annotated with the @ApiMethod annotation specifying a unique path used for routing requests to the method.

The @ApiMethod annotation also supports specifying the HTTP method used to invoke the method. The method field is used for this. If omitted the HTTP method defaults to GET.

A description of the method can also be specified using the description field. If omitted it defaults to the empty string.

Response message (return value)

A method must always return a response. The response can be either an instance of a class or a future of the instance. In the case where a method has no response the predefined VoidMessage class should be returned.

Example method returning nothing:

@ApiMethod(path: 'voidMethod')
VoidMessage myVoidMethod() {
  ...
  return null;
}

Example method returning class:

class MyResponse {
  String result;
}

@ApiMethod(path: 'someMethod')
MyResponse myMethod() {
  ...
  return new MyResponse();
}

Example method returning a future:

@ApiMethod(path: 'futureMethod')
Future<MyResponse> myFutureMethod() {
  ...
    completer.complete(new MyResponse();
  ...
  return completer.future;
}

The MyResponse class must be a non-abstract class with an unnamed constructor taking no required parameters. The RPC backend will automatically serialize all public fields of the the MyResponse instance into JSON corresponding to the generated Discovery Document schema.

Parameters

Method parameters can be passed in three different ways.

  • As a path parameter in the method path (supported on all HTTP methods)
  • As a query string parameter (supported for GET and DELETE)
  • As the request body (supported for POST and PUT)

Path parameters and the request body parameter are required. The query string parameters are optional named parameters.

Example of a method using POST with both path parameters and a request body:

@ApiMethod(
  method: 'POST',
  path: 'resource/{name}/type/{type}')
MyResponse myMethod(String name, String type, MyRequest request) {
  ...
  return new MyResponse();
}

The curly brackets specify path parameters and must appear as positional parameters in the same order as on the method signature. The request body parameter is always specified as the last parameter.

Assuming the above method was part of the Cloud class defined above the url to the method would be:

http://localhost:8080/cloud/v1/resource/foo/type/storage

where the first parameter name would get the value foo and the type parameter would get the value storage.

The MyRequest class must be a non-abstract class with an unnamed constructor taking no arguments. The RPC backend will automatically create an instance of the MyRequest class, decode the JSON request body, and set the class instance's fields to the values found in the decoded request body.

If the request body is not needed it is possible to use the VoidMessage class or change it to use the GET HTTP method. If using GET the method signature would instead become.

@ApiMethod(path: '/resource/{name}/type/{type}')
MyResponse myMethod(String name, String type) {
   ...
   return new MyResponse(); 
}

When using GET it is possible to use optional named parameters as below.

@ApiMethod(path: '/resource/{name}/type/{type}')
MyResponse myMethod(String name, String type, {String filter}) {
   ...
   return new MyResponse(); 
}

in which case the caller can pass the filter as part of the query string. E.g.

http://localhost:8080/cloud/v1/resource/foo/type/storage?filter=fast

More about Request/Response Messages

The data sent either as a request (using HTTP POST and PUT) or as a response body corresponds to a non-abstract class as described above.

The RPC backend will automatically decode HTTP request bodies into class instances and encode method results into an HTTP response's body. This is done according to the generated Discovery Document schemas.

Only the public fields of the classes are encoded/decoded. Currently supported types for the public fields are int, double, bool, String, DateTime, List, Map<String, SomeType>, and another message class.

A field can be further annotated using the @ApiProperty annotation to specify default values, format of an int, BigInt, or double specifying how to handle it in the backend, min/max value of an int property, and whether a property is required.

For int properties the format field is used to specify the size of the integer. It can take the values int32, or uint32. BigInt properties can be specified as int64 or uint64. The 64-bit variants will be represented as String in the JSON objects.

For int and BigInt properties the minValue and maxValue fields can be used to specify the min and max value of the integer. For 64-bit variants these fields must be specified as Strings to reduce ambiguity and to allow specifying the full range of values for uint64 given the limitations of Dart 2 integers. See also dart-lang/sdk#33893.

For double properties the format parameter can take the value double or float.

The defaultValue field is used to specify a default value. The required fields is used to specify whether a field is required.

Example schema:

class MyRequest {
   @ApiProperty(
     format: 'uint32',
     defaultValue: 40,
     minValue: 0,
     maxValue: 150)
   int age;

   @ApiProperty(format: 'float')
   double averageAge;

   @ApiProperty(
     format: 'uint64',
     minValue: '18446744073709551116', // 2^64 - 500
     maxValue: '18446744073709551216') // 2^64 - 400
   BigInt numberOfNeutrons;
}
Resources

Resources can be used to provide structure to your API by grouping certain API methods together under a resource. To create an API resource you will add a field to the class annotated with the @ApiClass annotation. The field must point to another class (the resource) containing the methods that should be exposed together for this resource. The field must be annotated with the @ApiResource annotation. By default the name of the resource will be the field name in camel-case. If another name is desired the name field can be used in the @ApiResource annotation.

Example resource API:

@ApiClass(version: 'v1')
class Cloud {

  @ApiResource(name: 'myResource')
  MyResource aResource = new MyResource();
  
  ...
}

class MyResource {
  
  @ApiMethod(path: 'someMethod')
  MyResponse myResourceMethod() { return new MyResponse(); }
}

Notice the @ApiResource annotation is on the field rather than the resource class. This allows for a resource class to be used in multiple places (e.g. different versions) of the API.

Also notice the path of the MyResource.myResourceMethod method is independent from the resource. E.g. if MyResource was used in the previous mentioned Cloud API the method would be exposed at the url http://<server ip>:<port>/cloud/v1/someMethod.

API Server

When having annotated your classes, resources, and methods you must create an ApiServer to route the HTTP requests to your methods. Creating a RPC API server is done by first creating an instance of the ApiServer class and calling the addApi method with an instance of the class annotated with the @ApiClass annotation.

You can choose to use any web server framework you prefer for serving HTTP requests. The rpc-examples github repository (https://github.com/dart-lang/rpc-examples) includes examples for both the standard dart:io HttpServer as well as an example using the shelf middleware.

E.g. to use dart:io you would do something like:

final ApiServer _apiServer = new ApiServer();

main() async {
  _apiServer.addApi(new Cloud());
  HttpServer server = await HttpServer.bind(InternetAddress.ANY_IP_V4, 8080);
  server.listen(_apiServer.httpRequestHandler);
}

The above example uses the default provided ApiServer HTTP request handler which converts the HttpRequest to a HttpApiRequest and forwards it along. A custom HTTP request handler doing the conversion to the HttpApiRequest class and calling the ApiServer.handleHttpApiRequest method itself can also be used if more flexibility is needed.

Notice that the ApiServer is agnostic of the HTTP server framework being used by the application. The RPC package does provide a request handler for the standard dart:io HttpRequest class. There is also a shelf_rpc package which provides the equivalent for shelf (see the example for how this is done). However as the RPC ApiServer is using its own HttpApiRequest class any framework can be used as long as it converts the HTTP request to a corresponding HttpApiRequest and calls the ApiServer.handleHttpApiRequest method.

The result of calling the handleHttpApiRequest method is returned as an HttpApiResponse which contains a stream with the encoded response or in the case of an error it contains the encoded JSON error as well as the exception thrown internally.

Errors

There are a couple of predefined error classes that can be used to return an error from the server to the client. They are:

  • any RpcError(HTTP status code, Error name, Any message)
  • 400 BadRequestError('You sent some data we don't understand.')
  • 404 NotFoundError('We didn't find the api or method you are looking for.')
  • 500 ApplicationError('The invoked method failed with an exception.')
  • 500 Some internal exception occurred and it was not due to a method invocation.

If one of the above exceptions are thrown by the server API implementation it will be sent back as a serialized json response as described below. Any other exception thrown will be wrapped in the ApplicationError exception containing the toString() version of the internal exception as the method.

The JSON format for errors is:

{
  "error": {
    "code": <http status code>,
    "message": <error message>
  }
}      

In addition to the basic way of returning an http status code and an error message, you can attach RpcErrorDetail objects to your RpcError (as specified in the Google JSON style guide):

throw new RpcError(403, 'InvalidUser', 'User does not exist')
    ..errors.add(new RpcErrorDetail(reason: 'UserDoesNotExist'));

This will return the JSON:

{
  "error": {
    "code": 403,
    "message": "User does not exist",
    "errors": [
      {"reason": "UserDoesNotExist"}
    ]
  }
}      
Generating client stub code

Once your server API is written you can generate a Discovery Document describing the API and use it to generate a client stub library to call the server from your client.

There are two ways to generate a Discovery Document from your server API.

  • Use the rpc:generate script to generate it from the commandline
  • Retrieve it from a running server instance

Using the rpc:generate script you can generate a Discovery Document by running the script on the file where you put the class annotated with @ApiClass. Assuming your @ApiClass class is in a file 'lib/server/cloudapi.dart' you would write:

cd <your package directory>
mkdir json
pub run rpc:generate discovery -i lib/server/cloudapi.dart > json/cloud.json

In order for the rpc:generate script to work the API class (@ApiClass class) must have a default constructor taking no required arguments.

The other way to retrive a Discovery Document if from a running server instance. This requires the Discovery Service to be enabled. This is done by calling the ApiServer.enableDiscoveryApi() method on the ApiServer, see Example. for details.

After enabling the Discovery Service deploy the server and download the Discovery Document. For example if we have the 'cloud' API from the above example the Discovery Document can be retrieved from the deployed server by:

URL='https://your_app_server/discovery/v1/apis/cloud/v1/rest'
mkdir json
curl -o json/cloud.json $URL

Once you have the Discovery Document you can generate a client stub library using a Discovery Document client API generator. For Dart we have the Discovery API Client Generator. Discovery Document generators for other languages can also be used to call your API from e.g Python or Java.

If you want to generate a standalone client library for calling your server do:

pub global activate discoveryapis_generator
pub global run discoveryapis_generator:generate package -i json -o client

This will create a new Dart package with generated client stubs for calling each of your API methods. The generated library can be used like any of the other Google Client API libraries, some samples here.

If you want to generate a client stub code that should be integrated into an existing client you can instead do:

pub global activate discoveryapis_generator
pub global run discoveryapis_generator:generate files -i json -o <path to existing client package>

This will just generate a file in the directory specified by the '-o' option. NOTE: you might have to modify the existing client's pubspec.yaml file to include the packages required by the generated client stub code.

More Repositories

1

angular.dart

Legacy source repository. See github.com/dart-lang/angular
1,250
star
2

stagehand

Dart project generator - web apps, console apps, servers, and more.
Dart
660
star
3

intl

Internationalization and localization support
Dart
527
star
4

dart-samples

Various samples and examples in Dart
Dart
468
star
5

sdk

The Dartino project was an experiment seeking to improve productivity when writing application code for embedded devices.
Dart
325
star
6

dart-services

The server backend for a web based interactive Dart service
Dart
312
star
7

angular.dart.tutorial

AngularDart tutorial
Dart
236
star
8

pub_server

Reusable components for making a pub package server
Dart
215
star
9

bleeding_edge-DEPRECATED-USE-SDK-INSTEAD

NO LONGER SYNCING. use dart-lang/sdk
Dart
210
star
10

www.dartlang.org

DEPRECATED - OLD SITE for DART
HTML
197
star
11

dart-tutorials-samples

Sample code for "A Game of Darts" tutorial
Dart
187
star
12

polymer-dart

Polymer support for Dart
Dart
181
star
13

ts2dart

ts2dart TypeScript to Dart transpiler
TypeScript
177
star
14

js_facade_gen

Generates package:js Javascript interop facades for arbitrary TypeScript libraries
TypeScript
159
star
15

dev_compiler

DEPRECATED - Moved to main SDK
JavaScript
136
star
16

intl_translation

Message extraction and code generation from translated messages for the intl package
Dart
128
star
17

tflite_native

A Dart interface to TensorFlow Lite (tflite) through dart:ffi
Dart
125
star
18

file.dart

A generic file system abstraction for Dart.
Dart
121
star
19

js-interop-deprecated

Deprecated: code is now in the SDK repo
Dart
114
star
20

one-hour-codelab

Learn how to build a webapp with Dart in one hour.
Dart
104
star
21

dart-up-and-running-book

ARCHIVE - The DocBook (XML) and code that make up the O'Reilly book Dart: Up and Running
Dart
98
star
22

isolate

Makes working with Dart isolates easier.
Dart
90
star
23

dart_docker

Docker images for Dart
Shell
85
star
24

dart_enhancement_proposals

This repo contains info on DEP - Dart Enhancement Proposal
Dart
81
star
25

dart-protoc-plugin

Dart plugin for protobuf compiler (protoc)
79
star
26

discoveryapis_generator

Create API Client libraries based on the API's Discovery documentation
Dart
77
star
27

graphs

Graph algorithms
Dart
74
star
28

angular_analyzer_plugin

WORK MOVED TO dart-lang/angular repository
69
star
29

di.dart

DEPRECATED
Dart
66
star
30

googleapis_examples

Examples for accessing Google APIs with Dart
Dart
66
star
31

angular_components_example

A sample usage of https://github.com/dart-lang/angular_components
Dart
66
star
32

polymer-dart-patterns

Small, useful, snippets/samples that show how to do things the Polymer.dart way.
HTML
63
star
33

conference_app

Dart
56
star
34

paper-elements

Polymer.dart <=0.17.0 wrappers for Polymer's paper-elements
HTML
52
star
35

pub-dartlang

DEPRECATED - old pub.dartlang.org site in Python
Python
43
star
36

dump-info-visualizer

A visualizer for the JSON data produced by the dart2js --dump-info command
Dart
43
star
37

googleapis_auth

Obtain OAuth 2.0 credentials to access Google APIs
Dart
38
star
38

http_server

Utility classes for HTTP server
Dart
37
star
39

bazel

Bazel support for Dart projects [EXPERIMENTAL]
Dart
34
star
40

appengine_samples

Dart App Engine samples
Dart
33
star
41

core-elements

Polymer core-* elements wrapped or ported for Dart
HTML
33
star
42

route.dart

MOVE to https://github.com/dart-lang/angular/tree/master/angular_router
Dart
29
star
43

ton80

A benchmark suite for Dart
Dart
25
star
44

polymer_elements

JavaScript
24
star
45

shelf_static

archived repo
Dart
24
star
46

shelf_proxy

A shelf handler for proxying requests to another server.
Dart
23
star
47

dartdoc-viewer

deprecated. Use dartdoc instead.
Dart
23
star
48

sample-todomvc-polymer

todomvc example built with polymer.dart
Dart
22
star
49

shelf_web_socket

A WebSocket handler for Shelf.
Dart
22
star
50

kernel

Dart IR (Intermediate Representation) -- moved to dart-lang/sdk
Dart
21
star
51

web-components

Dart package providing the web components platform polyfills
JavaScript
18
star
52

polymer-core-and-paper-examples

This repo contains examples for the core and paper polymer elements, from Dart
HTML
16
star
53

resource

Resource loading library.
Dart
16
star
54

http_io

Dart
15
star
55

http_retry

HTTP client middleware that automatically retries requests
Dart
15
star
56

angular_ast

DEPRECATED - development moved to share angular repo
Dart
15
star
57

dart2js_info

Model of the data produced by dart2js with --dump-info, and tools that process the information.
Dart
14
star
58

polymer-and-dart-codelab

Polymer Dart codelab sample code
HTML
14
star
59

custom-element-apigen

Tool to generate Dart APIs for polymer custom elements written in Javascript
Dart
13
star
60

old_ghpages_server_docs

Documentation for Dart on the server
HTML
13
star
61

observe

Support for marking objects as observable, and getting notifications when those objects are mutated
Dart
13
star
62

angular_test

**MOVED**: Repository has moved to https://github.com/dart-lang/angular
13
star
63

vm_service_client

A Darty client for the VM service protocol
Dart
12
star
64

smoke

Smoke is a Dart package that exposes a reduced reflective system API. This API includes accessing objects in a dynamic fashion (read properties, write properties, and call methods), inspecting types (for example, whether a method exists), and symbol/string convention.
Dart
12
star
65

rules_dart

Dart rules for Bazel
Python
11
star
66

polymer-spa-example

A sample "Single Page App" for Polymer.dart <=0.17.0
Dart
11
star
67

vm_service_drivers

[DEPRECATED] Libraries to access the Dart VM Service Protocol
Dart
11
star
68

barback

An asset build system for Dart.
Dart
11
star
69

angulardart.org

Website for AngularDart, a web framework for Dart
HTML
10
star
70

utf

Provides common operations for manipulating Unicode sequences
Dart
10
star
71

rpc-examples

Dart
9
star
72

expected_output

Dart
9
star
73

memcache

Memcache interface for the Dart appengine package
Dart
9
star
74

atom-dartino

Atom Plug-in for Dartino
JavaScript
8
star
75

angular2_api_examples

DEPRECATED - content moved
8
star
76

shelf_rpc

shelf_rpc is a Shelf Handler for the Dart rpc package.
Dart
7
star
77

webcore

Web DOM IDL files and support files copied from Blink
Python
7
star
78

eclipse3

DEPRECATED - NO LONGER maintained/supported - Eclipse plugins and Dart Editor
Java
7
star
79

polymer_interop

Repackaging of the original polymer js source.
HTML
7
star
80

package_resolver

First-class package resolution strategy classes.
Dart
7
star
81

dart2_fix

A tool to migrate API usage to Dart 2
Dart
7
star
82

plugin

Dart
7
star
83

observatory

The website for Dart VM's Observatory
CSS
7
star
84

initialize

Provides a common interface for initialization annotations on top level methods, classes, and libraries in Dart
Dart
7
star
85

dart2es6

Dart to ECMAScript 6 transpiler
JavaScript
6
star
86

dart-doc-syncer

A utility for syncing Dart examples for the public docs
Dart
6
star
87

http_throttle

HTTP client middleware that throttles requests.
Dart
6
star
88

io_2017_components_codelab

Dart
6
star
89

code_transformers

Package to help with code transformers in barback
Dart
5
star
90

discoveryapis_commons

A package used by client libraries generated from discovery documents.
Dart
5
star
91

shelf_test_handler

A Shelf handler that makes it easy to test HTTP interactions
Dart
5
star
92

shelf_appengine

A set helpers to make it easy to use Shelf on App Engine.
Dart
5
star
93

csslib-test-suite

Suite of CSS tests (originating from the W3C) used to validate CSS parsing.
HTML
4
star
94

site-events

Source of events.dartlang.org
CSS
3
star
95

test_reflectable

tests for the reflectable package
Dart
3
star
96

polymer-expressions

Polymer.dart <=0.17.0 support for polymer expressions in Dart
Dart
3
star
97

shelf_packages_handler

A shelf handler for serving a `packages/` directory
Dart
3
star
98

gulp-ts2dart

Gulp task to transpile TypeScript code to Dart
JavaScript
3
star
99

multi_server_socket

An implementation of dart:io's ServerSocket that wraps multiple servers and forwards methods to all of them
Dart
3
star
100

func

Repository for the func package containing convenience typedefs for specifying function types.
Dart
3
star