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. REST is not only (web) API interface ͦ͏ͩGoɺژ౎ɻ @plan9user (2017-04-29)

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

    Editor: acme • Shell: rc • ϑΣϯϦϧͰಇ͍͍ͯ·͢
  3. None
  4. ࠓ೔࿩͢͜ͱ • REST API͸ྑ͍બ୒͚ͩͲ΋ • ͦ͏͡Όͳ͍ํ๏΋͋ΔΑ • gRPC • GraphQL

    • FUSE
  5. RESTͰϓογϡ௨஌API͸ɺͲ ͏ઃܭ͢Ε͹͍͍ΜͩΖ͏ • ΤϯυϙΠϯτ͸send? request? etc… • ϝοηʔδ͸·ͱΊΔʁ෼͚Δʁ

  6. લఏ • iOS΋Android΋ɺ୺຤+ΞϓϦ͝ͱʹϢχʔ ΫͳIDΛ࣋ͭ(τʔΫϯ) • τʔΫϯͱϝοηʔδͷηοτͰ௨஌͢Δ • ࡉ͔͍෦෼͸ϓϥοτϑΥʔϜ͝ͱʹҧ͏

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

    "action-loc-key": "PLAY" }, "badge": 5 }, "attr1": "bar", "attr2": [ "bang", "whiz" ] }
  8. (ࢀߟ)FCMͷϖΠϩʔυ { "to": "(τʔΫϯจࣈྻ)", "notification": { "title": "αϯϓϧ", "body": "͜Ε͸ςετ௨஌Ͱ͢",

    "icon": "myicon" }, "data": { "attr1": "bar", "attr2": "bang" } }
  9. REST

  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ׂΓ౰ͯ ݻ༗ͷ஋͸ϔομͰ༩͑Δ
  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͸·ͱΊΔ
  12. REST͡Όͳͯ͘΋...

  13. gRPC

  14. gRPC • HTTP/2 • Protocol Buffer version 3 • GoogleʹΑͬͯ։ൃ͞Εͨ(ݱࡏ͸OSS)

    • Go, Java, C++, Ruby, C#, Node.js, PHP ...
  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ίʔυੜ੒ ग़ྗσΟϨΫτϦ
  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; }
  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 } ΠϯλʔϑΣΠεΛຬͨͤ͞Δ
  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 }
  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) }
  20. GoͰgRPC service PushService { rpc Send (Message) returns (stream Result);

    } ୯ൃϨεϙϯε ෳ਺Ϩεϙϯε ୯ൃϦΫΤετ 6OBSZ31$ 4FSWFSTUSFBNJOH31$ ෳ਺ϦΫΤετ $MJFOUTUSFBNJOH31$ #JEJSFDUJPOBMTUSFBNJOH31$ छྨʹΑͬͯϝιουͷγάωνϟ͕มΘΔ
  21. ࢀߟࢿྉ • Protocol Buffer Language Guide • protoc-gen-go parameters •

    gRPC Basics - Creating the server • GRPCͷ࣮ફͱݱঢ়Ͱͷར఺ɾܽ఺
  22. net/rpcύοέʔδ • جຊతʹGo͔ΒͷΈར༻ • Go 1.8͔Βౚ݁͞Εͨ...

  23. GraphQL

  24. GraphQL • Query language • JavaScript • Go൛͸ github.com/graphql-go/graphql

  25. ࡢ೥ͷΞυϕϯτΧϨϯμʔ

  26. GitHub

  27. GitHub API Issues Issue Comments

  28. ϑΝΠϧγεςϜͳͷͰ... • 1ϦϙδτϦͰʮIssue݅਺+1ʯճϦΫΤετ • IssueͷϦετ • ͦΕͧΕͷIssueίϝϯτ • find͢ΔͱͭΒ͍

  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" }, { ... } ] }
  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Ͱ͢ ࠷ॳ͸͔͜͜Β
  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ΩʔΛ୳͢
  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͸ΦϒδΣΫτ
  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ΩʔΛ୳͢
  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ΩʔΛ୳͢
  35. GoͰGraphQL • ຊ౰͸Schema͔ΒΦϒδΣΫτΛੜ੒͢Δ • GoͰ͸·ͩSchema͔Βੜ੒͸Ͱ͖ͳ͍ • ࠓճ͸ख࡞ۀ...

  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{ ... }, }, })
  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{ ... }, }, })
  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ʹ଍ͤΔͷͰ͓खܰ
  39. ࢀߟࢿྉ • GraphQL (ݴޠ࢓༷) • Introduction to GraphQL • GraphQLೖ໳

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

  41. FUSE • Filesystem in Userspace • Plan 9 • FUSE,

    FUSE for macOS, Dokan
  42. FUSEͷಈ࡞ • ϢʔβϓϩηεͰϑΝΠϧγεςϜ • γεςϜίʔϧʹద੾ͳԠ౴Λฦ͢ • ͍͍ͩͨશ෦Ͱ30ݸఔ౓ • ඞཁͳ෼͚࣮ͩ૷͢Δ •

    ࣮ࡍͷ௨৴͸ͳΜͰ΋͍͍(HTTP, MQTT, 9P)
  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͸σΟϨΫτϦͱΧʔωϧʹ఻͑Δ ଞͷϝιουΛ࣮૷͍ͯ͠ͳ͍ͷͰɺۭͷσΟϨΫτϦΛϚ΢ϯτ͢Δ͚ͩ
  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 } σΟϨΫτϦΤϯτϦΛදݱ͢Δ৔߹͸͜ͷลΓͷϝιουΛ࣮૷͢Δ
  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 } ϑΝΠϧૢ࡞Λ͢Δ৔߹ͷϝιουͳͲ
  46. ࢀߟࢿྉ • ϑΝΠϧڞ༗ϓϩηεͷ؆ૉԽ໨ࢦ͢Φʔϓ ϯιʔεϓϩδΣΫτʮUpspinʯൃද • GoͰFUSEΛ࢖ͬͯGitHubͷIssuesΛϚ΢ϯ τ͢Δ • 9P -

    Wikipedia