├── .github └── workflows │ ├── add-to-project.yaml │ ├── emergency-review-bypass.yaml │ ├── notify-approval-bypass.yaml │ └── pr-title.yaml ├── .gitignore ├── LICENSE ├── README.md └── tutorial ├── README.md ├── starwars-data-go ├── go.mod ├── go.sum └── pkg │ ├── film │ └── film.go │ ├── person │ └── person.go │ ├── planet │ └── planet.go │ ├── quote │ └── quote.go │ ├── species │ └── species.go │ ├── starship │ └── starship.go │ └── vehicle │ └── vehicle.go ├── starwars-data-ts ├── film │ └── film.ts ├── package-lock.json ├── package.json ├── starship │ └── starship.ts └── tsconfig.json ├── starwars-film-service-go ├── README.md ├── buf.gen.yaml ├── cmd │ └── filmservice │ │ └── filmservice.go ├── gen │ └── buf │ │ └── starwars │ │ └── film │ │ └── v1 │ │ ├── film.pb.go │ │ └── filmv1connect │ │ └── film.connect.go ├── go.mod ├── go.sum └── proto │ ├── buf.lock │ ├── buf.yaml │ └── buf │ └── starwars │ └── film │ └── v1 │ └── film.proto ├── starwars-film-service-ts ├── README.md ├── buf.gen.yaml ├── filmservice.ts ├── gen │ └── buf │ │ └── starwars │ │ └── film │ │ └── v1 │ │ ├── film_connect.ts │ │ └── film_pb.ts ├── package-lock.json ├── package.json ├── proto │ ├── buf.lock │ ├── buf.yaml │ └── buf │ │ └── starwars │ │ └── film │ │ └── v1 │ │ └── film.proto └── tsconfig.json ├── starwars-knit-client-app-ts ├── .gitignore ├── README.md ├── buf.gen.yaml ├── gen │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ ├── film_connect.ts │ │ │ ├── film_knit.ts │ │ │ └── film_pb.ts │ │ ├── relation │ │ └── v1 │ │ │ ├── relation_connect.ts │ │ │ ├── relation_knit.ts │ │ │ └── relation_pb.ts │ │ └── starship │ │ └── v1 │ │ ├── starship_connect.ts │ │ ├── starship_knit.ts │ │ └── starship_pb.ts ├── index.html ├── package-lock.json ├── package.json ├── public │ └── favicon.ico ├── src │ ├── App.css │ ├── App.tsx │ ├── index.css │ ├── main.tsx │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── starwars-knit-gateway-go ├── README.md ├── buf.gen.yaml ├── cmd │ └── gateway │ │ └── gateway.go ├── gen │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ ├── film.pb.go │ │ │ └── filmv1connect │ │ │ └── film.connect.go │ │ ├── relation │ │ └── v1 │ │ │ ├── relation.pb.go │ │ │ └── relationv1connect │ │ │ └── relation.connect.go │ │ └── starship │ │ └── v1 │ │ ├── starship.pb.go │ │ └── starshipv1connect │ │ └── starship.connect.go ├── go.mod └── go.sum ├── starwars-knit-gateway-standalone ├── README.md ├── knitgateway.yaml ├── proto │ ├── buf.lock │ ├── buf.yaml │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ └── film.proto │ │ ├── relation │ │ └── v1 │ │ │ └── relation.proto │ │ └── starship │ │ └── v1 │ │ └── starship.proto └── schema.protoset ├── starwars-knit-relation-service-go ├── README.md ├── buf.gen.yaml ├── cmd │ └── relationservice │ │ └── relationservice.go ├── gen │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ ├── film.pb.go │ │ │ └── filmv1connect │ │ │ └── film.connect.go │ │ ├── relation │ │ └── v1 │ │ │ ├── relation.pb.go │ │ │ └── relationv1connect │ │ │ └── relation.connect.go │ │ └── starship │ │ └── v1 │ │ ├── starship.pb.go │ │ └── starshipv1connect │ │ └── starship.connect.go ├── go.mod ├── go.sum └── proto │ ├── buf.lock │ ├── buf.yaml │ └── buf │ └── starwars │ ├── film │ └── v1 │ │ └── film.proto │ ├── relation │ └── v1 │ │ └── relation.proto │ └── starship │ └── v1 │ └── starship.proto ├── starwars-knit-relation-service-ts ├── README.md ├── buf.gen.yaml ├── gen │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ ├── film_connect.ts │ │ │ └── film_pb.ts │ │ ├── relation │ │ └── v1 │ │ │ ├── relation_connect.ts │ │ │ └── relation_pb.ts │ │ └── starship │ │ └── v1 │ │ ├── starship_connect.ts │ │ └── starship_pb.ts ├── package-lock.json ├── package.json ├── proto │ ├── buf.lock │ ├── buf.yaml │ └── buf │ │ └── starwars │ │ ├── film │ │ └── v1 │ │ │ └── film.proto │ │ ├── relation │ │ └── v1 │ │ │ └── relation.proto │ │ └── starship │ │ └── v1 │ │ └── starship.proto ├── relationservice.ts └── tsconfig.json ├── starwars-starship-service-go ├── README.md ├── buf.gen.yaml ├── cmd │ └── starshipservice │ │ └── starshipservice.go ├── gen │ └── buf │ │ └── starwars │ │ └── starship │ │ └── v1 │ │ ├── starship.pb.go │ │ └── starshipv1connect │ │ └── starship.connect.go ├── go.mod ├── go.sum └── proto │ ├── buf.yaml │ └── buf │ └── starwars │ └── starship │ └── v1 │ └── starship.proto └── starwars-starship-service-ts ├── README.md ├── buf.gen.yaml ├── gen └── buf │ └── starwars │ └── starship │ └── v1 │ ├── starship_connect.ts │ └── starship_pb.ts ├── package-lock.json ├── package.json ├── proto ├── buf.lock ├── buf.yaml └── buf │ └── starwars │ └── starship │ └── v1 │ └── starship.proto ├── starshipservice.ts └── tsconfig.json /.github/workflows/add-to-project.yaml: -------------------------------------------------------------------------------- 1 | name: Add issues and PRs to project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - reopened 8 | - transferred 9 | pull_request_target: 10 | types: 11 | - opened 12 | - reopened 13 | issue_comment: 14 | types: 15 | - created 16 | 17 | jobs: 18 | call-workflow-add-to-project: 19 | name: Call workflow to add issue to project 20 | uses: bufbuild/base-workflows/.github/workflows/add-to-project.yaml@main 21 | secrets: inherit 22 | -------------------------------------------------------------------------------- /.github/workflows/emergency-review-bypass.yaml: -------------------------------------------------------------------------------- 1 | name: Bypass review in case of emergency 2 | on: 3 | pull_request: 4 | types: 5 | - labeled 6 | permissions: 7 | pull-requests: write 8 | jobs: 9 | approve: 10 | if: github.event.label.name == 'Emergency Bypass Review' 11 | uses: bufbuild/base-workflows/.github/workflows/emergency-review-bypass.yaml@main 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.github/workflows/notify-approval-bypass.yaml: -------------------------------------------------------------------------------- 1 | name: PR Approval Bypass Notifier 2 | on: 3 | pull_request: 4 | types: 5 | - closed 6 | branches: 7 | - main 8 | permissions: 9 | pull-requests: read 10 | jobs: 11 | approval: 12 | uses: bufbuild/base-workflows/.github/workflows/notify-approval-bypass.yaml@main 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /.github/workflows/pr-title.yaml: -------------------------------------------------------------------------------- 1 | name: Lint PR Title 2 | # Prevent writing to the repository using the CI token. 3 | # Ref: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions 4 | permissions: 5 | pull-requests: read 6 | on: 7 | pull_request: 8 | # By default, a workflow only runs when a pull_request's activity type is opened, 9 | # synchronize, or reopened. We explicity override here so that PR titles are 10 | # re-linted when the PR text content is edited. 11 | types: 12 | - opened 13 | - edited 14 | - reopened 15 | - synchronize 16 | jobs: 17 | lint: 18 | uses: bufbuild/base-workflows/.github/workflows/pr-title.yaml@main 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | knit.code-workspace 2 | **/node_modules -------------------------------------------------------------------------------- /tutorial/README.md: -------------------------------------------------------------------------------- 1 | # 🧶 Knit Turorial 2 | 3 | [Back to top level README] 4 | 5 | The Knit tutorial uses the Star Wars API as a running example 6 | throughout. It contains the following parts: 7 | 8 | * [Star Wars Knit client app in TypeScript](/tutorial/starwars-knit-client-app-ts) 🧑‍💻 🌐 9 | * [Star Wars Knit gateway](/tutorial/starwars-knit-gateway-standalone) or [(embeddable gateway in Go)](/tutorial/starwars-knit-gateway-go) 10 | * [Star Wars Knit relation service in TypeScript](/tutorial/starwars-knit-relation-service-ts) or [Go](/tutorial/starwars-knit-relation-service-go) 11 | * [Star Wars film service in TypeScript](/tutorial/starwars-film-service-ts) or [Go](/tutorial/starwars-film-service-go) 12 | * [Star Wars starship service in TypeScript](/tutorial/starwars-starship-service-ts) or [Go](/tutorial/starwars-starship-service-go) 13 | 14 | All parts of the tutorial include full code which can be run from the 15 | terminal. Give the system a try by cloning this repo with 16 | `git clone https://github.com/bufbuild/knit.git` and launch the 17 | separate parts by following the instructions in their respective READMEs 18 | linked above. 19 | 20 | ## Parts of the tutorial 21 | The tutorial chooses to use individual processes for everything, so each 22 | component is its own sub-directory and has its own README that shows how 23 | to run that part of the system: 24 | 25 | ```mermaid 26 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 27 | flowchart LR 28 | A[Knit Client] --> B[Knit Gateway] 29 | subgraph r [Knit Relation Service] 30 | R{{Relation RPCs}} 31 | end 32 | subgraph f [Film Service] 33 | F{{Film RPCs}} 34 | end 35 | subgraph s [Starship Service] 36 | S{{Starship RPCs}} 37 | end 38 | B --> R 39 | B --> F 40 | B --> S 41 | ``` 42 | 43 | The Knit client app issues queries via the the Knit gateway. Executing 44 | Knit queries in the gateway may require calling the RPCs of the other 45 | services, and the Knit gateway plans out the correct order, and flow 46 | of required data from responses into subsequent requests until a query 47 | is fully executed. 48 | 49 | The Film service and Starship service are just normal gRPC services. 50 | In this tutorial they are built using [connect-es] and [connect-go], 51 | there is nothing Knit specific about them, however they are needed 52 | for the tutorial. 53 | 54 | The Knit `RelationService` extends the `Film` of `FilmService` with a 55 | new field called `starships`, without needing to modify `Film` or 56 | `FilmService`, and shows how such relations can be added to a system 57 | using Knit. 58 | 59 | ## Installing tools before you start 60 | Writing the services and client app from scratch will require the 61 | `buf` CLI, Node.js and Go, see the install instructions for the 62 | [buf CLI], [Node.js] and [Go]. 63 | 64 | [Knit README]: https://github.com/bufbuild/knit/ 65 | [Back to top level README]: https://github.com/bufbuild/knit/ 66 | [knit-ts]: https://github.com/bufbuild/knit-ts/ 67 | [knit-go]: https://github.com/bufbuild/knit-go/ 68 | [connect-go]: https://github.com/bufbuild/connect-go 69 | [connect-es]: https://github.com/bufbuild/connect-es 70 | [buf cli]: https://buf.build/docs/installation/ 71 | [node.js]: https://nodejs.org/en 72 | [go]: https://go.dev/ -------------------------------------------------------------------------------- /tutorial/starwars-data-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bufbuild/knit/tutorial/starwars-data 2 | 3 | go 1.23.0 4 | -------------------------------------------------------------------------------- /tutorial/starwars-data-go/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufbuild/knit/b4431e120487589868d918d52d499798b2ae7134/tutorial/starwars-data-go/go.sum -------------------------------------------------------------------------------- /tutorial/starwars-data-go/pkg/film/film.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package film 16 | 17 | // Find films with given id. 18 | func Find(id string) *Film { 19 | for _, v := range films { 20 | if v.FilmID == id { 21 | other := v 22 | return &other 23 | } 24 | } 25 | return nil 26 | } 27 | 28 | type Film struct { 29 | FilmID string 30 | EpisodeNumber uint64 31 | Title string 32 | OpeningText string 33 | Directors []string 34 | Producers []string 35 | // Relationships 36 | CharacterIDs []string 37 | PlanetIDs []string 38 | StarshipIDs []string 39 | VehicleIDs []string 40 | SpeciesIDs []string 41 | } 42 | 43 | var films = []Film{ 44 | { 45 | FilmID: "1", 46 | EpisodeNumber: 4, 47 | Title: "A New Hope", 48 | OpeningText: "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....", 49 | Directors: []string{"George Lucas"}, 50 | Producers: []string{"Gary Kurtz, Rick McCallum"}, 51 | CharacterIDs: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "12", "13", "14", "15", "16", "18", "19", "81"}, 52 | PlanetIDs: []string{"1", "2", "3"}, 53 | StarshipIDs: []string{"2", "3", "5", "9", "10", "11", "12", "13"}, 54 | VehicleIDs: []string{"4", "6", "7", "8"}, 55 | SpeciesIDs: []string{"1", "2", "3", "4", "5"}, 56 | }, 57 | { 58 | FilmID: "2", 59 | EpisodeNumber: 5, 60 | Title: "The Empire Strikes Back", 61 | OpeningText: "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....", 62 | Directors: []string{"Irvin Kershner"}, 63 | Producers: []string{"Gary Kurtz, Rick McCallum"}, 64 | CharacterIDs: []string{"1", "2", "3", "4", "5", "10", "13", "14", "18", "20", "21", "22", "23", "24", "25", "26"}, 65 | PlanetIDs: []string{"4", "5", "6", "27"}, 66 | StarshipIDs: []string{"3", "10", "11", "12", "15", "17", "21", "22", "23"}, 67 | VehicleIDs: []string{"8", "14", "16", "18", "19", "20"}, 68 | SpeciesIDs: []string{"1", "2", "3", "6", "7"}, 69 | }, 70 | { 71 | FilmID: "3", 72 | EpisodeNumber: 6, 73 | Title: "Return of the Jedi", 74 | OpeningText: "Luke Skywalker has returned to\r\nhis home planet of Tatooine in\r\nan attempt to rescue his\r\nfriend Han Solo from the\r\nclutches of the vile gangster\r\nJabba the Hutt.\r\n\r\nLittle does Luke know that the\r\nGALACTIC EMPIRE has secretly\r\nbegun construction on a new\r\narmored space station even\r\nmore powerful than the first\r\ndreaded Death Star.\r\n\r\nWhen completed, this ultimate\r\nweapon will spell certain doom\r\nfor the small band of rebels\r\nstruggling to restore freedom\r\nto the galaxy...", 75 | Directors: []string{"Richard Marquand"}, 76 | Producers: []string{"Howard G. Kazanjian, George Lucas, Rick McCallum"}, 77 | CharacterIDs: []string{"1", "2", "3", "4", "5", "10", "13", "14", "16", "18", "20", "21", "22", "25", "27", "28", "29", "30", "31", "45"}, 78 | PlanetIDs: []string{"1", "5", "7", "8", "9"}, 79 | StarshipIDs: []string{"2", "3", "10", "11", "12", "15", "17", "22", "23", "27", "28", "29"}, 80 | VehicleIDs: []string{"8", "16", "18", "19", "24", "25", "26", "30"}, 81 | SpeciesIDs: []string{"1", "2", "3", "5", "6", "8", "9", "10", "15"}, 82 | }, 83 | { 84 | FilmID: "4", 85 | EpisodeNumber: 1, 86 | Title: "The Phantom Menace", 87 | OpeningText: "Turmoil has engulfed the\r\nGalactic Republic. The taxation\r\nof trade routes to outlying star\r\nsystems is in dispute.\r\n\r\nHoping to resolve the matter\r\nwith a blockade of deadly\r\nbattleships, the greedy Trade\r\nFederation has stopped all\r\nshipping to the small planet\r\nof Naboo.\r\n\r\nWhile the Congress of the\r\nRepublic endlessly debates\r\nthis alarming chain of events,\r\nthe Supreme Chancellor has\r\nsecretly dispatched two Jedi\r\nKnights, the guardians of\r\npeace and justice in the\r\ngalaxy, to settle the conflict....", 88 | Directors: []string{"George Lucas"}, 89 | Producers: []string{"Rick McCallum"}, 90 | CharacterIDs: []string{"2", "3", "10", "11", "16", "20", "21", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"}, 91 | PlanetIDs: []string{"1", "8", "9"}, 92 | StarshipIDs: []string{"31", "32", "39", "40", "41"}, 93 | VehicleIDs: []string{"33", "34", "35", "36", "37", "38", "42"}, 94 | SpeciesIDs: []string{"1", "2", "6", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27"}, 95 | }, 96 | { 97 | FilmID: "5", 98 | EpisodeNumber: 2, 99 | Title: "Attack of the Clones", 100 | OpeningText: "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nThis separatist movement,\r\nunder the leadership of the\r\nmysterious Count Dooku, has\r\nmade it difficult for the limited\r\nnumber of Jedi Knights to maintain \r\npeace and order in the galaxy.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....", 101 | Directors: []string{"George Lucas"}, 102 | Producers: []string{"Rick McCallum"}, 103 | CharacterIDs: []string{"2", "3", "6", "7", "10", "11", "20", "21", "22", "33", "35", "36", "40", "43", "46", "51", "52", "53", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "82"}, 104 | PlanetIDs: []string{"1", "8", "9", "10", "11"}, 105 | StarshipIDs: []string{"21", "32", "39", "43", "47", "48", "49", "52", "58"}, 106 | VehicleIDs: []string{"4", "44", "45", "46", "50", "51", "53", "54", "55", "56", "57"}, 107 | SpeciesIDs: []string{"1", "2", "6", "12", "13", "15", "28", "29", "30", "31", "32", "33", "34", "35"}, 108 | }, 109 | { 110 | FilmID: "6", 111 | EpisodeNumber: 3, 112 | Title: "Revenge of the Sith", 113 | OpeningText: "War! The Republic is crumbling\r\nunder attacks by the ruthless\r\nSith Lord, Count Dooku.\r\nThere are heroes on both sides.\r\nEvil is everywhere.\r\n\r\nIn a stunning move, the\r\nfiendish droid leader, General\r\nGrievous, has swept into the\r\nRepublic capital and kidnapped\r\nChancellor Palpatine, leader of\r\nthe Galactic Senate.\r\n\r\nAs the Separatist Droid Army\r\nattempts to flee the besieged\r\ncapital with their valuable\r\nhostage, two Jedi Knights lead a\r\ndesperate mission to rescue the\r\ncaptive Chancellor....", 114 | Directors: []string{"George Lucas"}, 115 | Producers: []string{"Rick McCallum"}, 116 | CharacterIDs: []string{"1", "2", "3", "4", "5", "6", "7", "10", "11", "12", "13", "20", "21", "33", "35", "46", "51", "52", "53", "54", "55", "56", "58", "63", "64", "67", "68", "75", "78", "79", "80", "81", "82", "83"}, 117 | PlanetIDs: []string{"1", "2", "5", "8", "9", "12", "13", "14", "15", "16", "17", "18", "19"}, 118 | StarshipIDs: []string{"2", "32", "48", "59", "61", "63", "64", "65", "66", "68", "74", "75"}, 119 | VehicleIDs: []string{"33", "50", "53", "56", "60", "62", "67", "69", "70", "71", "72", "73", "76"}, 120 | SpeciesIDs: []string{"1", "2", "3", "6", "15", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "33", "34", "35", "36", "37"}, 121 | }, 122 | } 123 | -------------------------------------------------------------------------------- /tutorial/starwars-data-go/pkg/quote/quote.go: -------------------------------------------------------------------------------- 1 | package quote 2 | 3 | import "math/rand" 4 | 5 | // Find quote with given id. 6 | func Find(id string) *Quote { 7 | for _, v := range quotes { 8 | if v.QuoteID == id { 9 | other := v 10 | return &other 11 | } 12 | } 13 | return nil 14 | } 15 | 16 | // RandomQuote returns a random Star Wars quote. 17 | func RandomQuote() *Quote { 18 | n := len(quotes) 19 | i := rand.Intn(n) 20 | q := quotes[i] 21 | return &q 22 | } 23 | 24 | // Quote from Star Wars film. 25 | type Quote struct { 26 | QuoteID string 27 | Text string 28 | FilmID string 29 | PersonID string 30 | } 31 | 32 | var quotes = []Quote{ 33 | { 34 | // Yoda, Star Wars Episode V: The Empire Strikes Back 35 | QuoteID: "1", 36 | Text: "Try not. Do or do not. There is no try", 37 | FilmID: "2", 38 | PersonID: "20", 39 | }, 40 | { 41 | // Obi-Wan Kenobi, Star Wars Episode IV: A New Hope 42 | QuoteID: "2", 43 | Text: "Your eyes can deceive you; don't trust them", 44 | FilmID: "1", 45 | PersonID: "10", 46 | }, 47 | // “Luminous beings we are, not this crude matter.” —Yoda, The Empire Strikes Back 48 | { 49 | QuoteID: "3", 50 | Text: "Luminous beings we are, not this crude matter", 51 | FilmID: "2", 52 | PersonID: "20", 53 | }, 54 | // “Who’s the more foolish: the fool or the fool who follows him? —Obi-Wan Kenobi, A New Hope 55 | { 56 | QuoteID: "4", 57 | Text: "Who's the more foolish: the fool or the fool who follows him?", 58 | FilmID: "1", 59 | PersonID: "10", 60 | }, 61 | // “Your focus determines your reality.” —Qui-Gon Jinn, Star Wars Episode I: The Phantom Menace 62 | { 63 | QuoteID: "5", 64 | Text: "Your focus determines your reality", 65 | FilmID: "4", 66 | PersonID: "32", 67 | }, 68 | // “In a dark place we find ourselves and a little more knowledge lights our way.” —Yoda, Star Wars Episode III: Revenge Of The Sith 69 | { 70 | QuoteID: "6", 71 | Text: "In a dark place we find ourselves and a little more knowledge lights our way", 72 | FilmID: "6", 73 | PersonID: "20", 74 | }, 75 | // “Sometimes we must let go of our pride and do what is requested of us.” —Anakin Skywalker, Star Wars Episode II: Attack Of The Clones 76 | { 77 | QuoteID: "7", 78 | Text: "Sometimes we must let go of our pride and do what is requested of us", 79 | FilmID: "5", 80 | PersonID: "11", 81 | }, 82 | // “The ability to speak does not make you intelligent.” —Qui-Gon Jinn, The Phantom Menace 83 | { 84 | QuoteID: "9", 85 | Text: "The ability to speak does not make you intelligent", 86 | FilmID: "4", 87 | PersonID: "32", 88 | }, 89 | // “Difficult to see; always in motion is the future.” —Yoda, The Empire Strikes Back 90 | { 91 | QuoteID: "10", 92 | Text: "Difficult to see; always in motion is the future", 93 | FilmID: "2", 94 | PersonID: "20", 95 | }, 96 | // “Many of the truths that we cling to depend on our viewpoint.” — Obi-Wan Kenobi, Star Wars Episode VI: Return Of The Jedi 97 | { 98 | QuoteID: "11", 99 | Text: "Many of the truths that we cling to depend on our viewpoint", 100 | FilmID: "3", 101 | PersonID: "10", 102 | }, 103 | // “Train yourself to let go of everything you fear to lose.” —Yoda, Revenge Of The Sith 104 | { 105 | QuoteID: "12", 106 | Text: "Train yourself to let go of everything you fear to lose", 107 | FilmID: "6", 108 | PersonID: "20", 109 | }, 110 | // “Great, kid, don’t get cocky.” —Han Solo, A New Hope 111 | { 112 | QuoteID: "13", 113 | Text: "Great, kid, don't get cocky", 114 | FilmID: "1", 115 | PersonID: "14", 116 | }, 117 | // “Once you start down the dark path, forever will it dominate your destiny.” —Yoda, The Empire Strikes Back 118 | { 119 | QuoteID: "14", 120 | Text: "Once you start down the dark path, forever will it dominate your destiny", 121 | FilmID: "2", 122 | PersonID: "20", 123 | }, 124 | // “Fear leads to anger, anger leads to hate, hate leads to suffering.” —Yoda, The Phantom Menace 125 | { 126 | QuoteID: "15", 127 | Text: "Fear leads to anger, anger leads to hate, hate leads to suffering", 128 | FilmID: "4", 129 | PersonID: "20", 130 | }, 131 | // “The fear of loss is a path to the dark side.” —Yoda, Revenge Of The Sith 132 | { 133 | QuoteID: "16", 134 | Text: "The fear of loss is a path to the dark side", 135 | FilmID: "6", 136 | PersonID: "20", 137 | }, 138 | // “Let go of your hate.” —Luke Skywalker, Return Of The Jedi 139 | { 140 | QuoteID: "17", 141 | Text: "Let go of your hate", 142 | FilmID: "3", 143 | PersonID: "1", 144 | }, 145 | // “Why, you stuck-up half-witted scruffy-looking nerf herder.” —Princess Leia, The Empire Strikes Back 146 | { 147 | QuoteID: "19", 148 | Text: "Why, you stuck-up half-witted scruffy-looking nerf herder", 149 | FilmID: "2", 150 | PersonID: "5", 151 | }, 152 | // “In my experience, there’s no such thing as luck.” —Obi-Wan Kenobi, A New Hope 153 | { 154 | QuoteID: "20", 155 | Text: "In my experience, there's no such thing as luck", 156 | FilmID: "1", 157 | PersonID: "10", 158 | }, 159 | } 160 | -------------------------------------------------------------------------------- /tutorial/starwars-data-ts/film/film.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // Find films with given id. 17 | export function findFilm(id: string): Film | undefined { 18 | let resp = undefined; 19 | films.forEach(film => { 20 | if (id === film.filmId) { 21 | resp = film 22 | } 23 | }) 24 | return resp; 25 | } 26 | 27 | export type Film = { 28 | filmId: string 29 | episodeNumber: number 30 | title: string 31 | openingText: string 32 | directors: string[] 33 | producers: string[] 34 | // Relationships 35 | characterIds: string[] 36 | planetIds: string[] 37 | starshipIds: string[] 38 | vehicleIds: string[] 39 | speciesIds: string[] 40 | } 41 | 42 | const films: Film[] = [ 43 | { 44 | filmId: "1", 45 | episodeNumber: 4, 46 | title: "A New Hope", 47 | openingText: "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....", 48 | directors: ["George Lucas"], 49 | producers: ["Gary Kurtz, Rick McCallum"], 50 | characterIds: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "12", "13", "14", "15", "16", "18", "19", "81"], 51 | planetIds: ["1", "2", "3"], 52 | starshipIds: ["2", "3", "5", "9", "10", "11", "12", "13"], 53 | vehicleIds: ["4", "6", "7", "8"], 54 | speciesIds: ["1", "2", "3", "4", "5"], 55 | }, 56 | { 57 | filmId: "2", 58 | episodeNumber: 5, 59 | title: "The Empire Strikes Back", 60 | openingText: "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....", 61 | directors: ["Irvin Kershner"], 62 | producers: ["Gary Kurtz, Rick McCallum"], 63 | characterIds: ["1", "2", "3", "4", "5", "10", "13", "14", "18", "20", "21", "22", "23", "24", "25", "26"], 64 | planetIds: ["4", "5", "6", "27"], 65 | starshipIds: ["3", "10", "11", "12", "15", "17", "21", "22", "23"], 66 | vehicleIds: ["8", "14", "16", "18", "19", "20"], 67 | speciesIds: ["1", "2", "3", "6", "7"], 68 | }, 69 | { 70 | filmId: "3", 71 | episodeNumber: 6, 72 | title: "Return of the Jedi", 73 | openingText: "Luke Skywalker has returned to\r\nhis home planet of Tatooine in\r\nan attempt to rescue his\r\nfriend Han Solo from the\r\nclutches of the vile gangster\r\nJabba the Hutt.\r\n\r\nLittle does Luke know that the\r\nGALACTIC EMPIRE has secretly\r\nbegun construction on a new\r\narmored space station even\r\nmore powerful than the first\r\ndreaded Death Star.\r\n\r\nWhen completed, this ultimate\r\nweapon will spell certain doom\r\nfor the small band of rebels\r\nstruggling to restore freedom\r\nto the galaxy...", 74 | directors: ["Richard Marquand"], 75 | producers: ["Howard G. Kazanjian, George Lucas, Rick McCallum"], 76 | characterIds: ["1", "2", "3", "4", "5", "10", "13", "14", "16", "18", "20", "21", "22", "25", "27", "28", "29", "30", "31", "45"], 77 | planetIds: ["1", "5", "7", "8", "9"], 78 | starshipIds: ["2", "3", "10", "11", "12", "15", "17", "22", "23", "27", "28", "29"], 79 | vehicleIds: ["8", "16", "18", "19", "24", "25", "26", "30"], 80 | speciesIds: ["1", "2", "3", "5", "6", "8", "9", "10", "15"], 81 | }, 82 | { 83 | filmId: "4", 84 | episodeNumber: 1, 85 | title: "The Phantom Menace", 86 | openingText: "Turmoil has engulfed the\r\nGalactic Republic. The taxation\r\nof trade routes to outlying star\r\nsystems is in dispute.\r\n\r\nHoping to resolve the matter\r\nwith a blockade of deadly\r\nbattleships, the greedy Trade\r\nFederation has stopped all\r\nshipping to the small planet\r\nof Naboo.\r\n\r\nWhile the Congress of the\r\nRepublic endlessly debates\r\nthis alarming chain of events,\r\nthe Supreme Chancellor has\r\nsecretly dispatched two Jedi\r\nKnights, the guardians of\r\npeace and justice in the\r\ngalaxy, to settle the conflict....", 87 | directors: ["George Lucas"], 88 | producers: ["Rick McCallum"], 89 | characterIds: ["2", "3", "10", "11", "16", "20", "21", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59"], 90 | planetIds: ["1", "8", "9"], 91 | starshipIds: ["31", "32", "39", "40", "41"], 92 | vehicleIds: ["33", "34", "35", "36", "37", "38", "42"], 93 | speciesIds: ["1", "2", "6", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27"], 94 | }, 95 | { 96 | filmId: "5", 97 | episodeNumber: 2, 98 | title: "Attack of the Clones", 99 | openingText: "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nThis separatist movement,\r\nunder the leadership of the\r\nmysterious Count Dooku, has\r\nmade it difficult for the limited\r\nnumber of Jedi Knights to maintain \r\npeace and order in the galaxy.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....", 100 | directors: ["George Lucas"], 101 | producers: ["Rick McCallum"], 102 | characterIds: ["2", "3", "6", "7", "10", "11", "20", "21", "22", "33", "35", "36", "40", "43", "46", "51", "52", "53", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "82"], 103 | planetIds: ["1", "8", "9", "10", "11"], 104 | starshipIds: ["21", "32", "39", "43", "47", "48", "49", "52", "58"], 105 | vehicleIds: ["4", "44", "45", "46", "50", "51", "53", "54", "55", "56", "57"], 106 | speciesIds: ["1", "2", "6", "12", "13", "15", "28", "29", "30", "31", "32", "33", "34", "35"], 107 | }, 108 | { 109 | filmId: "6", 110 | episodeNumber: 3, 111 | title: "Revenge of the Sith", 112 | openingText: "War! The Republic is crumbling\r\nunder attacks by the ruthless\r\nSith Lord, Count Dooku.\r\nThere are heroes on both sides.\r\nEvil is everywhere.\r\n\r\nIn a stunning move, the\r\nfiendish droid leader, General\r\nGrievous, has swept into the\r\nRepublic capital and kidnapped\r\nChancellor Palpatine, leader of\r\nthe Galactic Senate.\r\n\r\nAs the Separatist Droid Army\r\nattempts to flee the besieged\r\ncapital with their valuable\r\nhostage, two Jedi Knights lead a\r\ndesperate mission to rescue the\r\ncaptive Chancellor....", 113 | directors: ["George Lucas"], 114 | producers: ["Rick McCallum"], 115 | characterIds: ["1", "2", "3", "4", "5", "6", "7", "10", "11", "12", "13", "20", "21", "33", "35", "46", "51", "52", "53", "54", "55", "56", "58", "63", "64", "67", "68", "75", "78", "79", "80", "81", "82", "83"], 116 | planetIds: ["1", "2", "5", "8", "9", "12", "13", "14", "15", "16", "17", "18", "19"], 117 | starshipIds: ["2", "32", "48", "59", "61", "63", "64", "65", "66", "68", "74", "75"], 118 | vehicleIds: ["33", "50", "53", "56", "60", "62", "67", "69", "70", "71", "72", "73", "76"], 119 | speciesIds: ["1", "2", "3", "6", "15", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "33", "34", "35", "36", "37"], 120 | }, 121 | ]; 122 | -------------------------------------------------------------------------------- /tutorial/starwars-data-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starwars-data-ts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "tsx": "^3.12.7", 14 | "typescript": "^5.0.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/README.md: -------------------------------------------------------------------------------- 1 | # Star Wars Film Service in Go 2 | 3 | [Back to top of Tutorial] 4 | 5 | The film service is not strictly 🧶 Knit, it's just a simple gRPC service 6 | written using [connect-go], but it's needed for the other parts of the 7 | tutorial. Feel free to just run this service and go on to the Knit 8 | specific parts. The film service is made to listen on address 9 | `http://localhost:18001`. Look at the process diagram below 10 | to see where the film service fits into the bigger 11 | picture. 12 | 13 | ```mermaid 14 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 15 | flowchart LR 16 | A[Knit Client] --> B[Knit Gateway] 17 | subgraph r [Knit Relation Service] 18 | R{{Relation RPCs}} 19 | end 20 | subgraph f [Film Service] 21 | F{{Film RPCs}} 22 | end 23 | subgraph s [Starship Service] 24 | S{{Starship RPCs}} 25 | end 26 | B --> R 27 | B --> F 28 | B --> S 29 | style f stroke:#000,stroke-width:3px 30 | style F stroke:#000,stroke-width:3px 31 | ``` 32 | 33 | ## How to run the code 34 | To run the film service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 35 | then execute the following from the base of the repository (the other services must be running too). 36 | 37 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 38 | ``` 39 | cd tutorial/starwars-film-service-go/cmd/filmservice/ 40 | 41 | go mod tidy 42 | go run filmservice.go 43 | 44 | # Output 45 | 2023/05/01 11:33:03 Film service starting 46 | 2023/05/01 11:33:03 Handling connect service at prefix: /buf.starwars.film.v1.FilmService/ 47 | 2023/05/01 11:33:03 Listening on: 127.0.0.1:18001 48 | ``` 49 | 50 | Open another terminal, and at the base directory do: 51 | ``` 52 | buf curl \ 53 | "http://localhost:18001/buf.starwars.film.v1.FilmService/GetFilms" \ 54 | --data '{"filmIds":["1"]}' \ 55 | --http2-prior-knowledge \ 56 | --schema tutorial/starwars-film-service-go/proto/buf/starwars/film/v1/film.proto 57 | ``` 58 | 59 | [Back to top of Tutorial]: /tutorial 60 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 61 | [connect-go]: https://github.com/bufbuild/connect-go 62 | [badges_slack]: https://buf.build/links/slack -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: github.com/bufbuild/knit/tutorial/starwars-film-service-go/gen 6 | plugins: 7 | - remote: buf.build/protocolbuffers/plugins/go 8 | opt: paths=source_relative 9 | out: gen 10 | - remote: buf.build/bufbuild/plugins/connect-go 11 | opt: paths=source_relative 12 | out: gen -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/cmd/filmservice/filmservice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "net/http" 21 | 22 | "github.com/bufbuild/connect-go" 23 | grpcreflect "github.com/bufbuild/connect-grpcreflect-go" 24 | "github.com/bufbuild/knit/tutorial/starwars-data-go/pkg/film" 25 | filmv1 "github.com/bufbuild/knit/tutorial/starwars-film-service-go/gen/buf/starwars/film/v1" 26 | "github.com/bufbuild/knit/tutorial/starwars-film-service-go/gen/buf/starwars/film/v1/filmv1connect" 27 | "golang.org/x/net/http2" 28 | "golang.org/x/net/http2/h2c" 29 | ) 30 | 31 | func main() { 32 | const addr = "127.0.0.1:18001" 33 | 34 | log.Printf("Film service starting") 35 | 36 | mux := http.NewServeMux() 37 | 38 | path, handler := filmv1connect.NewFilmServiceHandler(&FilmService{}) 39 | mux.Handle(path, handler) 40 | log.Printf("Handling connect service at prefix: %v", path) 41 | 42 | reflector := grpcreflect.NewStaticReflector( 43 | filmv1connect.FilmServiceName, 44 | ) 45 | 46 | mux.Handle(grpcreflect.NewHandlerV1(reflector)) 47 | mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector)) 48 | 49 | log.Printf("Listening on: %v", addr) 50 | err := http.ListenAndServe( 51 | addr, 52 | h2c.NewHandler(mux, &http2.Server{}), 53 | ) 54 | 55 | if err != http.ErrServerClosed { 56 | log.Printf("Error running or stopping: %v", err) 57 | } 58 | } 59 | 60 | type FilmService struct{} 61 | 62 | var _ filmv1connect.FilmServiceHandler = (*FilmService)(nil) 63 | 64 | func (s *FilmService) GetFilms( 65 | ctx context.Context, 66 | req *connect.Request[filmv1.FilmsRequest], 67 | ) ( 68 | *connect.Response[filmv1.FilmsResponse], 69 | error, 70 | ) { 71 | 72 | resp := &filmv1.FilmsResponse{} 73 | for _, id := range req.Msg.FilmIds { 74 | v := film.Find(id) 75 | if v == nil { 76 | continue 77 | } 78 | resp.Films = append(resp.Films, &filmv1.Film{ 79 | FilmId: v.FilmID, 80 | Title: v.Title, 81 | EpisodeNumber: v.EpisodeNumber, 82 | OpeningText: v.OpeningText, 83 | Directors: v.Directors, 84 | Producers: v.Producers, 85 | // Relations 86 | CharacterIds: v.CharacterIDs, 87 | StarshipIds: v.StarshipIDs, 88 | PlanetIds: v.PlanetIDs, 89 | VehicleIds: v.VehicleIDs, 90 | SpeciesIds: v.SpeciesIDs, 91 | }) 92 | } 93 | 94 | return connect.NewResponse(resp), nil 95 | } 96 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/gen/buf/starwars/film/v1/filmv1connect/film.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/film/v1/film.proto 4 | 5 | package filmv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-film-service-go/gen/buf/starwars/film/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // FilmServiceName is the fully-qualified name of the FilmService service. 25 | FilmServiceName = "buf.starwars.film.v1.FilmService" 26 | ) 27 | 28 | // FilmServiceClient is a client for the buf.starwars.film.v1.FilmService service. 29 | type FilmServiceClient interface { 30 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 31 | } 32 | 33 | // NewFilmServiceClient constructs a client for the buf.starwars.film.v1.FilmService service. By 34 | // default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, 35 | // and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the 36 | // connect.WithGRPC() or connect.WithGRPCWeb() options. 37 | // 38 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 39 | // http://api.acme.com or https://acme.com/grpc). 40 | func NewFilmServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) FilmServiceClient { 41 | baseURL = strings.TrimRight(baseURL, "/") 42 | return &filmServiceClient{ 43 | getFilms: connect_go.NewClient[v1.FilmsRequest, v1.FilmsResponse]( 44 | httpClient, 45 | baseURL+"/buf.starwars.film.v1.FilmService/GetFilms", 46 | opts..., 47 | ), 48 | } 49 | } 50 | 51 | // filmServiceClient implements FilmServiceClient. 52 | type filmServiceClient struct { 53 | getFilms *connect_go.Client[v1.FilmsRequest, v1.FilmsResponse] 54 | } 55 | 56 | // GetFilms calls buf.starwars.film.v1.FilmService.GetFilms. 57 | func (c *filmServiceClient) GetFilms(ctx context.Context, req *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 58 | return c.getFilms.CallUnary(ctx, req) 59 | } 60 | 61 | // FilmServiceHandler is an implementation of the buf.starwars.film.v1.FilmService service. 62 | type FilmServiceHandler interface { 63 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 64 | } 65 | 66 | // NewFilmServiceHandler builds an HTTP handler from the service implementation. It returns the path 67 | // on which to mount the handler and the handler itself. 68 | // 69 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 70 | // and JSON codecs. They also support gzip compression. 71 | func NewFilmServiceHandler(svc FilmServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 72 | mux := http.NewServeMux() 73 | mux.Handle("/buf.starwars.film.v1.FilmService/GetFilms", connect_go.NewUnaryHandler( 74 | "/buf.starwars.film.v1.FilmService/GetFilms", 75 | svc.GetFilms, 76 | opts..., 77 | )) 78 | return "/buf.starwars.film.v1.FilmService/", mux 79 | } 80 | 81 | // UnimplementedFilmServiceHandler returns CodeUnimplemented from all methods. 82 | type UnimplementedFilmServiceHandler struct{} 83 | 84 | func (UnimplementedFilmServiceHandler) GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 85 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.film.v1.FilmService.GetFilms is not implemented")) 86 | } 87 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bufbuild/knit/tutorial/starwars-film-service-go 2 | 3 | go 1.23.0 4 | 5 | replace github.com/bufbuild/knit/tutorial/starwars-data-go => ../starwars-data-go 6 | 7 | require ( 8 | github.com/bufbuild/connect-go v1.0.0 9 | github.com/bufbuild/connect-grpcreflect-go v1.0.0 10 | github.com/bufbuild/knit/tutorial/starwars-data-go v0.0.0-20230505135109-8bf7f257e49d 11 | golang.org/x/net v0.39.0 12 | google.golang.org/protobuf v1.33.0 13 | ) 14 | 15 | require golang.org/x/text v0.24.0 // indirect 16 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bufbuild/connect-go v1.0.0 h1:htSflKUT8y1jxhoPhPYTZMrsY3ipUXjjrbcZR5O2cVo= 2 | github.com/bufbuild/connect-go v1.0.0/go.mod h1:9iNvh/NOsfhNBUH5CtvXeVUskQO1xsrEviH7ZArwZ3I= 3 | github.com/bufbuild/connect-grpcreflect-go v1.0.0 h1:zWsLFYqrT1O2sNJFYfTXI5WxbAyiY2dvevvnJHPtV5A= 4 | github.com/bufbuild/connect-grpcreflect-go v1.0.0/go.mod h1:825I20H8bfE9rLnBH/046JSpmm3uwpNYdG4duCARetc= 5 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 6 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 7 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= 8 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 9 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 10 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 11 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 12 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 13 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT -------------------------------------------------------------------------------- /tutorial/starwars-film-service-go/proto/buf/starwars/film/v1/film.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.film.v1; 4 | 5 | message Film { 6 | string film_id = 1; 7 | uint64 episode_number = 2; 8 | string title = 3; 9 | string opening_text = 4; 10 | repeated string directors = 5; 11 | repeated string producers = 6; 12 | repeated string character_ids = 7; 13 | repeated string planet_ids = 8; 14 | repeated string starship_ids = 9; 15 | repeated string vehicle_ids = 10; 16 | repeated string species_ids = 11; 17 | } 18 | 19 | message FilmsRequest { 20 | repeated string film_ids = 1; 21 | uint32 limit = 2; 22 | } 23 | 24 | message FilmsResponse { 25 | repeated Film films = 1; 26 | } 27 | 28 | service FilmService { 29 | rpc GetFilms(FilmsRequest) returns (FilmsResponse); 30 | } -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/README.md: -------------------------------------------------------------------------------- 1 | # Star Wars Film Service in TypeScript 2 | 3 | [Back to top of Tutorial] 4 | 5 | The film service is not strictly 🧶 Knit, it's just a simple gRPC service 6 | written using [connect-es], but it's needed for the other parts of the 7 | tutorial. Feel free to just run this service and go on to the Knit 8 | specific parts. The film service is made to listen on address 9 | `http://localhost:18001`. Look at the process diagram below 10 | to see where the film service fits into the bigger 11 | picture. 12 | 13 | ```mermaid 14 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 15 | flowchart LR 16 | A[Knit Client] --> B[Knit Gateway] 17 | subgraph r [Knit Relation Service] 18 | R{{Relation RPCs}} 19 | end 20 | subgraph f [Film Service] 21 | F{{Film RPCs}} 22 | end 23 | subgraph s [Starship Service] 24 | S{{Starship RPCs}} 25 | end 26 | B --> R 27 | B --> F 28 | B --> S 29 | style f stroke:#000,stroke-width:3px 30 | style F stroke:#000,stroke-width:3px 31 | ``` 32 | 33 | ## How to run the code 34 | To run the film service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 35 | then execute the following from the base of the repository (the other services must be running too). 36 | 37 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 38 | ``` 39 | cd tutorial/starwars-film-service-ts/ 40 | 41 | npm install 42 | npx tsx fileservice.ts 43 | 44 | # Output 45 | Film service starting 46 | Listening on: 127.0.0.1:18001 47 | ``` 48 | 49 | Open another terminal, and at the base directory do: 50 | ``` 51 | buf curl \ 52 | "http://localhost:18001/buf.starwars.film.v1.FilmService/GetFilms" \ 53 | --data '{"filmIds":["1"]}' \ 54 | --http2-prior-knowledge \ 55 | --schema tutorial/starwars-film-service-ts/proto/buf/starwars/film/v1/film.proto 56 | ``` 57 | 58 | [Back to top of Tutorial]: /tutorial 59 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 60 | [connect-es]: https://github.com/bufbuild/connect-es 61 | [badges_slack]: https://buf.build/links/slack -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | plugins: 5 | - plugin: es 6 | opt: target=ts 7 | out: gen 8 | - plugin: connect-es 9 | opt: target=ts 10 | out: gen -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/filmservice.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import { ConnectRouter } from "@connectrpc/connect"; 16 | import { FilmService } from "./gen/buf/starwars/film/v1/film_connect"; 17 | import { Film, FilmsRequest, FilmsResponse } from "./gen/buf/starwars/film/v1/film_pb"; 18 | import { findFilm } from "../starwars-data-ts/film/film"; 19 | 20 | import { fastify } from "fastify"; 21 | import { fastifyConnectPlugin } from "@connectrpc/connect-fastify"; 22 | 23 | function routes(router: ConnectRouter) { 24 | router.service(FilmService, { 25 | async getFilms(req: FilmsRequest): Promise { 26 | console.log(`Getting films: ${req.filmIds}`) 27 | const films = new Array(); 28 | req.filmIds.forEach(id => { 29 | const film = findFilm(id); 30 | if (film) { 31 | films.push(new Film({ 32 | filmId: film.filmId, 33 | episodeNumber: BigInt(film.episodeNumber), 34 | directors: film.directors, 35 | producers: film.producers, 36 | openingText: film.openingText, 37 | title: film.title, 38 | characterIds: film.characterIds, 39 | planetIds: film.planetIds, 40 | speciesIds: film.speciesIds, 41 | starshipIds: film.starshipIds, 42 | vehicleIds: film.vehicleIds, 43 | })); 44 | } 45 | }) 46 | return new FilmsResponse({ films }) 47 | } 48 | }) 49 | } 50 | 51 | async function main() { 52 | console.log("Film service starting"); 53 | 54 | const server = fastify(); 55 | await server.register(fastifyConnectPlugin, { routes }) 56 | 57 | const host = "127.0.0.1"; 58 | const port = 18001; 59 | 60 | console.log(`Listening on: ${host}:${port}`); 61 | await server.listen({ host, port }); 62 | } 63 | 64 | void main(); 65 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/gen/buf/starwars/film/v1/film_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { FilmsRequest, FilmsResponse } from "./film_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.film.v1.FilmService 11 | */ 12 | export const FilmService = { 13 | typeName: "buf.starwars.film.v1.FilmService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.film.v1.FilmService.GetFilms 17 | */ 18 | getFilms: { 19 | name: "GetFilms", 20 | I: FilmsRequest, 21 | O: FilmsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/gen/buf/starwars/film/v1/film_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.film.v1.Film 11 | */ 12 | export class Film extends Message { 13 | /** 14 | * @generated from field: string film_id = 1; 15 | */ 16 | filmId = ""; 17 | 18 | /** 19 | * @generated from field: uint64 episode_number = 2; 20 | */ 21 | episodeNumber = protoInt64.zero; 22 | 23 | /** 24 | * @generated from field: string title = 3; 25 | */ 26 | title = ""; 27 | 28 | /** 29 | * @generated from field: string opening_text = 4; 30 | */ 31 | openingText = ""; 32 | 33 | /** 34 | * @generated from field: repeated string directors = 5; 35 | */ 36 | directors: string[] = []; 37 | 38 | /** 39 | * @generated from field: repeated string producers = 6; 40 | */ 41 | producers: string[] = []; 42 | 43 | /** 44 | * @generated from field: repeated string character_ids = 7; 45 | */ 46 | characterIds: string[] = []; 47 | 48 | /** 49 | * @generated from field: repeated string planet_ids = 8; 50 | */ 51 | planetIds: string[] = []; 52 | 53 | /** 54 | * @generated from field: repeated string starship_ids = 9; 55 | */ 56 | starshipIds: string[] = []; 57 | 58 | /** 59 | * @generated from field: repeated string vehicle_ids = 10; 60 | */ 61 | vehicleIds: string[] = []; 62 | 63 | /** 64 | * @generated from field: repeated string species_ids = 11; 65 | */ 66 | speciesIds: string[] = []; 67 | 68 | constructor(data?: PartialMessage) { 69 | super(); 70 | proto3.util.initPartial(data, this); 71 | } 72 | 73 | static readonly runtime: typeof proto3 = proto3; 74 | static readonly typeName = "buf.starwars.film.v1.Film"; 75 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 76 | { no: 1, name: "film_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 77 | { no: 2, name: "episode_number", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 78 | { no: 3, name: "title", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 79 | { no: 4, name: "opening_text", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 80 | { no: 5, name: "directors", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 81 | { no: 6, name: "producers", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 82 | { no: 7, name: "character_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 83 | { no: 8, name: "planet_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 84 | { no: 9, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 85 | { no: 10, name: "vehicle_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 86 | { no: 11, name: "species_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 87 | ]); 88 | 89 | static fromBinary(bytes: Uint8Array, options?: Partial): Film { 90 | return new Film().fromBinary(bytes, options); 91 | } 92 | 93 | static fromJson(jsonValue: JsonValue, options?: Partial): Film { 94 | return new Film().fromJson(jsonValue, options); 95 | } 96 | 97 | static fromJsonString(jsonString: string, options?: Partial): Film { 98 | return new Film().fromJsonString(jsonString, options); 99 | } 100 | 101 | static equals(a: Film | PlainMessage | undefined, b: Film | PlainMessage | undefined): boolean { 102 | return proto3.util.equals(Film, a, b); 103 | } 104 | } 105 | 106 | /** 107 | * @generated from message buf.starwars.film.v1.FilmsRequest 108 | */ 109 | export class FilmsRequest extends Message { 110 | /** 111 | * @generated from field: repeated string film_ids = 1; 112 | */ 113 | filmIds: string[] = []; 114 | 115 | /** 116 | * @generated from field: uint32 limit = 2; 117 | */ 118 | limit = 0; 119 | 120 | constructor(data?: PartialMessage) { 121 | super(); 122 | proto3.util.initPartial(data, this); 123 | } 124 | 125 | static readonly runtime: typeof proto3 = proto3; 126 | static readonly typeName = "buf.starwars.film.v1.FilmsRequest"; 127 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 128 | { no: 1, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 129 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 130 | ]); 131 | 132 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsRequest { 133 | return new FilmsRequest().fromBinary(bytes, options); 134 | } 135 | 136 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsRequest { 137 | return new FilmsRequest().fromJson(jsonValue, options); 138 | } 139 | 140 | static fromJsonString(jsonString: string, options?: Partial): FilmsRequest { 141 | return new FilmsRequest().fromJsonString(jsonString, options); 142 | } 143 | 144 | static equals(a: FilmsRequest | PlainMessage | undefined, b: FilmsRequest | PlainMessage | undefined): boolean { 145 | return proto3.util.equals(FilmsRequest, a, b); 146 | } 147 | } 148 | 149 | /** 150 | * @generated from message buf.starwars.film.v1.FilmsResponse 151 | */ 152 | export class FilmsResponse extends Message { 153 | /** 154 | * @generated from field: repeated buf.starwars.film.v1.Film films = 1; 155 | */ 156 | films: Film[] = []; 157 | 158 | constructor(data?: PartialMessage) { 159 | super(); 160 | proto3.util.initPartial(data, this); 161 | } 162 | 163 | static readonly runtime: typeof proto3 = proto3; 164 | static readonly typeName = "buf.starwars.film.v1.FilmsResponse"; 165 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 166 | { no: 1, name: "films", kind: "message", T: Film, repeated: true }, 167 | ]); 168 | 169 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsResponse { 170 | return new FilmsResponse().fromBinary(bytes, options); 171 | } 172 | 173 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsResponse { 174 | return new FilmsResponse().fromJson(jsonValue, options); 175 | } 176 | 177 | static fromJsonString(jsonString: string, options?: Partial): FilmsResponse { 178 | return new FilmsResponse().fromJsonString(jsonString, options); 179 | } 180 | 181 | static equals(a: FilmsResponse | PlainMessage | undefined, b: FilmsResponse | PlainMessage | undefined): boolean { 182 | return proto3.util.equals(FilmsResponse, a, b); 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starwars-film-service-ts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@bufbuild/buf": "^1.26.1", 14 | "@bufbuild/protobuf": "^1.3.0", 15 | "@bufbuild/protoc-gen-es": "^1.3.0", 16 | "@connectrpc/connect": "^0.13.1", 17 | "@connectrpc/connect-fastify": "^0.13.1", 18 | "@connectrpc/protoc-gen-connect-es": "^0.13.1", 19 | "fastify": "^4.29.0", 20 | "tsx": "^3.12.7", 21 | "typescript": "^5.0.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT -------------------------------------------------------------------------------- /tutorial/starwars-film-service-ts/proto/buf/starwars/film/v1/film.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.film.v1; 4 | 5 | message Film { 6 | string film_id = 1; 7 | uint64 episode_number = 2; 8 | string title = 3; 9 | string opening_text = 4; 10 | repeated string directors = 5; 11 | repeated string producers = 6; 12 | repeated string character_ids = 7; 13 | repeated string planet_ids = 8; 14 | repeated string starship_ids = 9; 15 | repeated string vehicle_ids = 10; 16 | repeated string species_ids = 11; 17 | } 18 | 19 | message FilmsRequest { 20 | repeated string film_ids = 1; 21 | uint32 limit = 2; 22 | } 23 | 24 | message FilmsResponse { 25 | repeated Film films = 1; 26 | } 27 | 28 | service FilmService { 29 | rpc GetFilms(FilmsRequest) returns (FilmsResponse); 30 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | plugins: 5 | - plugin: es 6 | out: gen 7 | opt: target=ts 8 | - plugin: connect-es 9 | out: gen 10 | opt: target=ts 11 | - plugin: knit-ts 12 | out: gen 13 | opt: target=ts 14 | strategy: all -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/film/v1/film_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { FilmsRequest, FilmsResponse } from "./film_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.film.v1.FilmService 11 | */ 12 | export const FilmService = { 13 | typeName: "buf.starwars.film.v1.FilmService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.film.v1.FilmService.GetFilms 17 | */ 18 | getFilms: { 19 | name: "GetFilms", 20 | I: FilmsRequest, 21 | O: FilmsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/film/v1/film_knit.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-knit-ts v0.0.1 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | export type FilmService = { 7 | "buf.starwars.film.v1.FilmService": { 8 | fetch: { 9 | }; 10 | do: { 11 | getFilms: { $: FilmsRequest; value: FilmsResponse; }; 12 | }; 13 | listen: { 14 | }; 15 | }; 16 | }; 17 | 18 | export interface Film { 19 | filmId: string; 20 | episodeNumber: bigint; 21 | title: string; 22 | openingText: string; 23 | directors: Array; 24 | producers: Array; 25 | characterIds: Array; 26 | planetIds: Array; 27 | starshipIds: Array; 28 | vehicleIds: Array; 29 | speciesIds: Array; 30 | }; 31 | 32 | export interface FilmsRequest { 33 | filmIds: Array; 34 | limit: number; 35 | }; 36 | 37 | export interface FilmsResponse { 38 | films: Array; 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/film/v1/film_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.film.v1.Film 11 | */ 12 | export class Film extends Message { 13 | /** 14 | * @generated from field: string film_id = 1; 15 | */ 16 | filmId = ""; 17 | 18 | /** 19 | * @generated from field: uint64 episode_number = 2; 20 | */ 21 | episodeNumber = protoInt64.zero; 22 | 23 | /** 24 | * @generated from field: string title = 3; 25 | */ 26 | title = ""; 27 | 28 | /** 29 | * @generated from field: string opening_text = 4; 30 | */ 31 | openingText = ""; 32 | 33 | /** 34 | * @generated from field: repeated string directors = 5; 35 | */ 36 | directors: string[] = []; 37 | 38 | /** 39 | * @generated from field: repeated string producers = 6; 40 | */ 41 | producers: string[] = []; 42 | 43 | /** 44 | * @generated from field: repeated string character_ids = 7; 45 | */ 46 | characterIds: string[] = []; 47 | 48 | /** 49 | * @generated from field: repeated string planet_ids = 8; 50 | */ 51 | planetIds: string[] = []; 52 | 53 | /** 54 | * @generated from field: repeated string starship_ids = 9; 55 | */ 56 | starshipIds: string[] = []; 57 | 58 | /** 59 | * @generated from field: repeated string vehicle_ids = 10; 60 | */ 61 | vehicleIds: string[] = []; 62 | 63 | /** 64 | * @generated from field: repeated string species_ids = 11; 65 | */ 66 | speciesIds: string[] = []; 67 | 68 | constructor(data?: PartialMessage) { 69 | super(); 70 | proto3.util.initPartial(data, this); 71 | } 72 | 73 | static readonly runtime: typeof proto3 = proto3; 74 | static readonly typeName = "buf.starwars.film.v1.Film"; 75 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 76 | { no: 1, name: "film_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 77 | { no: 2, name: "episode_number", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 78 | { no: 3, name: "title", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 79 | { no: 4, name: "opening_text", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 80 | { no: 5, name: "directors", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 81 | { no: 6, name: "producers", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 82 | { no: 7, name: "character_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 83 | { no: 8, name: "planet_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 84 | { no: 9, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 85 | { no: 10, name: "vehicle_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 86 | { no: 11, name: "species_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 87 | ]); 88 | 89 | static fromBinary(bytes: Uint8Array, options?: Partial): Film { 90 | return new Film().fromBinary(bytes, options); 91 | } 92 | 93 | static fromJson(jsonValue: JsonValue, options?: Partial): Film { 94 | return new Film().fromJson(jsonValue, options); 95 | } 96 | 97 | static fromJsonString(jsonString: string, options?: Partial): Film { 98 | return new Film().fromJsonString(jsonString, options); 99 | } 100 | 101 | static equals(a: Film | PlainMessage | undefined, b: Film | PlainMessage | undefined): boolean { 102 | return proto3.util.equals(Film, a, b); 103 | } 104 | } 105 | 106 | /** 107 | * @generated from message buf.starwars.film.v1.FilmsRequest 108 | */ 109 | export class FilmsRequest extends Message { 110 | /** 111 | * @generated from field: repeated string film_ids = 1; 112 | */ 113 | filmIds: string[] = []; 114 | 115 | /** 116 | * @generated from field: uint32 limit = 2; 117 | */ 118 | limit = 0; 119 | 120 | constructor(data?: PartialMessage) { 121 | super(); 122 | proto3.util.initPartial(data, this); 123 | } 124 | 125 | static readonly runtime: typeof proto3 = proto3; 126 | static readonly typeName = "buf.starwars.film.v1.FilmsRequest"; 127 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 128 | { no: 1, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 129 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 130 | ]); 131 | 132 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsRequest { 133 | return new FilmsRequest().fromBinary(bytes, options); 134 | } 135 | 136 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsRequest { 137 | return new FilmsRequest().fromJson(jsonValue, options); 138 | } 139 | 140 | static fromJsonString(jsonString: string, options?: Partial): FilmsRequest { 141 | return new FilmsRequest().fromJsonString(jsonString, options); 142 | } 143 | 144 | static equals(a: FilmsRequest | PlainMessage | undefined, b: FilmsRequest | PlainMessage | undefined): boolean { 145 | return proto3.util.equals(FilmsRequest, a, b); 146 | } 147 | } 148 | 149 | /** 150 | * @generated from message buf.starwars.film.v1.FilmsResponse 151 | */ 152 | export class FilmsResponse extends Message { 153 | /** 154 | * @generated from field: repeated buf.starwars.film.v1.Film films = 1; 155 | */ 156 | films: Film[] = []; 157 | 158 | constructor(data?: PartialMessage) { 159 | super(); 160 | proto3.util.initPartial(data, this); 161 | } 162 | 163 | static readonly runtime: typeof proto3 = proto3; 164 | static readonly typeName = "buf.starwars.film.v1.FilmsResponse"; 165 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 166 | { no: 1, name: "films", kind: "message", T: Film, repeated: true }, 167 | ]); 168 | 169 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsResponse { 170 | return new FilmsResponse().fromBinary(bytes, options); 171 | } 172 | 173 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsResponse { 174 | return new FilmsResponse().fromJson(jsonValue, options); 175 | } 176 | 177 | static fromJsonString(jsonString: string, options?: Partial): FilmsResponse { 178 | return new FilmsResponse().fromJsonString(jsonString, options); 179 | } 180 | 181 | static equals(a: FilmsResponse | PlainMessage | undefined, b: FilmsResponse | PlainMessage | undefined): boolean { 182 | return proto3.util.equals(FilmsResponse, a, b); 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/relation/v1/relation_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/relation/v1/relation.proto (package buf.starwars.relation.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { GetFilmStarshipsRequest, GetFilmStarshipsResponse } from "./relation_pb.js"; 7 | import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.relation.v1.RelationService 11 | */ 12 | export const RelationService = { 13 | typeName: "buf.starwars.relation.v1.RelationService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.relation.v1.RelationService.GetFilmStarships 17 | */ 18 | getFilmStarships: { 19 | name: "GetFilmStarships", 20 | I: GetFilmStarshipsRequest, 21 | O: GetFilmStarshipsResponse, 22 | kind: MethodKind.Unary, 23 | idempotency: MethodIdempotency.NoSideEffects, 24 | }, 25 | } 26 | } as const; 27 | 28 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/relation/v1/relation_knit.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-knit-ts v0.0.1 with parameter "target=ts" 2 | // @generated from file buf/starwars/relation/v1/relation.proto (package buf.starwars.relation.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { Film } from "../../film/v1/film_knit.js"; 7 | import type { Starship } from "../../starship/v1/starship_knit.js"; 8 | 9 | export type RelationService = { 10 | "buf.starwars.relation.v1.RelationService": { 11 | fetch: { 12 | getFilmStarships: { $: GetFilmStarshipsRequest; value: GetFilmStarshipsResponse; }; 13 | }; 14 | do: { 15 | }; 16 | listen: { 17 | }; 18 | }; 19 | }; 20 | 21 | export interface GetFilmStarshipsRequest { 22 | bases: Array; 23 | }; 24 | 25 | export interface GetFilmStarshipsResponse { 26 | values: Array; 27 | }; 28 | 29 | export interface GetFilmStarshipsResponse_Value { 30 | starships: Array; 31 | }; 32 | 33 | declare module "../../film/v1/film_knit.js" { 34 | export interface Film{ 35 | starships?: { $: undefined; value: GetFilmStarshipsResponse_Value["starships"] } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/relation/v1/relation_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/relation/v1/relation.proto (package buf.starwars.relation.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3 } from "@bufbuild/protobuf"; 8 | import { Film } from "../../film/v1/film_pb.js"; 9 | import { Starship } from "../../starship/v1/starship_pb.js"; 10 | 11 | /** 12 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsRequest 13 | */ 14 | export class GetFilmStarshipsRequest extends Message { 15 | /** 16 | * @generated from field: repeated buf.starwars.film.v1.Film bases = 1; 17 | */ 18 | bases: Film[] = []; 19 | 20 | constructor(data?: PartialMessage) { 21 | super(); 22 | proto3.util.initPartial(data, this); 23 | } 24 | 25 | static readonly runtime: typeof proto3 = proto3; 26 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsRequest"; 27 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 28 | { no: 1, name: "bases", kind: "message", T: Film, repeated: true }, 29 | ]); 30 | 31 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsRequest { 32 | return new GetFilmStarshipsRequest().fromBinary(bytes, options); 33 | } 34 | 35 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsRequest { 36 | return new GetFilmStarshipsRequest().fromJson(jsonValue, options); 37 | } 38 | 39 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsRequest { 40 | return new GetFilmStarshipsRequest().fromJsonString(jsonString, options); 41 | } 42 | 43 | static equals(a: GetFilmStarshipsRequest | PlainMessage | undefined, b: GetFilmStarshipsRequest | PlainMessage | undefined): boolean { 44 | return proto3.util.equals(GetFilmStarshipsRequest, a, b); 45 | } 46 | } 47 | 48 | /** 49 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsResponse 50 | */ 51 | export class GetFilmStarshipsResponse extends Message { 52 | /** 53 | * @generated from field: repeated buf.starwars.relation.v1.GetFilmStarshipsResponse.Value values = 1; 54 | */ 55 | values: GetFilmStarshipsResponse_Value[] = []; 56 | 57 | constructor(data?: PartialMessage) { 58 | super(); 59 | proto3.util.initPartial(data, this); 60 | } 61 | 62 | static readonly runtime: typeof proto3 = proto3; 63 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsResponse"; 64 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 65 | { no: 1, name: "values", kind: "message", T: GetFilmStarshipsResponse_Value, repeated: true }, 66 | ]); 67 | 68 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsResponse { 69 | return new GetFilmStarshipsResponse().fromBinary(bytes, options); 70 | } 71 | 72 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsResponse { 73 | return new GetFilmStarshipsResponse().fromJson(jsonValue, options); 74 | } 75 | 76 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsResponse { 77 | return new GetFilmStarshipsResponse().fromJsonString(jsonString, options); 78 | } 79 | 80 | static equals(a: GetFilmStarshipsResponse | PlainMessage | undefined, b: GetFilmStarshipsResponse | PlainMessage | undefined): boolean { 81 | return proto3.util.equals(GetFilmStarshipsResponse, a, b); 82 | } 83 | } 84 | 85 | /** 86 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsResponse.Value 87 | */ 88 | export class GetFilmStarshipsResponse_Value extends Message { 89 | /** 90 | * @generated from field: repeated buf.starwars.starship.v1.Starship starships = 1; 91 | */ 92 | starships: Starship[] = []; 93 | 94 | constructor(data?: PartialMessage) { 95 | super(); 96 | proto3.util.initPartial(data, this); 97 | } 98 | 99 | static readonly runtime: typeof proto3 = proto3; 100 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsResponse.Value"; 101 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 102 | { no: 1, name: "starships", kind: "message", T: Starship, repeated: true }, 103 | ]); 104 | 105 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsResponse_Value { 106 | return new GetFilmStarshipsResponse_Value().fromBinary(bytes, options); 107 | } 108 | 109 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsResponse_Value { 110 | return new GetFilmStarshipsResponse_Value().fromJson(jsonValue, options); 111 | } 112 | 113 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsResponse_Value { 114 | return new GetFilmStarshipsResponse_Value().fromJsonString(jsonString, options); 115 | } 116 | 117 | static equals(a: GetFilmStarshipsResponse_Value | PlainMessage | undefined, b: GetFilmStarshipsResponse_Value | PlainMessage | undefined): boolean { 118 | return proto3.util.equals(GetFilmStarshipsResponse_Value, a, b); 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/starship/v1/starship_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { StarshipsRequest, StarshipsResponse } from "./starship_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.starship.v1.StarshipService 11 | */ 12 | export const StarshipService = { 13 | typeName: "buf.starwars.starship.v1.StarshipService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.starship.v1.StarshipService.GetStarships 17 | */ 18 | getStarships: { 19 | name: "GetStarships", 20 | I: StarshipsRequest, 21 | O: StarshipsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/starship/v1/starship_knit.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-knit-ts v0.0.1 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | export type StarshipService = { 7 | "buf.starwars.starship.v1.StarshipService": { 8 | fetch: { 9 | }; 10 | do: { 11 | getStarships: { $: StarshipsRequest; value: StarshipsResponse; }; 12 | }; 13 | listen: { 14 | }; 15 | }; 16 | }; 17 | 18 | export interface Starship { 19 | starshipId: string; 20 | name: string; 21 | model: string; 22 | manufacturer: string; 23 | costInCredits: bigint; 24 | length: bigint; 25 | maxAtmospheringSpeed: bigint; 26 | crew: bigint; 27 | passengers: bigint; 28 | cargoCapacity: bigint; 29 | consumables: string; 30 | hyperdriveRating: number; 31 | mglt: bigint; 32 | starshipClass: string; 33 | pilotIds: Array; 34 | filmIds: Array; 35 | }; 36 | 37 | export interface StarshipsRequest { 38 | starshipIds: Array; 39 | limit: number; 40 | }; 41 | 42 | export interface StarshipsResponse { 43 | starships: Array; 44 | }; 45 | 46 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/gen/buf/starwars/starship/v1/starship_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.starship.v1.Starship 11 | */ 12 | export class Starship extends Message { 13 | /** 14 | * @generated from field: string starship_id = 1; 15 | */ 16 | starshipId = ""; 17 | 18 | /** 19 | * @generated from field: string name = 2; 20 | */ 21 | name = ""; 22 | 23 | /** 24 | * @generated from field: string model = 3; 25 | */ 26 | model = ""; 27 | 28 | /** 29 | * @generated from field: string manufacturer = 4; 30 | */ 31 | manufacturer = ""; 32 | 33 | /** 34 | * @generated from field: uint64 cost_in_credits = 5; 35 | */ 36 | costInCredits = protoInt64.zero; 37 | 38 | /** 39 | * @generated from field: uint64 length = 6; 40 | */ 41 | length = protoInt64.zero; 42 | 43 | /** 44 | * @generated from field: uint64 max_atmosphering_speed = 7; 45 | */ 46 | maxAtmospheringSpeed = protoInt64.zero; 47 | 48 | /** 49 | * @generated from field: uint64 crew = 8; 50 | */ 51 | crew = protoInt64.zero; 52 | 53 | /** 54 | * @generated from field: uint64 passengers = 9; 55 | */ 56 | passengers = protoInt64.zero; 57 | 58 | /** 59 | * @generated from field: uint64 cargo_capacity = 10; 60 | */ 61 | cargoCapacity = protoInt64.zero; 62 | 63 | /** 64 | * @generated from field: string consumables = 11; 65 | */ 66 | consumables = ""; 67 | 68 | /** 69 | * @generated from field: float hyperdrive_rating = 12; 70 | */ 71 | hyperdriveRating = 0; 72 | 73 | /** 74 | * @generated from field: uint64 mglt = 13; 75 | */ 76 | mglt = protoInt64.zero; 77 | 78 | /** 79 | * @generated from field: string starship_class = 14; 80 | */ 81 | starshipClass = ""; 82 | 83 | /** 84 | * Relations 85 | * 86 | * @generated from field: repeated string pilot_ids = 15; 87 | */ 88 | pilotIds: string[] = []; 89 | 90 | /** 91 | * @generated from field: repeated string film_ids = 16; 92 | */ 93 | filmIds: string[] = []; 94 | 95 | constructor(data?: PartialMessage) { 96 | super(); 97 | proto3.util.initPartial(data, this); 98 | } 99 | 100 | static readonly runtime: typeof proto3 = proto3; 101 | static readonly typeName = "buf.starwars.starship.v1.Starship"; 102 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 103 | { no: 1, name: "starship_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 104 | { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 105 | { no: 3, name: "model", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 106 | { no: 4, name: "manufacturer", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 107 | { no: 5, name: "cost_in_credits", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 108 | { no: 6, name: "length", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 109 | { no: 7, name: "max_atmosphering_speed", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 110 | { no: 8, name: "crew", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 111 | { no: 9, name: "passengers", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 112 | { no: 10, name: "cargo_capacity", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 113 | { no: 11, name: "consumables", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 114 | { no: 12, name: "hyperdrive_rating", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, 115 | { no: 13, name: "mglt", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 116 | { no: 14, name: "starship_class", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 117 | { no: 15, name: "pilot_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 118 | { no: 16, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 119 | ]); 120 | 121 | static fromBinary(bytes: Uint8Array, options?: Partial): Starship { 122 | return new Starship().fromBinary(bytes, options); 123 | } 124 | 125 | static fromJson(jsonValue: JsonValue, options?: Partial): Starship { 126 | return new Starship().fromJson(jsonValue, options); 127 | } 128 | 129 | static fromJsonString(jsonString: string, options?: Partial): Starship { 130 | return new Starship().fromJsonString(jsonString, options); 131 | } 132 | 133 | static equals(a: Starship | PlainMessage | undefined, b: Starship | PlainMessage | undefined): boolean { 134 | return proto3.util.equals(Starship, a, b); 135 | } 136 | } 137 | 138 | /** 139 | * @generated from message buf.starwars.starship.v1.StarshipsRequest 140 | */ 141 | export class StarshipsRequest extends Message { 142 | /** 143 | * @generated from field: repeated string starship_ids = 1; 144 | */ 145 | starshipIds: string[] = []; 146 | 147 | /** 148 | * @generated from field: uint32 limit = 2; 149 | */ 150 | limit = 0; 151 | 152 | constructor(data?: PartialMessage) { 153 | super(); 154 | proto3.util.initPartial(data, this); 155 | } 156 | 157 | static readonly runtime: typeof proto3 = proto3; 158 | static readonly typeName = "buf.starwars.starship.v1.StarshipsRequest"; 159 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 160 | { no: 1, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 161 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 162 | ]); 163 | 164 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsRequest { 165 | return new StarshipsRequest().fromBinary(bytes, options); 166 | } 167 | 168 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsRequest { 169 | return new StarshipsRequest().fromJson(jsonValue, options); 170 | } 171 | 172 | static fromJsonString(jsonString: string, options?: Partial): StarshipsRequest { 173 | return new StarshipsRequest().fromJsonString(jsonString, options); 174 | } 175 | 176 | static equals(a: StarshipsRequest | PlainMessage | undefined, b: StarshipsRequest | PlainMessage | undefined): boolean { 177 | return proto3.util.equals(StarshipsRequest, a, b); 178 | } 179 | } 180 | 181 | /** 182 | * @generated from message buf.starwars.starship.v1.StarshipsResponse 183 | */ 184 | export class StarshipsResponse extends Message { 185 | /** 186 | * @generated from field: repeated buf.starwars.starship.v1.Starship starships = 1; 187 | */ 188 | starships: Starship[] = []; 189 | 190 | constructor(data?: PartialMessage) { 191 | super(); 192 | proto3.util.initPartial(data, this); 193 | } 194 | 195 | static readonly runtime: typeof proto3 = proto3; 196 | static readonly typeName = "buf.starwars.starship.v1.StarshipsResponse"; 197 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 198 | { no: 1, name: "starships", kind: "message", T: Starship, repeated: true }, 199 | ]); 200 | 201 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsResponse { 202 | return new StarshipsResponse().fromBinary(bytes, options); 203 | } 204 | 205 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsResponse { 206 | return new StarshipsResponse().fromJson(jsonValue, options); 207 | } 208 | 209 | static fromJsonString(jsonString: string, options?: Partial): StarshipsResponse { 210 | return new StarshipsResponse().fromJsonString(jsonString, options); 211 | } 212 | 213 | static equals(a: StarshipsResponse | PlainMessage | undefined, b: StarshipsResponse | PlainMessage | undefined): boolean { 214 | return proto3.util.equals(StarshipsResponse, a, b); 215 | } 216 | } 217 | 218 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React + Knit 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starwars-knit-client-app-ts", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@bufbuild/buf": "^1.26.1", 13 | "@connectrpc/connect": "^0.13.1", 14 | "@connectrpc/protoc-gen-connect-es": "^0.13.1", 15 | "@bufbuild/protobuf": "^1.3.0", 16 | "@bufbuild/protoc-gen-es": "^1.3.0", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "@bufbuild/knit": "^0.0.1" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^18.0.28", 23 | "@types/react-dom": "^18.0.11", 24 | "@vitejs/plugin-react": "^4.4.0", 25 | "typescript": "^4.9.3", 26 | "vite": "^6.3.4", 27 | "@bufbuild/protoc-gen-knit-ts": "^0.0.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufbuild/knit/b4431e120487589868d918d52d499798b2ae7134/tutorial/starwars-knit-client-app-ts/public/favicon.ico -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .film { 9 | background-color:#f5f5fcaa; 10 | border: 1px solid #646cff; 11 | margin: 10px; 12 | padding: 10px; 13 | border-radius: 10px; 14 | } 15 | 16 | .title { 17 | font-size: 1.0em; 18 | font-weight: bold; 19 | padding-bottom: 20px; 20 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import './App.css' 3 | 4 | import type { FilmService } from '../gen/buf/starwars/film/v1/film_knit'; 5 | import type { StarshipService } from '../gen/buf/starwars/starship/v1/starship_knit'; 6 | import type { } from '../gen/buf/starwars/relation/v1/relation_knit'; 7 | import { createClient } from '@bufbuild/knit'; 8 | 9 | type Schema = FilmService & StarshipService; 10 | 11 | const client = createClient({ 12 | baseUrl: 'http://localhost:5173/knit', 13 | }) 14 | 15 | function App(): JSX.Element { 16 | const [data, setFilms] = useState(new Array<{title: string, models: string[]}>()); 17 | 18 | async function callKnit() { 19 | const resp = await client.do({ 20 | "buf.starwars.film.v1.FilmService": { 21 | getFilms: { 22 | $: { filmIds: ["1","2"] }, 23 | films: { 24 | title: {}, 25 | starships: { 26 | $: {}, 27 | model: {}, 28 | }, 29 | }, 30 | }, 31 | }, 32 | }); 33 | 34 | const films = resp['buf.starwars.film.v1.FilmService'].getFilms.films.map(film => { 35 | return { 36 | title: film.title, 37 | models: film.starships.map(ship => ship.model), 38 | } 39 | }); 40 | setFilms(films); 41 | } 42 | 43 | return ( 44 |
45 | 50 |

