REST is not only (web) API interface

REST is not only (web) API interface

RESTだけじゃなくて他にもインターフェイスは色々あるという話です。
「そうだGo、京都。」で発表。
参考リンクは https://gist.github.com/lufia/79a17579be449cf4e9aad790b58db284 に置きました。

Fdbc02073fe49c59f4f1ae705691a128?s=128

kadota kyohei

April 29, 2017
Tweet

Transcript

  1. 2.

    ࣗݾ঺հ • ໳ଟګฏ • Favorites • OS: Plan 9 •

    Editor: acme • Shell: rc • ϑΣϯϦϧͰಇ͍͍ͯ·͢
  2. 3.
  3. 7.

    (ࢀߟ)APNsͷϖΠϩʔυ { "aps": { "alert": { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢",

    "action-loc-key": "PLAY" }, "badge": 5 }, "attr1": "bar", "attr2": [ "bang", "whiz" ] }
  4. 9.
  5. 10.

    ૹ৴API (v1) POST /devices/:device_token Content-Type: application/json X-Priority: high X-TTL: 300

    { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢", "icon": "myicon", "data": { "attr1": "bar", "attr2": "bang" } } σόΠεຖʹURIׂΓ౰ͯ ݻ༗ͷ஋͸ϔομͰ༩͑Δ
  6. 11.

    ૹ৴API (v2) POST /messages Content-Type: application/json [ { "tokens": ["token1",

    "token2"], "title": "αϯϓϧ", "body": “͜Ε͸ςετ௨஌Ͱ͢", "priority": "high" }, { "tokens": ["token3", "token4", "token5"], "title": "αϯϓϧ2", "body": "͜Ε͸2ͭ໨ͷςετ௨஌Ͱ͢" } ] URI͸·ͱΊΔ
  7. 13.
  8. 15.

    GoͰgRPC // ίϚϯυͷΠϯετʔϧ $ brew install protobuf $ go get

    -u github.com/golang/protobuf/protoc-gen-go // ΤσΟλͰprotoϑΝΠϧΛॻ͘ $ acme service.proto // ίʔυੜ੒ $ protoc --go_out=plugins=grpc:. service.proto gRPCίʔυੜ੒ ग़ྗσΟϨΫτϦ
  9. 16.

    GoͰgRPC // service.proto service PushService { rpc Send (Message) returns

    (stream Result); } message Message { repeated string tokens = 1; Priority priority = 2; string payload = 3; int32 ttl = 4; } enum Priority { HIGH = 0; NORMAL = 1; }
  10. 17.

    GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server

    struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil } ΠϯλʔϑΣΠεΛຬͨͤ͞Δ
  11. 18.

    GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server

    struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil }
  12. 19.

    GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) func main()

    { l, err := net.Listen("tcp", ":8080") if err != nil { log.Fatalln(err) } s := grpc.NewServer() pb.RegisterPushServiceServer(s, &Server{}) s.Serve(l) }
  13. 20.

    GoͰgRPC service PushService { rpc Send (Message) returns (stream Result);

    } ୯ൃϨεϙϯε ෳ਺Ϩεϙϯε ୯ൃϦΫΤετ 6OBSZ31$ 4FSWFSTUSFBNJOH31$ ෳ਺ϦΫΤετ $MJFOUTUSFBNJOH31$ #JEJSFDUJPOBMTUSFBNJOH31$ छྨʹΑͬͯϝιουͷγάωνϟ͕มΘΔ
  14. 21.

    ࢀߟࢿྉ • Protocol Buffer Language Guide • protoc-gen-go parameters •

    gRPC Basics - Creating the server • GRPCͷ࣮ફͱݱঢ়Ͱͷར఺ɾܽ఺
  15. 23.
  16. 26.
  17. 29.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ HTTP/1.1 200 OK { "owner": "lufia", "organization": null, "issues": [ { "title": "add Plan 9 support", "body": "I should support Plan 9.", "html_url": "https://github.com/lufia/taskfs/issues/1", "comments": [ { "body": "9P implementations", "created_at": "2016-12-11T23:14:00+09:00", "updated_at": "2016-12-11T23:14:00+09:00" } ], "created_at": "2016-12-11T23:13:00+09:00", "updated_at": "2016-12-11T23:13:00+09:00" }, { ... } ] }
  18. 30.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ࠷ॳ͸͔͜͜Β
  19. 31.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repositoryΩʔΛ୳͢
  20. 32.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repository͸ΦϒδΣΫτ
  21. 33.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ownerΩʔΛ୳͢
  22. 34.

    GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues

    { title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ organizationΩʔΛ୳͢
  23. 36.

    GoͰGraphQL(ϑΟʔϧυ) import ( "github.com/graphql-go/graphql" ) var RepositoryType = graphql.NewObject(graphql.ObjectConfig{ Name:

    "Repository", Fields: graphql.Fields{ "owner": &graphql.Field{ ... }, "organization": &graphql.Field{ ... }, "issues": graphql.Field{ ... }, }, })
  24. 37.

    GoͰGraphQL(஋ͷղܾ) type Issue struct{ ... } var IssueType = graphql.NewObject(graphql.ObjectConfig{

    Name: "Issue", Fields: graphql.Fields{ "title": &graphql.Field{ Type: graphql.NonNull(graphql.String), Description: "λΠτϧ", Resolve: func(p graphql.ResolveParams) (interface{}, error) { if v, ok := p.Source.(*Issue); !ok { return "", nil } return v.Title, nil }, }, "body": &graphql.Field{ ... }, "html_url": &graphql.Field{ ... }, }, })
  25. 38.

    GoͰGraphQL func main() { schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: QueryType,

    }) http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request){ q, err := ioutil.ReadAll(r.Body) res := graphql.Do(graphql.Params{ Schema: schema, RequestString: string(q), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(res) }) http.ListenAndServe(":8080", nil) } طଘAPIʹ଍ͤΔͷͰ͓खܰ
  26. 39.

    ࢀߟࢿྉ • GraphQL (ݴޠ࢓༷) • Introduction to GraphQL • GraphQLೖ໳

    - ࢖͍ͨ͘ͳΔGraphQL • GitHub GraphQL API (Early Access)
  27. 40.
  28. 43.

    GoͰFUSE import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" ) type Root struct {

    nodefs.Node } func (root *Root) GetAttr(out *fuse.Attr, file nodefs.File, ctx *fuse.Context) fuse.Status { out.Mode = fuse.S_IFDIR | 0755 out.Atime = uint64(time.Now().Unix()) out.Mtime = uint64(time.Now().Unix()) return fuse.OK } func main() { root := &Root{Node: nodefs.NewDefaultNode()} s, _, err := nodefs.MountRoot("/mnt/taskfs", root, &opts) s.Serve() } root͸σΟϨΫτϦͱΧʔωϧʹ఻͑Δ ଞͷϝιουΛ࣮૷͍ͯ͠ͳ͍ͷͰɺۭͷσΟϨΫτϦΛϚ΢ϯτ͢Δ͚ͩ
  29. 44.

    GoͰFUSE func (root *Root) Lookup(out *fuse.Attr, name string, ctx *fuse.Context)

    (*nodefs.Inode, fuse.Status) { _, status := root.readDir() if status != fuse.OK { return nil, status } c := root.lookupName(name) return c.GetAttr(out, nil, ctx) } func (root *Root) OpenDir(ctx *fuse.Context) ([]fuse.DirEntry, fuse.Status){ kids, err := root.readDir() a := make([]fuse.DirEntry, len(kids)) for i, kid := range kids { kid.Stat().FillDirEntry(&a[i]) } return a, fuse.OK } σΟϨΫτϦΤϯτϦΛදݱ͢Δ৔߹͸͜ͷลΓͷϝιουΛ࣮૷͢Δ
  30. 45.

    GoͰFUSE func (ctl *Ctl) Open(flags uint32, ctx *fuse.Context) (nodefs.File, fuse.Status)

    { p, err := ctl.ReadFile() if err != nil { return nil, fuse.EIO } return nodefs.NewDataFile(p), fuse.OK } func (ctl *Ctl) Truncate(file nodefs.File, size uint64, ctx *fuse.Context) fuse.Status { return fuse.OK } func (ctl *Ctl) Write(file nodefs.File, data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) { err := ctl.WriteFile(data) if err != nil { return 0, fuse.EINVAL } return uint32(len(data)), fuse.OK } ϑΝΠϧૢ࡞Λ͢Δ৔߹ͷϝιουͳͲ