11# Getting Started
22
3- ## Installation
3+ Connect is a slim library for building HTTP APIs consumable anywhere, including browsers.
4+ You define your service with a Protocol Buffer schema, and Connect generates type-safe server
5+ and client code. Fill in your server's business logic and you're done — no hand-written
6+ marshaling, routing, or client code required!
47
5- ### Basic Client
8+ This fifteen-minute walkthrough helps you create a small Connect service in Python.
9+ It demonstrates what you'll be writing by hand, what Connect generates for you,
10+ and how to call your new API.
611
7- For basic client functionality:
12+ ## Prerequisites
13+
14+ - [ uv] ( https://docs.astral.sh/uv/#installation ) installed. Any package manager including pip can also be used.
15+ - [ The Buf CLI] ( https://buf.build/docs/installation ) installed, and include it in the ` $PATH ` .
16+ - We'll also use [ cURL] ( https://curl.se/ ) . It's available from Homebrew and most Linux package managers.
17+
18+ ## Setup python environment
19+
20+ First, we'll setup the python environment and dependencies.
21+
22+ === "ASGI"
23+
24+ ```bash
25+ uv init
26+ uv add connect-python uvicorn
27+ ```
28+
29+ === "WSGI"
30+
31+ ```bash
32+ uv init
33+ uv add connect-python gunicorn
34+ ```
35+
36+ ## Define a service
37+
38+ Now we're ready to write the Protocol Buffer schema that defines our service. In your shell,
839
940``` bash
10- pip install connect-python
41+ mkdir -p proto/greet/v1
42+ touch proto/greet/v1/greet.proto
1143```
1244
13- ### Code Generation
45+ Open ` proto/greet/v1/greet.proto ` in your editor and add:
1446
15- For code generation additionally install the protoc plugin:
47+ ``` protobuf
48+ syntax = "proto3";
1649
17- ``` bash
18- pip install protoc-gen-connect-python
50+ package greet.v1;
51+
52+ message GreetRequest {
53+ string name = 1;
54+ }
55+
56+ message GreetResponse {
57+ string greeting = 1;
58+ }
59+
60+ service GreetService {
61+ rpc Greet(GreetRequest) returns (GreetResponse) {}
62+ }
1963```
2064
21- ## Code Generation
65+ This file declares the ` greet.v1 ` Protobuf package, a service called ` GreetService ` , and a single method
66+ called ` Greet ` with its request and response structures. These package, service, and method names will
67+ reappear soon in our HTTP API's URLs.
2268
23- With a protobuf definition in hand, you can generate a client. This is
24- easiest using buf, but you can also use protoc directly.
69+ ## Generate code
2570
26- Install the compiler (eg ` pip install protoc-gen-connect-python ` ), and
27- it can be referenced as ` protoc-gen-connect_python ` .
71+ We're going to generate our code using [ Buf] ( https://buf.build/ ) , a modern replacement for Google's protobuf compiler.
2872
29- ### Using Buf (Recommended)
73+ First, scaffold a basic [ buf.yaml] ( https://buf.build/docs/configuration/v2/buf-yaml ) by running ` buf config init ` .
74+ Then, edit ` buf.yaml ` to use our ` proto ` directory:
3075
31- A reasonable ` buf.gen.yaml ` :
76+ ``` yaml hl_lines="2 3"
77+ version : v2
78+ modules :
79+ - path : proto
80+ lint :
81+ use :
82+ - DEFAULT
83+ breaking :
84+ use :
85+ - FILE
86+ ` ` `
87+
88+ We will use [remote plugins](https://buf.build/docs/bsr/remote-plugins/usage), a feature of the
89+ [Buf Schema Registry](https://buf.build/docs/tutorials/getting-started-with-bsr) for generating code. Tell buf how to
90+ generate code by creating a buf.gen.yaml:
91+
92+ ` ` ` bash
93+ touch buf.gen.yaml
94+ ```
3295
3396``` yaml
3497version : v2
@@ -37,59 +100,157 @@ plugins:
37100 out : .
38101 - remote : buf.build/protocolbuffers/pyi
39102 out : .
40- - local : .venv/bin/protoc-gen-connect_python
103+ - remote : buf.build/connectrpc/python
41104 out : .
42105` ` `
43106
44- ### Using protoc
107+ With those configuration files in place, you can lint your schema and generate code:
45108
46109` ` ` bash
47- protoc --plugin=protoc-gen-connect-python=.venv/bin/protoc-gen-connect-python \
48- --connect-python_out=. \
49- --python_out=. \
50- --pyi_out=. \
51- your_service.proto
110+ buf lint
111+ buf generate
112+ ```
113+
114+ In the ` greet ` package, you should now see some generated Python:
115+
52116```
117+ greet
118+ └── v1
119+ ├── greet_connect.py
120+ └── greet_pb2.py
121+ └── greet_pb2.pyi
122+ ```
123+
124+ The package ` greet/v1 ` contains ` greet_pb2.py ` and ` greet_pb2.pyi ` which were generated by
125+ the [ protocolbuffers/python] ( https://buf.build/protocolbuffers/python ) and
126+ [ protocolbuffers/pyi] ( https://buf.build/protocolbuffers/pyi ) and contain ` GreetRequest `
127+ and ` GreetResponse ` structs and the associated marshaling code. ` greet_connect.py ` was
128+ generated by [ connectrpc/python] ( https://buf.build/connectrpc/python ) and contains the
129+ WSGI and ASGI service interfaces and client code to access a Connect server. Feel free to
130+ poke around if you're interested - ` greet_connect.py ` is standard Python code.
131+
132+ ## Implement service
53133
54- ## Example Service Definition
134+ The code we've generated takes care of the boring boilerplate, but we still need to implement our greeting logic.
135+ In the generated code, this is represented as the ` greet_connect.GreetService ` and ` greet_connect.GreetServiceSync `
136+ interfaces for async ASGI and sync WSGI servers respectively. Since the interface is so small, we can do everything
137+ in one Python file. ` touch server.py ` and add:
55138
56- If you have a proto definition like this:
139+ === "ASGI"
57140
58- ``` proto
59- service ElizaService {
60- rpc Say(SayRequest) returns (SayResponse) {}
61- rpc Converse(stream ConverseRequest) returns (stream ConverseResponse) {}
62- rpc Introduce(IntroduceRequest) returns (stream IntroduceResponse) {}
63- rpc Pontificate(stream PontificateRequest) returns (PontificateResponse) {}
141+ ```python
142+ from greet.v1.greet_connect import GreetService, GreetServiceASGIApplication
143+ from greet.v1.greet_pb2 import GreetResponse
144+
145+ class Greeter(GreetService):
146+ async def greet(self, request, ctx):
147+ print("Request headers: ", ctx.request_headers())
148+ response = GreetResponse(greeting=f"Hello, {request.name}!")
149+ ctx.response_headers()["greet-version"] = "v1"
150+ return response
151+
152+ app = GreetServiceASGIApplication(Greeter())
153+ ```
154+
155+ === "WSGI"
156+
157+ ```python
158+ from greet.v1.greet_connect import GreetServiceSync, GreetServiceWSGIApplication
159+ from greet.v1.greet_pb2 import GreetResponse
160+
161+ class Greeter(GreetServiceSync):
162+ def greet(self, request, ctx):
163+ print("Request headers: ", ctx.request_headers())
164+ response = GreetResponse(greeting=f"Hello, {request.name}!")
165+ ctx.response_headers()["greet-version"] = "v1"
166+ return response
167+
168+ app = GreetServiceWSGIApplication(Greeter())
169+ ```
170+
171+ In a separate terminal window, you can now start your server:
172+
173+ === "ASGI"
174+
175+ ```bash
176+ uv run uvicorn server:app
177+ ```
178+
179+ === "WSGI"
180+
181+ ```bash
182+ uv run gunicorn server:app
183+ ```
184+
185+ ## Make requests
186+
187+ The simplest way to consume your new API is an HTTP/1.1 POST with a JSON payload. If you have a recent version of
188+ cURL installed, it's a one-liner:
189+
190+ ``` bash
191+ curl \
192+ --header " Content-Type: application/json" \
193+ --data ' {"name": "Jane"}' \
194+ http://localhost:8000/greet.v1.GreetService/Greet
195+ ```
196+
197+ This responds:
198+
199+ ``` json
200+ {
201+ "greeting" : " Hello, Jane!"
64202}
65203```
66204
67- ## Generated Client
205+ We can also make requests using Connect's generated client. ` touch client.py ` and add:
206+
207+ === "Async"
208+
209+ ```python
210+ import asyncio
211+
212+ from greet.v1.greet_connect import GreetServiceClient
213+ from greet.v1.greet_pb2 import GreetRequest
68214
69- Then the generated client will have methods like this (optional arguments have been elided for clarity):
215+ async def main():
216+ client = GreetServiceClient("http://localhost:8000")
217+ res = await client.greet(GreetRequest(name="Jane"))
218+ print(res.greeting)
70219
71- ``` python
72- class ElizaServiceClient :
73- def __init__ (self , url : str ):
74- ...
220+ if __name__ == "__main__":
221+ asyncio.run(main())
222+ ```
75223
76- # Unary (no streams)
77- def say (self , req : eliza_pb2.SayRequest) -> eliza_pb2.SayResponse:
78- ...
224+ === "Sync"
79225
80- # Bidirectional (both sides stream)
81- def converse ( self , req : Iterator[eliza_pb2.ConverseRequest]) -> Iterator[eliza_pb2.SayResponse]:
82- ...
226+ ```python
227+ from greet.v1.greet_connect import GreetServiceClientSync
228+ from greet.v1.greet_pb2 import GreetRequest
83229
84- # Server streaming (client sends one message, server sends a stream)
85- def introduce (self , req : eliza_pb2.IntroduceRequest) -> Iterator[eliza_pb2.IntroduceResponse]:
86- ...
230+ def main():
231+ client = GreetServiceClientSync("http://localhost:8000")
232+ res = client.greet(GreetRequest(name="Jane"))
233+ print(res.greeting)
87234
88- # Client streaming (client sends a stream, server sends one message back)
89- def pontificate (self , req : Iterator[eliza_pb2.PontificateRequest]) -> eliza_pb2.PontificateResponse:
90- ...
235+ if __name__ == "__main__":
236+ main()
237+ ```
238+
239+ With your server still running in a separate terminal window, you can now run your client:
240+
241+ ``` bash
242+ uv run python client.py
91243```
92244
245+ Congratulations — you've built your first Connect service! 🎉
246+
247+ ## So what?
248+
249+ With just a few lines of hand-written code, you've built a real API server that supports both the and Connect protocol.
250+ Unlike a hand-written REST service, you didn't need to design a URL hierarchy, hand-write request and response structs,
251+ manage your own marshaling, or parse typed values out of query parameters. More importantly, your users got an idiomatic,
252+ type-safe client without any extra work on your part.
253+
93254## Next Steps
94255
95256- Learn about [ Usage] ( ./usage.md ) patterns
0 commit comments