Vite + React + Knit

51 |
52 | 55 |
56 |
57 | {data.map((v) =>
{Film(v.title, v.models)}
)} 58 |
59 |
60 | ) 61 | } 62 | 63 | function Film(title: string, starships: string[]): JSX.Element { 64 | return ( 65 |
66 |
{title}
67 | {starships.map(model =>
{model}
)} 68 |
69 | ) 70 | } 71 | 72 | export default App 73 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: top center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | 54 | @media (prefers-color-scheme: light) { 55 | :root { 56 | color: #213547; 57 | background-color: #ffffff; 58 | } 59 | a:hover { 60 | color: #747bff; 61 | } 62 | button { 63 | background-color: #f9f9f9; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-client-app-ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { 8 | proxy: { 9 | '/knit': { 10 | target: 'http://127.0.0.1:8080', 11 | changeOrigin: true, 12 | secure: false, 13 | rewrite: (path) => path.replace(/^\/knit/, ""), 14 | } 15 | } 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/README.md: -------------------------------------------------------------------------------- 1 | # 🧶 Knit Gateway in Go 2 | 3 | [Back to top of Tutorial] 4 | 5 | In this tutorial the Knit gateway is implemented using [knit-go], and is made to 6 | listen on address `http://localhost:8080`. Look at the process diagram below to 7 | see where the Knit gateway fits into the bigger picture. 8 | 9 | The Knit gateway is what puts the magic into a system using Knit. The gateway is 10 | called by Knit clients, and based on the clients' queries automatically issues 11 | RPCs to the required backend services to evaluate a query. The gateway will 12 | automatically batch and parallelize the requests, issue them in the correct 13 | order, and flow required data from responses into subsequent requests until a 14 | query is fully executed. 15 | 16 | ```mermaid 17 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 18 | flowchart LR 19 | A[Knit Client] --> B[Knit Gateway] 20 | subgraph r [Knit Relation Service] 21 | R{{Relation RPCs}} 22 | end 23 | subgraph f [Film Service] 24 | F{{Film RPCs}} 25 | end 26 | subgraph s [Starship Service] 27 | S{{Starship RPCs}} 28 | end 29 | B --> R 30 | B --> F 31 | B --> S 32 | style B stroke:#000,stroke-width:3px 33 | ``` 34 | 35 | ## How to run the code 36 | To run the Knit gateway service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 37 | then execute the following from the base of the repository (the other services must be running too). 38 | 39 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 40 | ``` 41 | cd tutorial/starwars-knit-gateway-go/cmd/gateway 42 | 43 | go mod tidy 44 | go run gateway.go 45 | 46 | # Output 47 | 2023/05/01 11:32:49 Knit gateway starting 48 | 2023/05/01 11:32:49 Listening on: 127.0.0.1:8080 49 | ``` 50 | 51 | [Back to top of Tutorial]: /tutorial 52 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 53 | [knit-go]: https://github.com/bufbuild/knit-go 54 | [badges_slack]: https://buf.build/links/slack 55 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen 6 | override: 7 | # Without this override dependencies would also 8 | # be prefixed with the default prefix. 9 | buf.build/bufbuild/knit: buf.build/gen/go/bufbuild/knit/protocolbuffers/go 10 | plugins: 11 | - plugin: buf.build/protocolbuffers/go 12 | out: gen 13 | opt: paths=source_relative 14 | - plugin: buf.build/bufbuild/connect-go 15 | out: gen 16 | opt: paths=source_relative 17 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/cmd/gateway/gateway.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "log" 19 | "net/http" 20 | "net/url" 21 | 22 | grpcreflect "github.com/bufbuild/connect-grpcreflect-go" 23 | "github.com/bufbuild/knit-go" 24 | "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/film/v1/filmv1connect" 25 | "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/relation/v1/relationv1connect" 26 | "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/starship/v1/starshipv1connect" 27 | "golang.org/x/net/http2" 28 | "golang.org/x/net/http2/h2c" 29 | "google.golang.org/protobuf/reflect/protoreflect" 30 | ) 31 | 32 | func main() { 33 | const addr = "127.0.0.1:8080" 34 | 35 | log.Printf("Knit gateway starting") 36 | 37 | relationServiceURL, err := url.Parse("http://127.0.0.1:18000") 38 | if err != nil { 39 | log.Fatalf("Failed to parse relation URL: %v", err) 40 | } 41 | 42 | filmServiceURL, err := url.Parse("http://127.0.0.1:18001") 43 | if err != nil { 44 | log.Fatalf("Failed to parse film URL: %v", err) 45 | } 46 | 47 | starshipServiceURL, err := url.Parse("http://127.0.0.1:18002") 48 | if err != nil { 49 | log.Fatalf("Failed to parse person URL: %v", err) 50 | } 51 | 52 | gateway := knit.Gateway{} 53 | if err := gateway.AddServiceByName(protoreflect.FullName(relationv1connect.RelationServiceName), knit.WithRoute(relationServiceURL)); err != nil { 54 | log.Fatalf("Failed to add service: %v, error: %v", relationv1connect.RelationServiceName, err) 55 | } 56 | if err := gateway.AddServiceByName(protoreflect.FullName(filmv1connect.FilmServiceName), knit.WithRoute(filmServiceURL)); err != nil { 57 | log.Fatalf("Failed to add service: %v, error: %v", filmv1connect.FilmServiceName, err) 58 | } 59 | if err := gateway.AddServiceByName(protoreflect.FullName(starshipv1connect.StarshipServiceName), knit.WithRoute(starshipServiceURL)); err != nil { 60 | log.Fatalf("Failed to add service: %v, error: %v", starshipv1connect.StarshipServiceName, err) 61 | } 62 | 63 | mux := http.NewServeMux() 64 | mux.Handle(gateway.AsHandler()) 65 | 66 | reflector := grpcreflect.NewStaticReflector( 67 | relationv1connect.RelationServiceName, 68 | filmv1connect.FilmServiceName, 69 | starshipv1connect.StarshipServiceName, 70 | ) 71 | 72 | mux.Handle(grpcreflect.NewHandlerV1(reflector)) 73 | mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector)) 74 | 75 | log.Printf("Listening on: %v", addr) 76 | err = http.ListenAndServe( 77 | addr, 78 | h2c.NewHandler(mux, &http2.Server{}), 79 | ) 80 | 81 | if err != http.ErrServerClosed { 82 | log.Printf("Error running or stopping: %v", err) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/gen/buf/starwars/film/v1/filmv1connect/film.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/film/v1/film.proto 4 | 5 | package filmv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/film/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // FilmServiceName is the fully-qualified name of the FilmService service. 25 | FilmServiceName = "buf.starwars.film.v1.FilmService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // FilmServiceGetFilmsProcedure is the fully-qualified name of the FilmService's GetFilms RPC. 37 | FilmServiceGetFilmsProcedure = "/buf.starwars.film.v1.FilmService/GetFilms" 38 | ) 39 | 40 | // FilmServiceClient is a client for the buf.starwars.film.v1.FilmService service. 41 | type FilmServiceClient interface { 42 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 43 | } 44 | 45 | // NewFilmServiceClient constructs a client for the buf.starwars.film.v1.FilmService service. By 46 | // default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, 47 | // and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the 48 | // connect.WithGRPC() or connect.WithGRPCWeb() options. 49 | // 50 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 51 | // http://api.acme.com or https://acme.com/grpc). 52 | func NewFilmServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) FilmServiceClient { 53 | baseURL = strings.TrimRight(baseURL, "/") 54 | return &filmServiceClient{ 55 | getFilms: connect_go.NewClient[v1.FilmsRequest, v1.FilmsResponse]( 56 | httpClient, 57 | baseURL+FilmServiceGetFilmsProcedure, 58 | opts..., 59 | ), 60 | } 61 | } 62 | 63 | // filmServiceClient implements FilmServiceClient. 64 | type filmServiceClient struct { 65 | getFilms *connect_go.Client[v1.FilmsRequest, v1.FilmsResponse] 66 | } 67 | 68 | // GetFilms calls buf.starwars.film.v1.FilmService.GetFilms. 69 | func (c *filmServiceClient) GetFilms(ctx context.Context, req *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 70 | return c.getFilms.CallUnary(ctx, req) 71 | } 72 | 73 | // FilmServiceHandler is an implementation of the buf.starwars.film.v1.FilmService service. 74 | type FilmServiceHandler interface { 75 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 76 | } 77 | 78 | // NewFilmServiceHandler builds an HTTP handler from the service implementation. It returns the path 79 | // on which to mount the handler and the handler itself. 80 | // 81 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 82 | // and JSON codecs. They also support gzip compression. 83 | func NewFilmServiceHandler(svc FilmServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 84 | mux := http.NewServeMux() 85 | mux.Handle(FilmServiceGetFilmsProcedure, connect_go.NewUnaryHandler( 86 | FilmServiceGetFilmsProcedure, 87 | svc.GetFilms, 88 | opts..., 89 | )) 90 | return "/buf.starwars.film.v1.FilmService/", mux 91 | } 92 | 93 | // UnimplementedFilmServiceHandler returns CodeUnimplemented from all methods. 94 | type UnimplementedFilmServiceHandler struct{} 95 | 96 | func (UnimplementedFilmServiceHandler) GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 97 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.film.v1.FilmService.GetFilms is not implemented")) 98 | } 99 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/gen/buf/starwars/relation/v1/relationv1connect/relation.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/relation/v1/relation.proto 4 | 5 | package relationv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/relation/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion1_7_0 22 | 23 | const ( 24 | // RelationServiceName is the fully-qualified name of the RelationService service. 25 | RelationServiceName = "buf.starwars.relation.v1.RelationService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // RelationServiceGetFilmStarshipsProcedure is the fully-qualified name of the RelationService's 37 | // GetFilmStarships RPC. 38 | RelationServiceGetFilmStarshipsProcedure = "/buf.starwars.relation.v1.RelationService/GetFilmStarships" 39 | ) 40 | 41 | // RelationServiceClient is a client for the buf.starwars.relation.v1.RelationService service. 42 | type RelationServiceClient interface { 43 | GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) 44 | } 45 | 46 | // NewRelationServiceClient constructs a client for the buf.starwars.relation.v1.RelationService 47 | // service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for 48 | // gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply 49 | // the connect.WithGRPC() or connect.WithGRPCWeb() options. 50 | // 51 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 52 | // http://api.acme.com or https://acme.com/grpc). 53 | func NewRelationServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) RelationServiceClient { 54 | baseURL = strings.TrimRight(baseURL, "/") 55 | return &relationServiceClient{ 56 | getFilmStarships: connect_go.NewClient[v1.GetFilmStarshipsRequest, v1.GetFilmStarshipsResponse]( 57 | httpClient, 58 | baseURL+RelationServiceGetFilmStarshipsProcedure, 59 | connect_go.WithIdempotency(connect_go.IdempotencyNoSideEffects), 60 | connect_go.WithClientOptions(opts...), 61 | ), 62 | } 63 | } 64 | 65 | // relationServiceClient implements RelationServiceClient. 66 | type relationServiceClient struct { 67 | getFilmStarships *connect_go.Client[v1.GetFilmStarshipsRequest, v1.GetFilmStarshipsResponse] 68 | } 69 | 70 | // GetFilmStarships calls buf.starwars.relation.v1.RelationService.GetFilmStarships. 71 | func (c *relationServiceClient) GetFilmStarships(ctx context.Context, req *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) { 72 | return c.getFilmStarships.CallUnary(ctx, req) 73 | } 74 | 75 | // RelationServiceHandler is an implementation of the buf.starwars.relation.v1.RelationService 76 | // service. 77 | type RelationServiceHandler interface { 78 | GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) 79 | } 80 | 81 | // NewRelationServiceHandler builds an HTTP handler from the service implementation. It returns the 82 | // path on which to mount the handler and the handler itself. 83 | // 84 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 85 | // and JSON codecs. They also support gzip compression. 86 | func NewRelationServiceHandler(svc RelationServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 87 | mux := http.NewServeMux() 88 | mux.Handle(RelationServiceGetFilmStarshipsProcedure, connect_go.NewUnaryHandler( 89 | RelationServiceGetFilmStarshipsProcedure, 90 | svc.GetFilmStarships, 91 | connect_go.WithIdempotency(connect_go.IdempotencyNoSideEffects), 92 | connect_go.WithHandlerOptions(opts...), 93 | )) 94 | return "/buf.starwars.relation.v1.RelationService/", mux 95 | } 96 | 97 | // UnimplementedRelationServiceHandler returns CodeUnimplemented from all methods. 98 | type UnimplementedRelationServiceHandler struct{} 99 | 100 | func (UnimplementedRelationServiceHandler) GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) { 101 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.relation.v1.RelationService.GetFilmStarships is not implemented")) 102 | } 103 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/gen/buf/starwars/starship/v1/starshipv1connect/starship.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/starship/v1/starship.proto 4 | 5 | package starshipv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go/gen/buf/starwars/starship/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // StarshipServiceName is the fully-qualified name of the StarshipService service. 25 | StarshipServiceName = "buf.starwars.starship.v1.StarshipService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // StarshipServiceGetStarshipsProcedure is the fully-qualified name of the StarshipService's 37 | // GetStarships RPC. 38 | StarshipServiceGetStarshipsProcedure = "/buf.starwars.starship.v1.StarshipService/GetStarships" 39 | ) 40 | 41 | // StarshipServiceClient is a client for the buf.starwars.starship.v1.StarshipService service. 42 | type StarshipServiceClient interface { 43 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 44 | } 45 | 46 | // NewStarshipServiceClient constructs a client for the buf.starwars.starship.v1.StarshipService 47 | // service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for 48 | // gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply 49 | // the connect.WithGRPC() or connect.WithGRPCWeb() options. 50 | // 51 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 52 | // http://api.acme.com or https://acme.com/grpc). 53 | func NewStarshipServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) StarshipServiceClient { 54 | baseURL = strings.TrimRight(baseURL, "/") 55 | return &starshipServiceClient{ 56 | getStarships: connect_go.NewClient[v1.StarshipsRequest, v1.StarshipsResponse]( 57 | httpClient, 58 | baseURL+StarshipServiceGetStarshipsProcedure, 59 | opts..., 60 | ), 61 | } 62 | } 63 | 64 | // starshipServiceClient implements StarshipServiceClient. 65 | type starshipServiceClient struct { 66 | getStarships *connect_go.Client[v1.StarshipsRequest, v1.StarshipsResponse] 67 | } 68 | 69 | // GetStarships calls buf.starwars.starship.v1.StarshipService.GetStarships. 70 | func (c *starshipServiceClient) GetStarships(ctx context.Context, req *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 71 | return c.getStarships.CallUnary(ctx, req) 72 | } 73 | 74 | // StarshipServiceHandler is an implementation of the buf.starwars.starship.v1.StarshipService 75 | // service. 76 | type StarshipServiceHandler interface { 77 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 78 | } 79 | 80 | // NewStarshipServiceHandler builds an HTTP handler from the service implementation. It returns the 81 | // path on which to mount the handler and the handler itself. 82 | // 83 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 84 | // and JSON codecs. They also support gzip compression. 85 | func NewStarshipServiceHandler(svc StarshipServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 86 | mux := http.NewServeMux() 87 | mux.Handle(StarshipServiceGetStarshipsProcedure, connect_go.NewUnaryHandler( 88 | StarshipServiceGetStarshipsProcedure, 89 | svc.GetStarships, 90 | opts..., 91 | )) 92 | return "/buf.starwars.starship.v1.StarshipService/", mux 93 | } 94 | 95 | // UnimplementedStarshipServiceHandler returns CodeUnimplemented from all methods. 96 | type UnimplementedStarshipServiceHandler struct{} 97 | 98 | func (UnimplementedStarshipServiceHandler) GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 99 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.starship.v1.StarshipService.GetStarships is not implemented")) 100 | } 101 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bufbuild/knit/tutorial/starwars-knit-gateway-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1 7 | github.com/bufbuild/connect-go v1.7.0 8 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364 9 | github.com/bufbuild/knit-go v0.0.1 10 | golang.org/x/net v0.39.0 11 | google.golang.org/protobuf v1.33.0 12 | ) 13 | 14 | require ( 15 | buf.build/gen/go/bufbuild/knit/bufbuild/connect-go v1.7.0-20230504140941-3dc602456973.1 // indirect 16 | golang.org/x/sync v0.13.0 // indirect 17 | golang.org/x/text v0.24.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-go/go.sum: -------------------------------------------------------------------------------- 1 | buf.build/gen/go/bufbuild/knit/bufbuild/connect-go v1.7.0-20230504140941-3dc602456973.1 h1:UO5VZtfLW03qs8stGP9jDstQQzzAJdbZmlVtIOah280= 2 | buf.build/gen/go/bufbuild/knit/bufbuild/connect-go v1.7.0-20230504140941-3dc602456973.1/go.mod h1:zE4qs0wSqtp0QpzXngvrgGLFy6ZBpL8EE2jSmdOUD+w= 3 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1 h1:XBygYORdxM6ImtYu7NsJd94W2k5eYZAqUiOaly4jdyE= 4 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1/go.mod h1:puJ8m7+AgZdN63sAJ02dpgbuhoFQN1URhTWULCBnALs= 5 | github.com/bufbuild/connect-go v1.7.0 h1:MGp82v7SCza+3RhsVhV7aMikwxvI3ZfD72YiGt8FYJo= 6 | github.com/bufbuild/connect-go v1.7.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk= 7 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364 h1:qiRIhoFC0QibU5hUHZNVM0Rgowsv4Kw6S1te+Q7WLPg= 8 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364/go.mod h1:57KmO9HZ/NoysS7mdbbCzUq/y6ogUFIZv034OHUR1as= 9 | github.com/bufbuild/knit-go v0.0.1 h1:gQJ2uWR1ds1HDpUDOY7jdk2mGWUX1EJaz3pMCM5eNHE= 10 | github.com/bufbuild/knit-go v0.0.1/go.mod h1:XtySG60koyHlcxVog2PaWst7EKTq3/UvVbjTFQJHCrM= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 14 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 15 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 16 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 20 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 21 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= 22 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 23 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 24 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 25 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 26 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 27 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 28 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 29 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 30 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 31 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 32 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 33 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 34 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/README.md: -------------------------------------------------------------------------------- 1 | # 🧶 Knit Gateway (Standalone) 2 | 3 | [Back to top of Tutorial] 4 | 5 | In this tutorial the Knit standalone gateway is configured to use the Film 6 | Service, Starship Service, and Relation Service to reslove requests, and is 7 | made to listen on address `http://localhost:8080`. Look at the process diagram 8 | below to see where the Knit gateway fits into the bigger picture. 9 | 10 | The Knit gateway is what puts the magic into a system using Knit. The gateway is 11 | called by Knit clients, and based on the clients' queries automatically issues 12 | RPCs to the required backend services to evaluate a query. The gateway will 13 | automatically batch and parallelize the requests, issue them in the correct 14 | order, and flow required data from responses into subsequent requests until a 15 | query is fully executed. 16 | 17 | ```mermaid 18 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 19 | flowchart LR 20 | A[Knit Client] --> B[Knit Gateway] 21 | subgraph r [Knit Relation Service] 22 | R{{Relation RPCs}} 23 | end 24 | subgraph f [Film Service] 25 | F{{Film RPCs}} 26 | end 27 | subgraph s [Starship Service] 28 | S{{Starship RPCs}} 29 | end 30 | B --> R 31 | B --> F 32 | B --> S 33 | style B stroke:#000,stroke-width:3px 34 | ``` 35 | 36 | ## How to run the code 37 | To install the Knit standalone gateway clone the [knit-go] repo using 38 | `git clone https://github.com/bufbuild/knit-go.git`, then execute the 39 | following from the base of the repository: 40 | 41 | ``` 42 | cd cmd/knitgateway 43 | 44 | go mod tidy 45 | go install 46 | 47 | # Try running the gateway, it should error because 48 | # there is no configuration file. 49 | knitgateway 50 | 51 | # Output 52 | 2023/04/26 16:45:52 failed to load config from knitgateway.yaml: open knitgateway.yaml: no such file or directory 53 | ``` 54 | 55 | To get the configuration file for the tutorial clone the [knit] repo using 56 | `git clone https://github.com/bufbuild/knit.git`, then execute the following 57 | from the base of the repository (the other services must be running too). 58 | 59 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 60 | ``` 61 | cd tutorial/starwars-knit-gateway-standalone 62 | 63 | knitgateway 64 | 65 | # Output 66 | 2023/04/26 16:48:00 Listening on 127.0.0.1:8080 for HTTP requests... 67 | ``` 68 | 69 | ## Configuring the gateway 70 | The `knitgateway` looks for a file called `knitgateway.yaml` in the current 71 | working directory, or it can be invoked as `knitgateway -conf ` for a 72 | specific configuration path and file. 73 | 74 | The configuration used for this tutorial is shown below. Because the tutorial has 75 | each service running in its own backend process, each service has its own section 76 | under `backends`: 77 | 78 | ```yaml 79 | listen: 80 | bind_address: 127.0.0.1 81 | port: 8080 82 | 83 | backends: 84 | - route_to: http://127.0.0.1:18000 85 | services: 86 | - buf.starwars.relation.v1.RelationService 87 | descriptors: 88 | descriptor_set_file: schema.protoset 89 | - route_to: http://127.0.0.1:18001 90 | services: 91 | - buf.starwars.film.v1.FilmService 92 | descriptors: 93 | descriptor_set_file: schema.protoset 94 | - route_to: http://127.0.0.1:18002 95 | services: 96 | - buf.starwars.starship.v1.StarshipService 97 | descriptors: 98 | descriptor_set_file: schema.protoset 99 | ``` 100 | 101 | ## Schema 102 | The `knitgateway.yaml` in this tutorial sets the Knit gateway to look for the schema 103 | of all three services in the binary file called `schema.protoset`, which can be 104 | recreated by using the `buf build` command: 105 | 106 | ``` 107 | buf build proto -o schema.protoset 108 | ``` 109 | 110 | Note that the `knitgateway` can be configured to find the schema of the services it 111 | calls in many different ways, including using the [Buf Schema Registry] and gRPC 112 | reflection. 113 | 114 | ## Configuration options 115 | The gateway itself is configured with: 116 | 117 | * `bind_address`: The address on which to listen, defaults to 127.0.0.1 if not provided 118 | * `port`: The port number on which to listen, defaults to an ephemeral port if not provided 119 | 120 | The rest of the configuration found under the `backends` key word configures 121 | the gateway with: 122 | 123 | * Locations of processes that the gateway needs to call 124 | * Services within those processes that speak the gRPC, gRPC-web, or connect protocol 125 | * Methods of gettings the schema, aka: getting the descriptors, of those services 126 | 127 | The relevant parameters are: 128 | * `route_to`: The base URL for a backend process 129 | * `services`: The list of fully-qualified service names running in a backend process 130 | * `descriptors`: The method to use to discover the schema of all the services running in a backend process 131 | * `descriptor_set_file`: The file name of the `.protoset` containing the schema for a bundle of services 132 | 133 | See the [knit-go] repo for all the details of how the Knit standalone gateway 134 | can be configured, including the many different ways the gateway can find 135 | service schemas. 136 | 137 | [Back to top of Tutorial]: /tutorial 138 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 139 | [knit]: https://github.com/bufbuild/knit 140 | [knit-go]: https://github.com/bufbuild/knit-go 141 | [descriptor set examples]: https://github.com/bufbuild/knit-go#descriptor-set-examples 142 | [badges_slack]: https://buf.build/links/slack 143 | [file descriptor set]: https://github.com/protocolbuffers/protobuf/blob/v22.0/src/google/protobuf/descriptor.proto#L54-L58 144 | [gRPC server reflection]: https://github.com/grpc/grpc/blob/master/doc/server-reflection.md 145 | [buf schema registry]: https://buf.build/ -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/knitgateway.yaml: -------------------------------------------------------------------------------- 1 | listen: 2 | bind_address: 127.0.0.1 3 | port: 8080 4 | 5 | backends: 6 | - route_to: http://127.0.0.1:18000 7 | services: 8 | - buf.starwars.relation.v1.RelationService 9 | descriptors: 10 | descriptor_set_file: schema.protoset 11 | - route_to: http://127.0.0.1:18001 12 | services: 13 | - buf.starwars.film.v1.FilmService 14 | descriptors: 15 | descriptor_set_file: schema.protoset 16 | - route_to: http://127.0.0.1:18002 17 | services: 18 | - buf.starwars.starship.v1.StarshipService 19 | descriptors: 20 | descriptor_set_file: schema.protoset -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: bufbuild 6 | repository: knit 7 | commit: 3dc602456973471584684e4878f99b10 8 | digest: shake256:5909694f816b2ebb5a502fadcb722a3cb73c217c4e118508c41219009b91d2b2b484fd6b5d9eb5b972f1fe9244ab24783a191d859390f9e6e36ee5a6c2479823 9 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT 8 | deps: 9 | - buf.build/bufbuild/knit -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/proto/buf/starwars/film/v1/film.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.film.v1; 4 | 5 | message Film { 6 | string film_id = 1; 7 | uint64 episode_number = 2; 8 | string title = 3; 9 | string opening_text = 4; 10 | repeated string directors = 5; 11 | repeated string producers = 6; 12 | repeated string character_ids = 7; 13 | repeated string planet_ids = 8; 14 | repeated string starship_ids = 9; 15 | repeated string vehicle_ids = 10; 16 | repeated string species_ids = 11; 17 | } 18 | 19 | message FilmsRequest { 20 | repeated string film_ids = 1; 21 | uint32 limit = 2; 22 | } 23 | 24 | message FilmsResponse { 25 | repeated Film films = 1; 26 | } 27 | 28 | service FilmService { 29 | rpc GetFilms(FilmsRequest) returns (FilmsResponse); 30 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/proto/buf/starwars/relation/v1/relation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "buf/starwars/film/v1/film.proto"; 4 | import "buf/starwars/starship/v1/starship.proto"; 5 | import "buf/knit/v1alpha1/options.proto"; 6 | 7 | package buf.starwars.relation.v1; 8 | 9 | message GetFilmStarshipsRequest { 10 | repeated buf.starwars.film.v1.Film bases = 1; 11 | } 12 | 13 | message GetFilmStarshipsResponse { 14 | repeated Result values = 1; 15 | message Result { 16 | repeated buf.starwars.starship.v1.Starship starships = 1; 17 | } 18 | } 19 | 20 | service RelationService { 21 | rpc GetFilmStarships(GetFilmStarshipsRequest) returns (GetFilmStarshipsResponse) { 22 | option (buf.knit.v1alpha1.relation).name = "starships"; 23 | option idempotency_level = NO_SIDE_EFFECTS; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/proto/buf/starwars/starship/v1/starship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.starship.v1; 4 | 5 | message Starship { 6 | string starship_id = 1; 7 | string name = 2; 8 | string model = 3; 9 | string manufacturer = 4; 10 | uint64 cost_in_credits = 5; 11 | uint64 length = 6; 12 | uint64 max_atmosphering_speed = 7; 13 | uint64 crew = 8; 14 | uint64 passengers = 9; 15 | uint64 cargo_capacity = 10; 16 | string consumables = 11; 17 | float hyperdrive_rating = 12; 18 | uint64 mglt = 13; 19 | string starship_class = 14; 20 | // Relations 21 | repeated string pilot_ids = 15; 22 | repeated string film_ids = 16; 23 | } 24 | 25 | message StarshipsRequest { 26 | repeated string starship_ids = 1; 27 | uint32 limit = 2; 28 | } 29 | 30 | message StarshipsResponse { 31 | repeated Starship starships = 1; 32 | } 33 | 34 | service StarshipService { 35 | rpc GetStarships(StarshipsRequest) returns (StarshipsResponse); 36 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-gateway-standalone/schema.protoset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufbuild/knit/b4431e120487589868d918d52d499798b2ae7134/tutorial/starwars-knit-gateway-standalone/schema.protoset -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/README.md: -------------------------------------------------------------------------------- 1 | # 🧶 Knit Relation Service in Go 2 | 3 | [Back to top of Tutorial] 4 | 5 | In this tutorial the Knit relation service is implemented using [connect-go], 6 | and is made to listen on address `http://localhost:18000`. Look at the process 7 | diagram below to see where the Knit relation service fits into the bigger 8 | picture. 9 | 10 | ```mermaid 11 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 12 | flowchart LR 13 | A[Knit Client] --> B[Knit Gateway] 14 | subgraph r [Knit Relation Service] 15 | R{{Relation RPCs}} 16 | end 17 | subgraph f [Film Service] 18 | F{{Film RPCs}} 19 | end 20 | subgraph s [Starship Service] 21 | S{{Starship RPCs}} 22 | end 23 | B --> R 24 | B --> F 25 | B --> S 26 | style r stroke:#000,stroke-width:3px 27 | style R stroke:#000,stroke-width:3px 28 | ``` 29 | 30 | ## How to run the code 31 | To run the Knit relation service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 32 | then execute the following from the base of the repository (the other services must be running too). 33 | 34 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 35 | ``` 36 | cd tutorial/starwars-knit-relation-service-go/cmd/relationservice 37 | 38 | go mod tidy 39 | go run starshipservice.go 40 | 41 | # Output 42 | 2023/05/01 11:32:40 Knit relation service starting 43 | 2023/05/01 11:32:40 Handling connect service at prefix: /buf.starwars.relation.v1.RelationService/ 44 | 2023/05/01 11:32:40 Listening on: 127.0.0.1:18000 45 | ``` 46 | 47 | ## Relations between services 48 | An explanation of how relations between services and their entities are 49 | defined is available in the [Top level Knit README]. 50 | 51 | [Top level Knit README]: https://github.com/bufbuild/knit#relations-between-services 52 | [Back to top of Tutorial]: /tutorial 53 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 54 | [connect-go]: https://github.com/bufbuild/connect-go 55 | [badges_slack]: https://buf.build/links/slack 56 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen 6 | override: 7 | # Without this override dependencies would also 8 | # be prefixed with the default prefix. 9 | buf.build/bufbuild/knit: buf.build/gen/go/bufbuild/knit/protocolbuffers/go 10 | plugins: 11 | - plugin: buf.build/protocolbuffers/go 12 | out: gen 13 | opt: paths=source_relative 14 | - plugin: buf.build/bufbuild/connect-go 15 | out: gen 16 | opt: paths=source_relative 17 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/cmd/relationservice/relationservice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "net/http" 21 | 22 | "github.com/bufbuild/connect-go" 23 | grpcreflect "github.com/bufbuild/connect-grpcreflect-go" 24 | relationv1 "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/relation/v1" 25 | "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/relation/v1/relationv1connect" 26 | starshipv1 "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/starship/v1" 27 | "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/starship/v1/starshipv1connect" 28 | "golang.org/x/net/http2" 29 | "golang.org/x/net/http2/h2c" 30 | ) 31 | 32 | func main() { 33 | const addr = "127.0.0.1:18000" 34 | 35 | log.Printf("Knit relation service starting") 36 | 37 | rel := &RelationService{ 38 | starshipClient: starshipv1connect.NewStarshipServiceClient(http.DefaultClient, "http://localhost:18002"), 39 | } 40 | 41 | path, handler := relationv1connect.NewRelationServiceHandler(rel) 42 | mux := http.NewServeMux() 43 | mux.Handle(path, handler) 44 | log.Printf("Handling connect service at prefix: %v", path) 45 | 46 | reflector := grpcreflect.NewStaticReflector( 47 | relationv1connect.RelationServiceName, 48 | ) 49 | 50 | mux.Handle(grpcreflect.NewHandlerV1(reflector)) 51 | mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector)) 52 | 53 | log.Printf("Listening on: %v", addr) 54 | err := http.ListenAndServe( 55 | addr, 56 | h2c.NewHandler(mux, &http2.Server{}), 57 | ) 58 | 59 | if err != http.ErrServerClosed { 60 | log.Printf("Error running or stopping: %v", err) 61 | } 62 | } 63 | 64 | type RelationService struct { 65 | starshipClient starshipv1connect.StarshipServiceClient 66 | } 67 | 68 | var _ (relationv1connect.RelationServiceHandler) = (*RelationService)(nil) 69 | 70 | func (s *RelationService) GetFilmStarships( 71 | ctx context.Context, 72 | req *connect.Request[relationv1.GetFilmStarshipsRequest], 73 | ) ( 74 | *connect.Response[relationv1.GetFilmStarshipsResponse], 75 | error, 76 | ) { 77 | resp := relationv1.GetFilmStarshipsResponse{} 78 | 79 | for _, film := range req.Msg.Bases { 80 | starships, err := s.starshipClient.GetStarships(ctx, connect.NewRequest(&starshipv1.StarshipsRequest{ 81 | StarshipIds: film.StarshipIds, 82 | })) 83 | 84 | if err != nil { 85 | log.Printf("Request to starship service with %v failed: %v", film.StarshipIds, err) 86 | return nil, err 87 | } else { 88 | resp.Values = append(resp.Values, &relationv1.GetFilmStarshipsResponse_Result{ 89 | Starships: starships.Msg.Starships, 90 | }) 91 | } 92 | } 93 | 94 | return connect.NewResponse(&resp), nil 95 | } 96 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/gen/buf/starwars/film/v1/filmv1connect/film.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/film/v1/film.proto 4 | 5 | package filmv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/film/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // FilmServiceName is the fully-qualified name of the FilmService service. 25 | FilmServiceName = "buf.starwars.film.v1.FilmService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // FilmServiceGetFilmsProcedure is the fully-qualified name of the FilmService's GetFilms RPC. 37 | FilmServiceGetFilmsProcedure = "/buf.starwars.film.v1.FilmService/GetFilms" 38 | ) 39 | 40 | // FilmServiceClient is a client for the buf.starwars.film.v1.FilmService service. 41 | type FilmServiceClient interface { 42 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 43 | } 44 | 45 | // NewFilmServiceClient constructs a client for the buf.starwars.film.v1.FilmService service. By 46 | // default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, 47 | // and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the 48 | // connect.WithGRPC() or connect.WithGRPCWeb() options. 49 | // 50 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 51 | // http://api.acme.com or https://acme.com/grpc). 52 | func NewFilmServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) FilmServiceClient { 53 | baseURL = strings.TrimRight(baseURL, "/") 54 | return &filmServiceClient{ 55 | getFilms: connect_go.NewClient[v1.FilmsRequest, v1.FilmsResponse]( 56 | httpClient, 57 | baseURL+FilmServiceGetFilmsProcedure, 58 | opts..., 59 | ), 60 | } 61 | } 62 | 63 | // filmServiceClient implements FilmServiceClient. 64 | type filmServiceClient struct { 65 | getFilms *connect_go.Client[v1.FilmsRequest, v1.FilmsResponse] 66 | } 67 | 68 | // GetFilms calls buf.starwars.film.v1.FilmService.GetFilms. 69 | func (c *filmServiceClient) GetFilms(ctx context.Context, req *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 70 | return c.getFilms.CallUnary(ctx, req) 71 | } 72 | 73 | // FilmServiceHandler is an implementation of the buf.starwars.film.v1.FilmService service. 74 | type FilmServiceHandler interface { 75 | GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) 76 | } 77 | 78 | // NewFilmServiceHandler builds an HTTP handler from the service implementation. It returns the path 79 | // on which to mount the handler and the handler itself. 80 | // 81 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 82 | // and JSON codecs. They also support gzip compression. 83 | func NewFilmServiceHandler(svc FilmServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 84 | mux := http.NewServeMux() 85 | mux.Handle(FilmServiceGetFilmsProcedure, connect_go.NewUnaryHandler( 86 | FilmServiceGetFilmsProcedure, 87 | svc.GetFilms, 88 | opts..., 89 | )) 90 | return "/buf.starwars.film.v1.FilmService/", mux 91 | } 92 | 93 | // UnimplementedFilmServiceHandler returns CodeUnimplemented from all methods. 94 | type UnimplementedFilmServiceHandler struct{} 95 | 96 | func (UnimplementedFilmServiceHandler) GetFilms(context.Context, *connect_go.Request[v1.FilmsRequest]) (*connect_go.Response[v1.FilmsResponse], error) { 97 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.film.v1.FilmService.GetFilms is not implemented")) 98 | } 99 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/gen/buf/starwars/relation/v1/relationv1connect/relation.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/relation/v1/relation.proto 4 | 5 | package relationv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/relation/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion1_7_0 22 | 23 | const ( 24 | // RelationServiceName is the fully-qualified name of the RelationService service. 25 | RelationServiceName = "buf.starwars.relation.v1.RelationService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // RelationServiceGetFilmStarshipsProcedure is the fully-qualified name of the RelationService's 37 | // GetFilmStarships RPC. 38 | RelationServiceGetFilmStarshipsProcedure = "/buf.starwars.relation.v1.RelationService/GetFilmStarships" 39 | ) 40 | 41 | // RelationServiceClient is a client for the buf.starwars.relation.v1.RelationService service. 42 | type RelationServiceClient interface { 43 | GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) 44 | } 45 | 46 | // NewRelationServiceClient constructs a client for the buf.starwars.relation.v1.RelationService 47 | // service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for 48 | // gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply 49 | // the connect.WithGRPC() or connect.WithGRPCWeb() options. 50 | // 51 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 52 | // http://api.acme.com or https://acme.com/grpc). 53 | func NewRelationServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) RelationServiceClient { 54 | baseURL = strings.TrimRight(baseURL, "/") 55 | return &relationServiceClient{ 56 | getFilmStarships: connect_go.NewClient[v1.GetFilmStarshipsRequest, v1.GetFilmStarshipsResponse]( 57 | httpClient, 58 | baseURL+RelationServiceGetFilmStarshipsProcedure, 59 | connect_go.WithIdempotency(connect_go.IdempotencyNoSideEffects), 60 | connect_go.WithClientOptions(opts...), 61 | ), 62 | } 63 | } 64 | 65 | // relationServiceClient implements RelationServiceClient. 66 | type relationServiceClient struct { 67 | getFilmStarships *connect_go.Client[v1.GetFilmStarshipsRequest, v1.GetFilmStarshipsResponse] 68 | } 69 | 70 | // GetFilmStarships calls buf.starwars.relation.v1.RelationService.GetFilmStarships. 71 | func (c *relationServiceClient) GetFilmStarships(ctx context.Context, req *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) { 72 | return c.getFilmStarships.CallUnary(ctx, req) 73 | } 74 | 75 | // RelationServiceHandler is an implementation of the buf.starwars.relation.v1.RelationService 76 | // service. 77 | type RelationServiceHandler interface { 78 | GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) 79 | } 80 | 81 | // NewRelationServiceHandler builds an HTTP handler from the service implementation. It returns the 82 | // path on which to mount the handler and the handler itself. 83 | // 84 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 85 | // and JSON codecs. They also support gzip compression. 86 | func NewRelationServiceHandler(svc RelationServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 87 | mux := http.NewServeMux() 88 | mux.Handle(RelationServiceGetFilmStarshipsProcedure, connect_go.NewUnaryHandler( 89 | RelationServiceGetFilmStarshipsProcedure, 90 | svc.GetFilmStarships, 91 | connect_go.WithIdempotency(connect_go.IdempotencyNoSideEffects), 92 | connect_go.WithHandlerOptions(opts...), 93 | )) 94 | return "/buf.starwars.relation.v1.RelationService/", mux 95 | } 96 | 97 | // UnimplementedRelationServiceHandler returns CodeUnimplemented from all methods. 98 | type UnimplementedRelationServiceHandler struct{} 99 | 100 | func (UnimplementedRelationServiceHandler) GetFilmStarships(context.Context, *connect_go.Request[v1.GetFilmStarshipsRequest]) (*connect_go.Response[v1.GetFilmStarshipsResponse], error) { 101 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.relation.v1.RelationService.GetFilmStarships is not implemented")) 102 | } 103 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/gen/buf/starwars/starship/v1/starshipv1connect/starship.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/starship/v1/starship.proto 4 | 5 | package starshipv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go/gen/buf/starwars/starship/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // StarshipServiceName is the fully-qualified name of the StarshipService service. 25 | StarshipServiceName = "buf.starwars.starship.v1.StarshipService" 26 | ) 27 | 28 | // These constants are the fully-qualified names of the RPCs defined in this package. They're 29 | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. 30 | // 31 | // Note that these are different from the fully-qualified method names used by 32 | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to 33 | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a 34 | // period. 35 | const ( 36 | // StarshipServiceGetStarshipsProcedure is the fully-qualified name of the StarshipService's 37 | // GetStarships RPC. 38 | StarshipServiceGetStarshipsProcedure = "/buf.starwars.starship.v1.StarshipService/GetStarships" 39 | ) 40 | 41 | // StarshipServiceClient is a client for the buf.starwars.starship.v1.StarshipService service. 42 | type StarshipServiceClient interface { 43 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 44 | } 45 | 46 | // NewStarshipServiceClient constructs a client for the buf.starwars.starship.v1.StarshipService 47 | // service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for 48 | // gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply 49 | // the connect.WithGRPC() or connect.WithGRPCWeb() options. 50 | // 51 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 52 | // http://api.acme.com or https://acme.com/grpc). 53 | func NewStarshipServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) StarshipServiceClient { 54 | baseURL = strings.TrimRight(baseURL, "/") 55 | return &starshipServiceClient{ 56 | getStarships: connect_go.NewClient[v1.StarshipsRequest, v1.StarshipsResponse]( 57 | httpClient, 58 | baseURL+StarshipServiceGetStarshipsProcedure, 59 | opts..., 60 | ), 61 | } 62 | } 63 | 64 | // starshipServiceClient implements StarshipServiceClient. 65 | type starshipServiceClient struct { 66 | getStarships *connect_go.Client[v1.StarshipsRequest, v1.StarshipsResponse] 67 | } 68 | 69 | // GetStarships calls buf.starwars.starship.v1.StarshipService.GetStarships. 70 | func (c *starshipServiceClient) GetStarships(ctx context.Context, req *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 71 | return c.getStarships.CallUnary(ctx, req) 72 | } 73 | 74 | // StarshipServiceHandler is an implementation of the buf.starwars.starship.v1.StarshipService 75 | // service. 76 | type StarshipServiceHandler interface { 77 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 78 | } 79 | 80 | // NewStarshipServiceHandler builds an HTTP handler from the service implementation. It returns the 81 | // path on which to mount the handler and the handler itself. 82 | // 83 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 84 | // and JSON codecs. They also support gzip compression. 85 | func NewStarshipServiceHandler(svc StarshipServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 86 | mux := http.NewServeMux() 87 | mux.Handle(StarshipServiceGetStarshipsProcedure, connect_go.NewUnaryHandler( 88 | StarshipServiceGetStarshipsProcedure, 89 | svc.GetStarships, 90 | opts..., 91 | )) 92 | return "/buf.starwars.starship.v1.StarshipService/", mux 93 | } 94 | 95 | // UnimplementedStarshipServiceHandler returns CodeUnimplemented from all methods. 96 | type UnimplementedStarshipServiceHandler struct{} 97 | 98 | func (UnimplementedStarshipServiceHandler) GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 99 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.starship.v1.StarshipService.GetStarships is not implemented")) 100 | } 101 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bufbuild/knit/tutorial/starwars-knit-relation-service-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1 7 | github.com/bufbuild/connect-go v1.7.0 8 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364 9 | golang.org/x/net v0.38.0 10 | google.golang.org/protobuf v1.33.0 11 | ) 12 | 13 | require golang.org/x/text v0.23.0 // indirect 14 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/go.sum: -------------------------------------------------------------------------------- 1 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1 h1:XBygYORdxM6ImtYu7NsJd94W2k5eYZAqUiOaly4jdyE= 2 | buf.build/gen/go/bufbuild/knit/protocolbuffers/go v1.30.0-20230504140941-3dc602456973.1/go.mod h1:puJ8m7+AgZdN63sAJ02dpgbuhoFQN1URhTWULCBnALs= 3 | github.com/bufbuild/connect-go v1.7.0 h1:MGp82v7SCza+3RhsVhV7aMikwxvI3ZfD72YiGt8FYJo= 4 | github.com/bufbuild/connect-go v1.7.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk= 5 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364 h1:qiRIhoFC0QibU5hUHZNVM0Rgowsv4Kw6S1te+Q7WLPg= 6 | github.com/bufbuild/connect-grpcreflect-go v1.0.1-0.20230317120624-8e24e9604364/go.mod h1:57KmO9HZ/NoysS7mdbbCzUq/y6ogUFIZv034OHUR1as= 7 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 8 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 10 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 11 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 12 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 13 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 14 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 15 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 16 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 17 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 18 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 19 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 20 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: bufbuild 6 | repository: knit 7 | commit: 3dc602456973471584684e4878f99b10 8 | digest: shake256:5909694f816b2ebb5a502fadcb722a3cb73c217c4e118508c41219009b91d2b2b484fd6b5d9eb5b972f1fe9244ab24783a191d859390f9e6e36ee5a6c2479823 9 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT 8 | deps: 9 | - buf.build/bufbuild/knit 10 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/proto/buf/starwars/film/v1/film.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.film.v1; 4 | 5 | message Film { 6 | string film_id = 1; 7 | uint64 episode_number = 2; 8 | string title = 3; 9 | string opening_text = 4; 10 | repeated string directors = 5; 11 | repeated string producers = 6; 12 | repeated string character_ids = 7; 13 | repeated string planet_ids = 8; 14 | repeated string starship_ids = 9; 15 | repeated string vehicle_ids = 10; 16 | repeated string species_ids = 11; 17 | } 18 | 19 | message FilmsRequest { 20 | repeated string film_ids = 1; 21 | uint32 limit = 2; 22 | } 23 | 24 | message FilmsResponse { 25 | repeated Film films = 1; 26 | } 27 | 28 | service FilmService { 29 | rpc GetFilms(FilmsRequest) returns (FilmsResponse); 30 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/proto/buf/starwars/relation/v1/relation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "buf/starwars/film/v1/film.proto"; 4 | import "buf/starwars/starship/v1/starship.proto"; 5 | import "buf/knit/v1alpha1/options.proto"; 6 | 7 | package buf.starwars.relation.v1; 8 | 9 | message GetFilmStarshipsRequest { 10 | repeated buf.starwars.film.v1.Film bases = 1; 11 | } 12 | 13 | message GetFilmStarshipsResponse { 14 | repeated Result values = 1; 15 | message Result { 16 | repeated buf.starwars.starship.v1.Starship starships = 1; 17 | } 18 | } 19 | 20 | service RelationService { 21 | rpc GetFilmStarships(GetFilmStarshipsRequest) returns (GetFilmStarshipsResponse) { 22 | option (buf.knit.v1alpha1.relation).name = "starships"; 23 | option idempotency_level = NO_SIDE_EFFECTS; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-go/proto/buf/starwars/starship/v1/starship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.starship.v1; 4 | 5 | message Starship { 6 | string starship_id = 1; 7 | string name = 2; 8 | string model = 3; 9 | string manufacturer = 4; 10 | uint64 cost_in_credits = 5; 11 | uint64 length = 6; 12 | uint64 max_atmosphering_speed = 7; 13 | uint64 crew = 8; 14 | uint64 passengers = 9; 15 | uint64 cargo_capacity = 10; 16 | string consumables = 11; 17 | float hyperdrive_rating = 12; 18 | uint64 mglt = 13; 19 | string starship_class = 14; 20 | // Relations 21 | repeated string pilot_ids = 15; 22 | repeated string film_ids = 16; 23 | } 24 | 25 | message StarshipsRequest { 26 | repeated string starship_ids = 1; 27 | uint32 limit = 2; 28 | } 29 | 30 | message StarshipsResponse { 31 | repeated Starship starships = 1; 32 | } 33 | 34 | service StarshipService { 35 | rpc GetStarships(StarshipsRequest) returns (StarshipsResponse); 36 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/README.md: -------------------------------------------------------------------------------- 1 | # 🧶 Knit Relation Service in TypeScript 2 | 3 | [Back to top of Tutorial] 4 | 5 | In this tutorial the Knit relation service is implemented using [connect-es], 6 | and is made to listen on address `http://localhost:18000`. Look at the process 7 | diagram below to see where the Knit relation service fits into the bigger 8 | picture. 9 | 10 | ```mermaid 11 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 12 | flowchart LR 13 | A[Knit Client] --> B[Knit Gateway] 14 | subgraph r [Knit Relation Service] 15 | R{{Relation RPCs}} 16 | end 17 | subgraph f [Film Service] 18 | F{{Film RPCs}} 19 | end 20 | subgraph s [Starship Service] 21 | S{{Starship RPCs}} 22 | end 23 | B --> R 24 | B --> F 25 | B --> S 26 | style r stroke:#000,stroke-width:3px 27 | style R stroke:#000,stroke-width:3px 28 | ``` 29 | 30 | ## How to run the code 31 | To run the Knit relation service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 32 | then execute the following from the base of the repository (the other services must be running too). 33 | 34 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 35 | ``` 36 | cd tutorial/starwars-knit-relation-service-ts 37 | 38 | npm install 39 | npx tsx relationservice.ts 40 | 41 | # Output 42 | Knit relation service starting 43 | Listening on: 127.0.0.1:18000 44 | ``` 45 | 46 | ## Relations between services 47 | An explanation of how relations between services and their entities are 48 | defined is available in the [Top level Knit README]. 49 | 50 | [Top level Knit README]: https://github.com/bufbuild/knit#relations-between-services 51 | [Back to top of Tutorial]: /tutorial 52 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 53 | [connect-es]: https://github.com/bufbuild/connect-es 54 | [badges_slack]: https://buf.build/links/slack -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | plugins: 5 | - plugin: es 6 | opt: target=ts 7 | out: gen 8 | - plugin: connect-es 9 | opt: target=ts 10 | out: gen 11 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/film/v1/film_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { FilmsRequest, FilmsResponse } from "./film_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.film.v1.FilmService 11 | */ 12 | export const FilmService = { 13 | typeName: "buf.starwars.film.v1.FilmService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.film.v1.FilmService.GetFilms 17 | */ 18 | getFilms: { 19 | name: "GetFilms", 20 | I: FilmsRequest, 21 | O: FilmsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/film/v1/film_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/film/v1/film.proto (package buf.starwars.film.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.film.v1.Film 11 | */ 12 | export class Film extends Message { 13 | /** 14 | * @generated from field: string film_id = 1; 15 | */ 16 | filmId = ""; 17 | 18 | /** 19 | * @generated from field: uint64 episode_number = 2; 20 | */ 21 | episodeNumber = protoInt64.zero; 22 | 23 | /** 24 | * @generated from field: string title = 3; 25 | */ 26 | title = ""; 27 | 28 | /** 29 | * @generated from field: string opening_text = 4; 30 | */ 31 | openingText = ""; 32 | 33 | /** 34 | * @generated from field: repeated string directors = 5; 35 | */ 36 | directors: string[] = []; 37 | 38 | /** 39 | * @generated from field: repeated string producers = 6; 40 | */ 41 | producers: string[] = []; 42 | 43 | /** 44 | * @generated from field: repeated string character_ids = 7; 45 | */ 46 | characterIds: string[] = []; 47 | 48 | /** 49 | * @generated from field: repeated string planet_ids = 8; 50 | */ 51 | planetIds: string[] = []; 52 | 53 | /** 54 | * @generated from field: repeated string starship_ids = 9; 55 | */ 56 | starshipIds: string[] = []; 57 | 58 | /** 59 | * @generated from field: repeated string vehicle_ids = 10; 60 | */ 61 | vehicleIds: string[] = []; 62 | 63 | /** 64 | * @generated from field: repeated string species_ids = 11; 65 | */ 66 | speciesIds: string[] = []; 67 | 68 | constructor(data?: PartialMessage) { 69 | super(); 70 | proto3.util.initPartial(data, this); 71 | } 72 | 73 | static readonly runtime: typeof proto3 = proto3; 74 | static readonly typeName = "buf.starwars.film.v1.Film"; 75 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 76 | { no: 1, name: "film_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 77 | { no: 2, name: "episode_number", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 78 | { no: 3, name: "title", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 79 | { no: 4, name: "opening_text", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 80 | { no: 5, name: "directors", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 81 | { no: 6, name: "producers", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 82 | { no: 7, name: "character_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 83 | { no: 8, name: "planet_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 84 | { no: 9, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 85 | { no: 10, name: "vehicle_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 86 | { no: 11, name: "species_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 87 | ]); 88 | 89 | static fromBinary(bytes: Uint8Array, options?: Partial): Film { 90 | return new Film().fromBinary(bytes, options); 91 | } 92 | 93 | static fromJson(jsonValue: JsonValue, options?: Partial): Film { 94 | return new Film().fromJson(jsonValue, options); 95 | } 96 | 97 | static fromJsonString(jsonString: string, options?: Partial): Film { 98 | return new Film().fromJsonString(jsonString, options); 99 | } 100 | 101 | static equals(a: Film | PlainMessage | undefined, b: Film | PlainMessage | undefined): boolean { 102 | return proto3.util.equals(Film, a, b); 103 | } 104 | } 105 | 106 | /** 107 | * @generated from message buf.starwars.film.v1.FilmsRequest 108 | */ 109 | export class FilmsRequest extends Message { 110 | /** 111 | * @generated from field: repeated string film_ids = 1; 112 | */ 113 | filmIds: string[] = []; 114 | 115 | /** 116 | * @generated from field: uint32 limit = 2; 117 | */ 118 | limit = 0; 119 | 120 | constructor(data?: PartialMessage) { 121 | super(); 122 | proto3.util.initPartial(data, this); 123 | } 124 | 125 | static readonly runtime: typeof proto3 = proto3; 126 | static readonly typeName = "buf.starwars.film.v1.FilmsRequest"; 127 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 128 | { no: 1, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 129 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 130 | ]); 131 | 132 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsRequest { 133 | return new FilmsRequest().fromBinary(bytes, options); 134 | } 135 | 136 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsRequest { 137 | return new FilmsRequest().fromJson(jsonValue, options); 138 | } 139 | 140 | static fromJsonString(jsonString: string, options?: Partial): FilmsRequest { 141 | return new FilmsRequest().fromJsonString(jsonString, options); 142 | } 143 | 144 | static equals(a: FilmsRequest | PlainMessage | undefined, b: FilmsRequest | PlainMessage | undefined): boolean { 145 | return proto3.util.equals(FilmsRequest, a, b); 146 | } 147 | } 148 | 149 | /** 150 | * @generated from message buf.starwars.film.v1.FilmsResponse 151 | */ 152 | export class FilmsResponse extends Message { 153 | /** 154 | * @generated from field: repeated buf.starwars.film.v1.Film films = 1; 155 | */ 156 | films: Film[] = []; 157 | 158 | constructor(data?: PartialMessage) { 159 | super(); 160 | proto3.util.initPartial(data, this); 161 | } 162 | 163 | static readonly runtime: typeof proto3 = proto3; 164 | static readonly typeName = "buf.starwars.film.v1.FilmsResponse"; 165 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 166 | { no: 1, name: "films", kind: "message", T: Film, repeated: true }, 167 | ]); 168 | 169 | static fromBinary(bytes: Uint8Array, options?: Partial): FilmsResponse { 170 | return new FilmsResponse().fromBinary(bytes, options); 171 | } 172 | 173 | static fromJson(jsonValue: JsonValue, options?: Partial): FilmsResponse { 174 | return new FilmsResponse().fromJson(jsonValue, options); 175 | } 176 | 177 | static fromJsonString(jsonString: string, options?: Partial): FilmsResponse { 178 | return new FilmsResponse().fromJsonString(jsonString, options); 179 | } 180 | 181 | static equals(a: FilmsResponse | PlainMessage | undefined, b: FilmsResponse | PlainMessage | undefined): boolean { 182 | return proto3.util.equals(FilmsResponse, a, b); 183 | } 184 | } 185 | 186 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/relation/v1/relation_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/relation/v1/relation.proto (package buf.starwars.relation.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { GetFilmStarshipsRequest, GetFilmStarshipsResponse } from "./relation_pb.js"; 7 | import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.relation.v1.RelationService 11 | */ 12 | export const RelationService = { 13 | typeName: "buf.starwars.relation.v1.RelationService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.relation.v1.RelationService.GetFilmStarships 17 | */ 18 | getFilmStarships: { 19 | name: "GetFilmStarships", 20 | I: GetFilmStarshipsRequest, 21 | O: GetFilmStarshipsResponse, 22 | kind: MethodKind.Unary, 23 | idempotency: MethodIdempotency.NoSideEffects, 24 | }, 25 | } 26 | } as const; 27 | 28 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/relation/v1/relation_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/relation/v1/relation.proto (package buf.starwars.relation.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3 } from "@bufbuild/protobuf"; 8 | import { Film } from "../../film/v1/film_pb.js"; 9 | import { Starship } from "../../starship/v1/starship_pb.js"; 10 | 11 | /** 12 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsRequest 13 | */ 14 | export class GetFilmStarshipsRequest extends Message { 15 | /** 16 | * @generated from field: repeated buf.starwars.film.v1.Film bases = 1; 17 | */ 18 | bases: Film[] = []; 19 | 20 | constructor(data?: PartialMessage) { 21 | super(); 22 | proto3.util.initPartial(data, this); 23 | } 24 | 25 | static readonly runtime: typeof proto3 = proto3; 26 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsRequest"; 27 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 28 | { no: 1, name: "bases", kind: "message", T: Film, repeated: true }, 29 | ]); 30 | 31 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsRequest { 32 | return new GetFilmStarshipsRequest().fromBinary(bytes, options); 33 | } 34 | 35 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsRequest { 36 | return new GetFilmStarshipsRequest().fromJson(jsonValue, options); 37 | } 38 | 39 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsRequest { 40 | return new GetFilmStarshipsRequest().fromJsonString(jsonString, options); 41 | } 42 | 43 | static equals(a: GetFilmStarshipsRequest | PlainMessage | undefined, b: GetFilmStarshipsRequest | PlainMessage | undefined): boolean { 44 | return proto3.util.equals(GetFilmStarshipsRequest, a, b); 45 | } 46 | } 47 | 48 | /** 49 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsResponse 50 | */ 51 | export class GetFilmStarshipsResponse extends Message { 52 | /** 53 | * @generated from field: repeated buf.starwars.relation.v1.GetFilmStarshipsResponse.Result values = 1; 54 | */ 55 | values: GetFilmStarshipsResponse_Result[] = []; 56 | 57 | constructor(data?: PartialMessage) { 58 | super(); 59 | proto3.util.initPartial(data, this); 60 | } 61 | 62 | static readonly runtime: typeof proto3 = proto3; 63 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsResponse"; 64 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 65 | { no: 1, name: "values", kind: "message", T: GetFilmStarshipsResponse_Result, repeated: true }, 66 | ]); 67 | 68 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsResponse { 69 | return new GetFilmStarshipsResponse().fromBinary(bytes, options); 70 | } 71 | 72 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsResponse { 73 | return new GetFilmStarshipsResponse().fromJson(jsonValue, options); 74 | } 75 | 76 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsResponse { 77 | return new GetFilmStarshipsResponse().fromJsonString(jsonString, options); 78 | } 79 | 80 | static equals(a: GetFilmStarshipsResponse | PlainMessage | undefined, b: GetFilmStarshipsResponse | PlainMessage | undefined): boolean { 81 | return proto3.util.equals(GetFilmStarshipsResponse, a, b); 82 | } 83 | } 84 | 85 | /** 86 | * @generated from message buf.starwars.relation.v1.GetFilmStarshipsResponse.Result 87 | */ 88 | export class GetFilmStarshipsResponse_Result extends Message { 89 | /** 90 | * @generated from field: repeated buf.starwars.starship.v1.Starship starships = 1; 91 | */ 92 | starships: Starship[] = []; 93 | 94 | constructor(data?: PartialMessage) { 95 | super(); 96 | proto3.util.initPartial(data, this); 97 | } 98 | 99 | static readonly runtime: typeof proto3 = proto3; 100 | static readonly typeName = "buf.starwars.relation.v1.GetFilmStarshipsResponse.Result"; 101 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 102 | { no: 1, name: "starships", kind: "message", T: Starship, repeated: true }, 103 | ]); 104 | 105 | static fromBinary(bytes: Uint8Array, options?: Partial): GetFilmStarshipsResponse_Result { 106 | return new GetFilmStarshipsResponse_Result().fromBinary(bytes, options); 107 | } 108 | 109 | static fromJson(jsonValue: JsonValue, options?: Partial): GetFilmStarshipsResponse_Result { 110 | return new GetFilmStarshipsResponse_Result().fromJson(jsonValue, options); 111 | } 112 | 113 | static fromJsonString(jsonString: string, options?: Partial): GetFilmStarshipsResponse_Result { 114 | return new GetFilmStarshipsResponse_Result().fromJsonString(jsonString, options); 115 | } 116 | 117 | static equals(a: GetFilmStarshipsResponse_Result | PlainMessage | undefined, b: GetFilmStarshipsResponse_Result | PlainMessage | undefined): boolean { 118 | return proto3.util.equals(GetFilmStarshipsResponse_Result, a, b); 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/starship/v1/starship_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { StarshipsRequest, StarshipsResponse } from "./starship_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.starship.v1.StarshipService 11 | */ 12 | export const StarshipService = { 13 | typeName: "buf.starwars.starship.v1.StarshipService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.starship.v1.StarshipService.GetStarships 17 | */ 18 | getStarships: { 19 | name: "GetStarships", 20 | I: StarshipsRequest, 21 | O: StarshipsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/gen/buf/starwars/starship/v1/starship_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.starship.v1.Starship 11 | */ 12 | export class Starship extends Message { 13 | /** 14 | * @generated from field: string starship_id = 1; 15 | */ 16 | starshipId = ""; 17 | 18 | /** 19 | * @generated from field: string name = 2; 20 | */ 21 | name = ""; 22 | 23 | /** 24 | * @generated from field: string model = 3; 25 | */ 26 | model = ""; 27 | 28 | /** 29 | * @generated from field: string manufacturer = 4; 30 | */ 31 | manufacturer = ""; 32 | 33 | /** 34 | * @generated from field: uint64 cost_in_credits = 5; 35 | */ 36 | costInCredits = protoInt64.zero; 37 | 38 | /** 39 | * @generated from field: uint64 length = 6; 40 | */ 41 | length = protoInt64.zero; 42 | 43 | /** 44 | * @generated from field: uint64 max_atmosphering_speed = 7; 45 | */ 46 | maxAtmospheringSpeed = protoInt64.zero; 47 | 48 | /** 49 | * @generated from field: uint64 crew = 8; 50 | */ 51 | crew = protoInt64.zero; 52 | 53 | /** 54 | * @generated from field: uint64 passengers = 9; 55 | */ 56 | passengers = protoInt64.zero; 57 | 58 | /** 59 | * @generated from field: uint64 cargo_capacity = 10; 60 | */ 61 | cargoCapacity = protoInt64.zero; 62 | 63 | /** 64 | * @generated from field: string consumables = 11; 65 | */ 66 | consumables = ""; 67 | 68 | /** 69 | * @generated from field: float hyperdrive_rating = 12; 70 | */ 71 | hyperdriveRating = 0; 72 | 73 | /** 74 | * @generated from field: uint64 mglt = 13; 75 | */ 76 | mglt = protoInt64.zero; 77 | 78 | /** 79 | * @generated from field: string starship_class = 14; 80 | */ 81 | starshipClass = ""; 82 | 83 | /** 84 | * Relations 85 | * 86 | * @generated from field: repeated string pilot_ids = 15; 87 | */ 88 | pilotIds: string[] = []; 89 | 90 | /** 91 | * @generated from field: repeated string film_ids = 16; 92 | */ 93 | filmIds: string[] = []; 94 | 95 | constructor(data?: PartialMessage) { 96 | super(); 97 | proto3.util.initPartial(data, this); 98 | } 99 | 100 | static readonly runtime: typeof proto3 = proto3; 101 | static readonly typeName = "buf.starwars.starship.v1.Starship"; 102 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 103 | { no: 1, name: "starship_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 104 | { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 105 | { no: 3, name: "model", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 106 | { no: 4, name: "manufacturer", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 107 | { no: 5, name: "cost_in_credits", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 108 | { no: 6, name: "length", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 109 | { no: 7, name: "max_atmosphering_speed", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 110 | { no: 8, name: "crew", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 111 | { no: 9, name: "passengers", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 112 | { no: 10, name: "cargo_capacity", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 113 | { no: 11, name: "consumables", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 114 | { no: 12, name: "hyperdrive_rating", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, 115 | { no: 13, name: "mglt", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 116 | { no: 14, name: "starship_class", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 117 | { no: 15, name: "pilot_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 118 | { no: 16, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 119 | ]); 120 | 121 | static fromBinary(bytes: Uint8Array, options?: Partial): Starship { 122 | return new Starship().fromBinary(bytes, options); 123 | } 124 | 125 | static fromJson(jsonValue: JsonValue, options?: Partial): Starship { 126 | return new Starship().fromJson(jsonValue, options); 127 | } 128 | 129 | static fromJsonString(jsonString: string, options?: Partial): Starship { 130 | return new Starship().fromJsonString(jsonString, options); 131 | } 132 | 133 | static equals(a: Starship | PlainMessage | undefined, b: Starship | PlainMessage | undefined): boolean { 134 | return proto3.util.equals(Starship, a, b); 135 | } 136 | } 137 | 138 | /** 139 | * @generated from message buf.starwars.starship.v1.StarshipsRequest 140 | */ 141 | export class StarshipsRequest extends Message { 142 | /** 143 | * @generated from field: repeated string starship_ids = 1; 144 | */ 145 | starshipIds: string[] = []; 146 | 147 | /** 148 | * @generated from field: uint32 limit = 2; 149 | */ 150 | limit = 0; 151 | 152 | constructor(data?: PartialMessage) { 153 | super(); 154 | proto3.util.initPartial(data, this); 155 | } 156 | 157 | static readonly runtime: typeof proto3 = proto3; 158 | static readonly typeName = "buf.starwars.starship.v1.StarshipsRequest"; 159 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 160 | { no: 1, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 161 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 162 | ]); 163 | 164 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsRequest { 165 | return new StarshipsRequest().fromBinary(bytes, options); 166 | } 167 | 168 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsRequest { 169 | return new StarshipsRequest().fromJson(jsonValue, options); 170 | } 171 | 172 | static fromJsonString(jsonString: string, options?: Partial): StarshipsRequest { 173 | return new StarshipsRequest().fromJsonString(jsonString, options); 174 | } 175 | 176 | static equals(a: StarshipsRequest | PlainMessage | undefined, b: StarshipsRequest | PlainMessage | undefined): boolean { 177 | return proto3.util.equals(StarshipsRequest, a, b); 178 | } 179 | } 180 | 181 | /** 182 | * @generated from message buf.starwars.starship.v1.StarshipsResponse 183 | */ 184 | export class StarshipsResponse extends Message { 185 | /** 186 | * @generated from field: repeated buf.starwars.starship.v1.Starship starships = 1; 187 | */ 188 | starships: Starship[] = []; 189 | 190 | constructor(data?: PartialMessage) { 191 | super(); 192 | proto3.util.initPartial(data, this); 193 | } 194 | 195 | static readonly runtime: typeof proto3 = proto3; 196 | static readonly typeName = "buf.starwars.starship.v1.StarshipsResponse"; 197 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 198 | { no: 1, name: "starships", kind: "message", T: Starship, repeated: true }, 199 | ]); 200 | 201 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsResponse { 202 | return new StarshipsResponse().fromBinary(bytes, options); 203 | } 204 | 205 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsResponse { 206 | return new StarshipsResponse().fromJson(jsonValue, options); 207 | } 208 | 209 | static fromJsonString(jsonString: string, options?: Partial): StarshipsResponse { 210 | return new StarshipsResponse().fromJsonString(jsonString, options); 211 | } 212 | 213 | static equals(a: StarshipsResponse | PlainMessage | undefined, b: StarshipsResponse | PlainMessage | undefined): boolean { 214 | return proto3.util.equals(StarshipsResponse, a, b); 215 | } 216 | } 217 | 218 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starwars-knit-relation-service-ts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@bufbuild/buf": "^1.26.1", 14 | "@bufbuild/protobuf": "^1.3.0", 15 | "@bufbuild/protoc-gen-es": "^1.3.0", 16 | "@connectrpc/connect": "^0.13.1", 17 | "@connectrpc/connect-fastify": "^0.13.1", 18 | "@connectrpc/connect-web": "^0.13.1", 19 | "@connectrpc/protoc-gen-connect-es": "^0.13.1", 20 | "fastify": "^4.29.0", 21 | "tsx": "^3.12.7", 22 | "typescript": "^5.0.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: bufbuild 6 | repository: knit 7 | commit: 3dc602456973471584684e4878f99b10 8 | digest: shake256:5909694f816b2ebb5a502fadcb722a3cb73c217c4e118508c41219009b91d2b2b484fd6b5d9eb5b972f1fe9244ab24783a191d859390f9e6e36ee5a6c2479823 9 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT 8 | deps: 9 | - buf.build/bufbuild/knit 10 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/proto/buf/starwars/film/v1/film.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.film.v1; 4 | 5 | message Film { 6 | string film_id = 1; 7 | uint64 episode_number = 2; 8 | string title = 3; 9 | string opening_text = 4; 10 | repeated string directors = 5; 11 | repeated string producers = 6; 12 | repeated string character_ids = 7; 13 | repeated string planet_ids = 8; 14 | repeated string starship_ids = 9; 15 | repeated string vehicle_ids = 10; 16 | repeated string species_ids = 11; 17 | } 18 | 19 | message FilmsRequest { 20 | repeated string film_ids = 1; 21 | uint32 limit = 2; 22 | } 23 | 24 | message FilmsResponse { 25 | repeated Film films = 1; 26 | } 27 | 28 | service FilmService { 29 | rpc GetFilms(FilmsRequest) returns (FilmsResponse); 30 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/proto/buf/starwars/relation/v1/relation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "buf/starwars/film/v1/film.proto"; 4 | import "buf/starwars/starship/v1/starship.proto"; 5 | import "buf/knit/v1alpha1/options.proto"; 6 | 7 | package buf.starwars.relation.v1; 8 | 9 | message GetFilmStarshipsRequest { 10 | repeated buf.starwars.film.v1.Film bases = 1; 11 | } 12 | 13 | message GetFilmStarshipsResponse { 14 | repeated Result values = 1; 15 | message Result { 16 | repeated buf.starwars.starship.v1.Starship starships = 1; 17 | } 18 | } 19 | 20 | service RelationService { 21 | rpc GetFilmStarships(GetFilmStarshipsRequest) returns (GetFilmStarshipsResponse) { 22 | option (buf.knit.v1alpha1.relation).name = "starships"; 23 | option idempotency_level = NO_SIDE_EFFECTS; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/proto/buf/starwars/starship/v1/starship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.starship.v1; 4 | 5 | message Starship { 6 | string starship_id = 1; 7 | string name = 2; 8 | string model = 3; 9 | string manufacturer = 4; 10 | uint64 cost_in_credits = 5; 11 | uint64 length = 6; 12 | uint64 max_atmosphering_speed = 7; 13 | uint64 crew = 8; 14 | uint64 passengers = 9; 15 | uint64 cargo_capacity = 10; 16 | string consumables = 11; 17 | float hyperdrive_rating = 12; 18 | uint64 mglt = 13; 19 | string starship_class = 14; 20 | // Relations 21 | repeated string pilot_ids = 15; 22 | repeated string film_ids = 16; 23 | } 24 | 25 | message StarshipsRequest { 26 | repeated string starship_ids = 1; 27 | uint32 limit = 2; 28 | } 29 | 30 | message StarshipsResponse { 31 | repeated Starship starships = 1; 32 | } 33 | 34 | service StarshipService { 35 | rpc GetStarships(StarshipsRequest) returns (StarshipsResponse); 36 | } -------------------------------------------------------------------------------- /tutorial/starwars-knit-relation-service-ts/relationservice.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import { createPromiseClient, ConnectRouter } from "@connectrpc/connect"; 16 | import { createConnectTransport } from "@connectrpc/connect-web"; 17 | import { StarshipService } from "./gen/buf/starwars/starship/v1/starship_connect"; 18 | import { StarshipsRequest } from "./gen/buf/starwars/starship/v1/starship_pb"; 19 | import { RelationService } from "./gen/buf/starwars/relation/v1/relation_connect"; 20 | import { GetFilmStarshipsRequest, GetFilmStarshipsResponse, GetFilmStarshipsResponse_Result } from "./gen/buf/starwars/relation/v1/relation_pb"; 21 | 22 | 23 | import { fastify } from "fastify"; 24 | import { fastifyConnectPlugin } from "@connectrpc/connect-fastify"; 25 | 26 | const starshipClient = createPromiseClient(StarshipService, createConnectTransport({ 27 | baseUrl: "http://127.0.0.1:18002", // URL of starship service 28 | })); 29 | 30 | function routes(router: ConnectRouter) { 31 | router.service(RelationService, { 32 | async getFilmStarships(req: GetFilmStarshipsRequest): Promise { 33 | const values = Array(); 34 | const promises = req.bases.map(async film => { 35 | const resp = await starshipClient.getStarships(new StarshipsRequest({ 36 | starshipIds: film.starshipIds, 37 | })) 38 | values.push(new GetFilmStarshipsResponse_Result({ 39 | starships: resp.starships, 40 | })); 41 | }); 42 | await Promise.allSettled(promises); 43 | return new GetFilmStarshipsResponse({ values }); 44 | } 45 | }) 46 | } 47 | 48 | async function main() { 49 | console.log("Knit relation service starting"); 50 | 51 | const server = fastify(); 52 | await server.register(fastifyConnectPlugin, { routes }) 53 | 54 | const host = "127.0.0.1"; 55 | const port = 18000; 56 | 57 | console.log(`Listening on: ${host}:${port}`); 58 | await server.listen({ host, port }); 59 | } 60 | 61 | void main(); 62 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/README.md: -------------------------------------------------------------------------------- 1 | # Star Wars Starship Service in Go 2 | 3 | [Back to top of Tutorial] 4 | 5 | The starship service is not strictly 🧶 Knit, it's just a simple gRPC service 6 | written using [connect-go], but it's needed for the other parts of the 7 | tutorial. Feel free to just run this service and go on to the Knit 8 | specific parts. The starship service is made to listen on address 9 | `http://localhost:18002`. Look at the process diagram below 10 | to see where the starship service fits into the bigger 11 | picture. 12 | 13 | ```mermaid 14 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 15 | flowchart LR 16 | A[Knit Client] --> B[Knit Gateway] 17 | subgraph r [Knit Relation Service] 18 | R{{Relation RPCs}} 19 | end 20 | subgraph f [Film Service] 21 | F{{Film RPCs}} 22 | end 23 | subgraph s [Starship Service] 24 | S{{Starship RPCs}} 25 | end 26 | B --> R 27 | B --> F 28 | B --> S 29 | style s stroke:#000,stroke-width:3px 30 | style S stroke:#000,stroke-width:3px 31 | ``` 32 | 33 | ## How to run the code 34 | To run the starship service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 35 | then execute the following from the base of the repository (the other services must be running too). 36 | 37 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 38 | ``` 39 | cd tutorial/starwars-starship-service-go/cmd/starshipservice 40 | 41 | go mod tidy 42 | go run starshipservice.go 43 | 44 | # Output 45 | 2023/05/01 11:32:57 Starship service starting 46 | 2023/05/01 11:32:57 Handling connect service at prefix: /buf.starwars.starship.v1.StarshipService/ 47 | 2023/05/01 11:32:57 Listening on: 127.0.0.1:18002 48 | ``` 49 | 50 | Open another terminal, and at the base directory do: 51 | ``` 52 | buf curl \ 53 | "http://localhost:18002/buf.starwars.starship.v1.StarshipService/GetStarships" \ 54 | --data '{"starshipIds":["2"]}' \ 55 | --http2-prior-knowledge \ 56 | --schema tutorial/starwars-starship-service-go/proto/buf/starwars/starship/v1/starship.proto 57 | ``` 58 | 59 | [Back to top of Tutorial]: /tutorial 60 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 61 | [connect-go]: https://github.com/bufbuild/connect-go 62 | [badges_slack]: https://buf.build/links/slack -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: github.com/bufbuild/knit/tutorial/starwars-starship-service-go/gen 6 | plugins: 7 | - remote: buf.build/protocolbuffers/plugins/go 8 | opt: paths=source_relative 9 | out: gen 10 | - remote: buf.build/bufbuild/plugins/connect-go 11 | opt: paths=source_relative 12 | out: gen -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/cmd/starshipservice/starshipservice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "net/http" 21 | 22 | "github.com/bufbuild/connect-go" 23 | grpcreflect "github.com/bufbuild/connect-grpcreflect-go" 24 | "github.com/bufbuild/knit/tutorial/starwars-data-go/pkg/starship" 25 | starshipv1 "github.com/bufbuild/knit/tutorial/starwars-starship-service-go/gen/buf/starwars/starship/v1" 26 | "github.com/bufbuild/knit/tutorial/starwars-starship-service-go/gen/buf/starwars/starship/v1/starshipv1connect" 27 | "golang.org/x/net/http2" 28 | "golang.org/x/net/http2/h2c" 29 | ) 30 | 31 | func main() { 32 | const addr = "127.0.0.1:18002" 33 | 34 | log.Printf("Starship service starting") 35 | 36 | mux := http.NewServeMux() 37 | 38 | path, handler := starshipv1connect.NewStarshipServiceHandler(&StarshipService{}) 39 | mux.Handle(path, handler) 40 | log.Printf("Handling connect service at prefix: %v", path) 41 | 42 | reflector := grpcreflect.NewStaticReflector( 43 | starshipv1connect.StarshipServiceName, 44 | ) 45 | 46 | mux.Handle(grpcreflect.NewHandlerV1(reflector)) 47 | mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector)) 48 | 49 | log.Printf("Listening on: %v", addr) 50 | err := http.ListenAndServe( 51 | addr, 52 | h2c.NewHandler(mux, &http2.Server{}), 53 | ) 54 | 55 | if err != http.ErrServerClosed { 56 | log.Printf("Error running or stopping: %v", err) 57 | } 58 | } 59 | 60 | type StarshipService struct{} 61 | 62 | var _ starshipv1connect.StarshipServiceHandler = (*StarshipService)(nil) 63 | 64 | func (s *StarshipService) GetStarships( 65 | ctx context.Context, 66 | req *connect.Request[starshipv1.StarshipsRequest], 67 | ) ( 68 | *connect.Response[starshipv1.StarshipsResponse], 69 | error, 70 | ) { 71 | 72 | resp := &starshipv1.StarshipsResponse{} 73 | for _, id := range req.Msg.StarshipIds { 74 | v := starship.Find(id) 75 | if v == nil { 76 | continue 77 | } 78 | resp.Starships = append(resp.Starships, &starshipv1.Starship{ 79 | StarshipId: v.StarshipID, 80 | Name: v.Name, 81 | Model: v.Model, 82 | Manufacturer: v.Manufacturer, 83 | CostInCredits: v.CostInCredits, 84 | Length: v.Length, 85 | MaxAtmospheringSpeed: v.MaxAtmospheringSpeed, 86 | Crew: v.Crew, 87 | Passengers: v.Passengers, 88 | CargoCapacity: v.CargoCapacity, 89 | Consumables: v.Consumables, 90 | HyperdriveRating: float32(v.HyperdriveRating), 91 | Mglt: v.MGLT, 92 | StarshipClass: v.StarshipClass, 93 | // Relations 94 | PilotIds: v.PilotIDs, 95 | FilmIds: v.FilmIDs, 96 | }) 97 | } 98 | 99 | return connect.NewResponse(resp), nil 100 | } 101 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/gen/buf/starwars/starship/v1/starshipv1connect/starship.connect.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-connect-go. DO NOT EDIT. 2 | // 3 | // Source: buf/starwars/starship/v1/starship.proto 4 | 5 | package starshipv1connect 6 | 7 | import ( 8 | context "context" 9 | errors "errors" 10 | connect_go "github.com/bufbuild/connect-go" 11 | v1 "github.com/bufbuild/knit/tutorial/starwars-starship-service-go/gen/buf/starwars/starship/v1" 12 | http "net/http" 13 | strings "strings" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file and the connect package are 17 | // compatible. If you get a compiler error that this constant is not defined, this code was 18 | // generated with a version of connect newer than the one compiled into your binary. You can fix the 19 | // problem by either regenerating this code with an older version of connect or updating the connect 20 | // version compiled into your binary. 21 | const _ = connect_go.IsAtLeastVersion0_1_0 22 | 23 | const ( 24 | // StarshipServiceName is the fully-qualified name of the StarshipService service. 25 | StarshipServiceName = "buf.starwars.starship.v1.StarshipService" 26 | ) 27 | 28 | // StarshipServiceClient is a client for the buf.starwars.starship.v1.StarshipService service. 29 | type StarshipServiceClient interface { 30 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 31 | } 32 | 33 | // NewStarshipServiceClient constructs a client for the buf.starwars.starship.v1.StarshipService 34 | // service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for 35 | // gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply 36 | // the connect.WithGRPC() or connect.WithGRPCWeb() options. 37 | // 38 | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, 39 | // http://api.acme.com or https://acme.com/grpc). 40 | func NewStarshipServiceClient(httpClient connect_go.HTTPClient, baseURL string, opts ...connect_go.ClientOption) StarshipServiceClient { 41 | baseURL = strings.TrimRight(baseURL, "/") 42 | return &starshipServiceClient{ 43 | getStarships: connect_go.NewClient[v1.StarshipsRequest, v1.StarshipsResponse]( 44 | httpClient, 45 | baseURL+"/buf.starwars.starship.v1.StarshipService/GetStarships", 46 | opts..., 47 | ), 48 | } 49 | } 50 | 51 | // starshipServiceClient implements StarshipServiceClient. 52 | type starshipServiceClient struct { 53 | getStarships *connect_go.Client[v1.StarshipsRequest, v1.StarshipsResponse] 54 | } 55 | 56 | // GetStarships calls buf.starwars.starship.v1.StarshipService.GetStarships. 57 | func (c *starshipServiceClient) GetStarships(ctx context.Context, req *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 58 | return c.getStarships.CallUnary(ctx, req) 59 | } 60 | 61 | // StarshipServiceHandler is an implementation of the buf.starwars.starship.v1.StarshipService 62 | // service. 63 | type StarshipServiceHandler interface { 64 | GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) 65 | } 66 | 67 | // NewStarshipServiceHandler builds an HTTP handler from the service implementation. It returns the 68 | // path on which to mount the handler and the handler itself. 69 | // 70 | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf 71 | // and JSON codecs. They also support gzip compression. 72 | func NewStarshipServiceHandler(svc StarshipServiceHandler, opts ...connect_go.HandlerOption) (string, http.Handler) { 73 | mux := http.NewServeMux() 74 | mux.Handle("/buf.starwars.starship.v1.StarshipService/GetStarships", connect_go.NewUnaryHandler( 75 | "/buf.starwars.starship.v1.StarshipService/GetStarships", 76 | svc.GetStarships, 77 | opts..., 78 | )) 79 | return "/buf.starwars.starship.v1.StarshipService/", mux 80 | } 81 | 82 | // UnimplementedStarshipServiceHandler returns CodeUnimplemented from all methods. 83 | type UnimplementedStarshipServiceHandler struct{} 84 | 85 | func (UnimplementedStarshipServiceHandler) GetStarships(context.Context, *connect_go.Request[v1.StarshipsRequest]) (*connect_go.Response[v1.StarshipsResponse], error) { 86 | return nil, connect_go.NewError(connect_go.CodeUnimplemented, errors.New("buf.starwars.starship.v1.StarshipService.GetStarships is not implemented")) 87 | } 88 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bufbuild/knit/tutorial/starwars-starship-service-go 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/bufbuild/connect-go v1.6.0 7 | github.com/bufbuild/connect-grpcreflect-go v1.0.0 8 | github.com/bufbuild/knit/tutorial/starwars-data-go v0.0.0-00010101000000-000000000000 9 | golang.org/x/net v0.39.0 10 | google.golang.org/protobuf v1.33.0 11 | ) 12 | 13 | replace github.com/bufbuild/knit/tutorial/starwars-data-go => ../starwars-data-go 14 | 15 | require golang.org/x/text v0.24.0 // indirect 16 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bufbuild/connect-go v1.6.0 h1:OCEB8JuEuvcY5lEKZCQE95CUscqkDtLnQceNhDgi92k= 2 | github.com/bufbuild/connect-go v1.6.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk= 3 | github.com/bufbuild/connect-grpcreflect-go v1.0.0 h1:zWsLFYqrT1O2sNJFYfTXI5WxbAyiY2dvevvnJHPtV5A= 4 | github.com/bufbuild/connect-grpcreflect-go v1.0.0/go.mod h1:825I20H8bfE9rLnBH/046JSpmm3uwpNYdG4duCARetc= 5 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 6 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 7 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= 8 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 9 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 10 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 11 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 12 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 13 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-go/proto/buf/starwars/starship/v1/starship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.starship.v1; 4 | 5 | message Starship { 6 | string starship_id = 1; 7 | string name = 2; 8 | string model = 3; 9 | string manufacturer = 4; 10 | uint64 cost_in_credits = 5; 11 | uint64 length = 6; 12 | uint64 max_atmosphering_speed = 7; 13 | uint64 crew = 8; 14 | uint64 passengers = 9; 15 | uint64 cargo_capacity = 10; 16 | string consumables = 11; 17 | float hyperdrive_rating = 12; 18 | uint64 mglt = 13; 19 | string starship_class = 14; 20 | // Relations 21 | repeated string pilot_ids = 15; 22 | repeated string film_ids = 16; 23 | } 24 | 25 | message StarshipsRequest { 26 | repeated string starship_ids = 1; 27 | uint32 limit = 2; 28 | } 29 | 30 | message StarshipsResponse { 31 | repeated Starship starships = 1; 32 | } 33 | 34 | service StarshipService { 35 | rpc GetStarships(StarshipsRequest) returns (StarshipsResponse); 36 | } -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/README.md: -------------------------------------------------------------------------------- 1 | # Star Wars Starship Service in TypeScript 2 | 3 | [Back to top of Tutorial] 4 | 5 | The starship service is not strictly 🧶 Knit, it's just a simple gRPC service 6 | written using [connect-es], but it's needed for the other parts of the 7 | tutorial. Feel free to just run this service and go on to the Knit 8 | specific parts. The starship service is made to listen on address 9 | `http://localhost:18002`. Look at the process diagram below 10 | to see where the starship service fits into the bigger 11 | picture. 12 | 13 | ```mermaid 14 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 15 | flowchart LR 16 | A[Knit Client] --> B[Knit Gateway] 17 | subgraph r [Knit Relation Service] 18 | R{{Relation RPCs}} 19 | end 20 | subgraph f [Film Service] 21 | F{{Film RPCs}} 22 | end 23 | subgraph s [Starship Service] 24 | S{{Starship RPCs}} 25 | end 26 | B --> R 27 | B --> F 28 | B --> S 29 | style s stroke:#000,stroke-width:3px 30 | style S stroke:#000,stroke-width:3px 31 | ``` 32 | 33 | ## How to run the code 34 | To run the starship service clone the repo using `git clone https://github.com/bufbuild/knit.git`, 35 | then execute the following from the base of the repository (the other services must be running too). 36 | 37 | [![Slack](https://img.shields.io/badge/If_you_need_help_talk_to_us_in_Slack-Buf-%23e01563)][badges_slack] 38 | ``` 39 | cd tutorial/starwars-starship-service-ts 40 | 41 | npm install 42 | npx tsx starshipservice.ts 43 | 44 | # Output 45 | Starship service starting 46 | Listening on: 127.0.0.1:18002 47 | ``` 48 | 49 | Open another terminal, and at the base directory do: 50 | ``` 51 | buf curl \ 52 | "http://localhost:18002/buf.starwars.starship.v1.StarshipService/GetStarships" \ 53 | --data '{"starshipIds":["2"]}' \ 54 | --http2-prior-knowledge \ 55 | --schema tutorial/starwars-starship-service-go/proto/buf/starwars/starship/v1/starship.proto 56 | ``` 57 | 58 | [Back to top of Tutorial]: /tutorial 59 | [github.com/bufbuild/knit]: https://github.com/bufbuild/knit 60 | [connect-es]: https://github.com/bufbuild/connect-es 61 | [badges_slack]: https://buf.build/links/slack -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | plugins: 5 | - plugin: es 6 | opt: target=ts 7 | out: gen 8 | - plugin: connect-es 9 | opt: target=ts 10 | out: gen -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/gen/buf/starwars/starship/v1/starship_connect.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-connect-es v0.8.6 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import { StarshipsRequest, StarshipsResponse } from "./starship_pb.js"; 7 | import { MethodKind } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from service buf.starwars.starship.v1.StarshipService 11 | */ 12 | export const StarshipService = { 13 | typeName: "buf.starwars.starship.v1.StarshipService", 14 | methods: { 15 | /** 16 | * @generated from rpc buf.starwars.starship.v1.StarshipService.GetStarships 17 | */ 18 | getStarships: { 19 | name: "GetStarships", 20 | I: StarshipsRequest, 21 | O: StarshipsResponse, 22 | kind: MethodKind.Unary, 23 | }, 24 | } 25 | } as const; 26 | 27 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/gen/buf/starwars/starship/v1/starship_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v1.2.0 with parameter "target=ts" 2 | // @generated from file buf/starwars/starship/v1/starship.proto (package buf.starwars.starship.v1, syntax proto3) 3 | /* eslint-disable */ 4 | // @ts-nocheck 5 | 6 | import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; 7 | import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * @generated from message buf.starwars.starship.v1.Starship 11 | */ 12 | export class Starship extends Message { 13 | /** 14 | * @generated from field: string starship_id = 1; 15 | */ 16 | starshipId = ""; 17 | 18 | /** 19 | * @generated from field: string name = 2; 20 | */ 21 | name = ""; 22 | 23 | /** 24 | * @generated from field: string model = 3; 25 | */ 26 | model = ""; 27 | 28 | /** 29 | * @generated from field: string manufacturer = 4; 30 | */ 31 | manufacturer = ""; 32 | 33 | /** 34 | * @generated from field: uint64 cost_in_credits = 5; 35 | */ 36 | costInCredits = protoInt64.zero; 37 | 38 | /** 39 | * @generated from field: uint64 length = 6; 40 | */ 41 | length = protoInt64.zero; 42 | 43 | /** 44 | * @generated from field: uint64 max_atmosphering_speed = 7; 45 | */ 46 | maxAtmospheringSpeed = protoInt64.zero; 47 | 48 | /** 49 | * @generated from field: uint64 crew = 8; 50 | */ 51 | crew = protoInt64.zero; 52 | 53 | /** 54 | * @generated from field: uint64 passengers = 9; 55 | */ 56 | passengers = protoInt64.zero; 57 | 58 | /** 59 | * @generated from field: uint64 cargo_capacity = 10; 60 | */ 61 | cargoCapacity = protoInt64.zero; 62 | 63 | /** 64 | * @generated from field: string consumables = 11; 65 | */ 66 | consumables = ""; 67 | 68 | /** 69 | * @generated from field: float hyperdrive_rating = 12; 70 | */ 71 | hyperdriveRating = 0; 72 | 73 | /** 74 | * @generated from field: uint64 mglt = 13; 75 | */ 76 | mglt = protoInt64.zero; 77 | 78 | /** 79 | * @generated from field: string starship_class = 14; 80 | */ 81 | starshipClass = ""; 82 | 83 | /** 84 | * Relations 85 | * 86 | * @generated from field: repeated string pilot_ids = 15; 87 | */ 88 | pilotIds: string[] = []; 89 | 90 | /** 91 | * @generated from field: repeated string film_ids = 16; 92 | */ 93 | filmIds: string[] = []; 94 | 95 | constructor(data?: PartialMessage) { 96 | super(); 97 | proto3.util.initPartial(data, this); 98 | } 99 | 100 | static readonly runtime: typeof proto3 = proto3; 101 | static readonly typeName = "buf.starwars.starship.v1.Starship"; 102 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 103 | { no: 1, name: "starship_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 104 | { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 105 | { no: 3, name: "model", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 106 | { no: 4, name: "manufacturer", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 107 | { no: 5, name: "cost_in_credits", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 108 | { no: 6, name: "length", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 109 | { no: 7, name: "max_atmosphering_speed", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 110 | { no: 8, name: "crew", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 111 | { no: 9, name: "passengers", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 112 | { no: 10, name: "cargo_capacity", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 113 | { no: 11, name: "consumables", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 114 | { no: 12, name: "hyperdrive_rating", kind: "scalar", T: 2 /* ScalarType.FLOAT */ }, 115 | { no: 13, name: "mglt", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, 116 | { no: 14, name: "starship_class", kind: "scalar", T: 9 /* ScalarType.STRING */ }, 117 | { no: 15, name: "pilot_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 118 | { no: 16, name: "film_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 119 | ]); 120 | 121 | static fromBinary(bytes: Uint8Array, options?: Partial): Starship { 122 | return new Starship().fromBinary(bytes, options); 123 | } 124 | 125 | static fromJson(jsonValue: JsonValue, options?: Partial): Starship { 126 | return new Starship().fromJson(jsonValue, options); 127 | } 128 | 129 | static fromJsonString(jsonString: string, options?: Partial): Starship { 130 | return new Starship().fromJsonString(jsonString, options); 131 | } 132 | 133 | static equals(a: Starship | PlainMessage | undefined, b: Starship | PlainMessage | undefined): boolean { 134 | return proto3.util.equals(Starship, a, b); 135 | } 136 | } 137 | 138 | /** 139 | * @generated from message buf.starwars.starship.v1.StarshipsRequest 140 | */ 141 | export class StarshipsRequest extends Message { 142 | /** 143 | * @generated from field: repeated string starship_ids = 1; 144 | */ 145 | starshipIds: string[] = []; 146 | 147 | /** 148 | * @generated from field: uint32 limit = 2; 149 | */ 150 | limit = 0; 151 | 152 | constructor(data?: PartialMessage) { 153 | super(); 154 | proto3.util.initPartial(data, this); 155 | } 156 | 157 | static readonly runtime: typeof proto3 = proto3; 158 | static readonly typeName = "buf.starwars.starship.v1.StarshipsRequest"; 159 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 160 | { no: 1, name: "starship_ids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, 161 | { no: 2, name: "limit", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, 162 | ]); 163 | 164 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsRequest { 165 | return new StarshipsRequest().fromBinary(bytes, options); 166 | } 167 | 168 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsRequest { 169 | return new StarshipsRequest().fromJson(jsonValue, options); 170 | } 171 | 172 | static fromJsonString(jsonString: string, options?: Partial): StarshipsRequest { 173 | return new StarshipsRequest().fromJsonString(jsonString, options); 174 | } 175 | 176 | static equals(a: StarshipsRequest | PlainMessage | undefined, b: StarshipsRequest | PlainMessage | undefined): boolean { 177 | return proto3.util.equals(StarshipsRequest, a, b); 178 | } 179 | } 180 | 181 | /** 182 | * @generated from message buf.starwars.starship.v1.StarshipsResponse 183 | */ 184 | export class StarshipsResponse extends Message { 185 | /** 186 | * @generated from field: repeated buf.starwars.starship.v1.Starship starships = 1; 187 | */ 188 | starships: Starship[] = []; 189 | 190 | constructor(data?: PartialMessage) { 191 | super(); 192 | proto3.util.initPartial(data, this); 193 | } 194 | 195 | static readonly runtime: typeof proto3 = proto3; 196 | static readonly typeName = "buf.starwars.starship.v1.StarshipsResponse"; 197 | static readonly fields: FieldList = proto3.util.newFieldList(() => [ 198 | { no: 1, name: "starships", kind: "message", T: Starship, repeated: true }, 199 | ]); 200 | 201 | static fromBinary(bytes: Uint8Array, options?: Partial): StarshipsResponse { 202 | return new StarshipsResponse().fromBinary(bytes, options); 203 | } 204 | 205 | static fromJson(jsonValue: JsonValue, options?: Partial): StarshipsResponse { 206 | return new StarshipsResponse().fromJson(jsonValue, options); 207 | } 208 | 209 | static fromJsonString(jsonString: string, options?: Partial): StarshipsResponse { 210 | return new StarshipsResponse().fromJsonString(jsonString, options); 211 | } 212 | 213 | static equals(a: StarshipsResponse | PlainMessage | undefined, b: StarshipsResponse | PlainMessage | undefined): boolean { 214 | return proto3.util.equals(StarshipsResponse, a, b); 215 | } 216 | } 217 | 218 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starwars-starship-service-ts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@bufbuild/buf": "^1.26.1", 14 | "@bufbuild/protobuf": "^1.3.0", 15 | "@bufbuild/protoc-gen-es": "^1.3.0", 16 | "@connectrpc/connect": "^0.13.1", 17 | "@connectrpc/connect-fastify": "^0.13.1", 18 | "@connectrpc/protoc-gen-connect-es": "^0.13.1", 19 | "fastify": "^4.29.0", 20 | "tsx": "^3.12.7", 21 | "typescript": "^5.0.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - DEFAULT -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/proto/buf/starwars/starship/v1/starship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package buf.starwars.starship.v1; 4 | 5 | message Starship { 6 | string starship_id = 1; 7 | string name = 2; 8 | string model = 3; 9 | string manufacturer = 4; 10 | uint64 cost_in_credits = 5; 11 | uint64 length = 6; 12 | uint64 max_atmosphering_speed = 7; 13 | uint64 crew = 8; 14 | uint64 passengers = 9; 15 | uint64 cargo_capacity = 10; 16 | string consumables = 11; 17 | float hyperdrive_rating = 12; 18 | uint64 mglt = 13; 19 | string starship_class = 14; 20 | // Relations 21 | repeated string pilot_ids = 15; 22 | repeated string film_ids = 16; 23 | } 24 | 25 | message StarshipsRequest { 26 | repeated string starship_ids = 1; 27 | uint32 limit = 2; 28 | } 29 | 30 | message StarshipsResponse { 31 | repeated Starship starships = 1; 32 | } 33 | 34 | service StarshipService { 35 | rpc GetStarships(StarshipsRequest) returns (StarshipsResponse); 36 | } -------------------------------------------------------------------------------- /tutorial/starwars-starship-service-ts/starshipservice.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Buf Technologies, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import { ConnectRouter } from "@connectrpc/connect"; 16 | import { StarshipService } from "./gen/buf/starwars/starship/v1/starship_connect"; 17 | import { Starship, StarshipsRequest, StarshipsResponse } from "./gen/buf/starwars/starship/v1/starship_pb"; 18 | import { findStarship } from "../starwars-data-ts/starship/starship"; 19 | 20 | import { fastify } from "fastify"; 21 | import { fastifyConnectPlugin } from "@connectrpc/connect-fastify"; 22 | 23 | function routes(router: ConnectRouter) { 24 | router.service(StarshipService, { 25 | async getStarships(req: StarshipsRequest): Promise { 26 | console.log(`Getting starships: ${req.starshipIds}`) 27 | const starships = new Array(); 28 | req.starshipIds.forEach(id => { 29 | const starship = findStarship(id); 30 | if (starship) { 31 | starships.push(new Starship({ 32 | starshipId: starship.starshipId, 33 | name: starship.name, 34 | model: starship.model, 35 | manufacturer: starship.manufacturer, 36 | costInCredits: BigInt(starship.costInCredits), 37 | length: BigInt(starship.length), 38 | maxAtmospheringSpeed: BigInt(starship.maxAtmospheringSpeed), 39 | crew: BigInt(starship.crew), 40 | passengers: BigInt(starship.passengers), 41 | cargoCapacity: BigInt(starship.cargoCapacity), 42 | consumables: starship.consumables, 43 | hyperdriveRating: starship.hyperdriveRating, 44 | mglt: BigInt(starship.mglt), 45 | starshipClass: starship.starshipClass, 46 | pilotIds: starship.pilotIds, 47 | filmIds: starship.filmIds, 48 | })); 49 | } 50 | }) 51 | return new StarshipsResponse({ starships }); 52 | } 53 | }); 54 | } 55 | 56 | async function main() { 57 | console.log("Starship service starting"); 58 | 59 | const server = fastify(); 60 | await server.register(fastifyConnectPlugin, { routes }) 61 | 62 | const host = "127.0.0.1"; 63 | const port = 18002; 64 | 65 | console.log(`Listening on: ${host}:${port}`); 66 | await server.listen({ host, port }); 67 | } 68 | 69 | void main(); 70 | --------------------------------------------------------------------------------