Skip to content

Commit

Permalink
V1 API: Wrapping react-query (connectrpc#224)
Browse files Browse the repository at this point in the history
Introducing a whole new API for connect-query. This API ties itself more
tightly with the fantastic `@tanstack/react-query` in order to improve
developer experience and reduce bundle size.

At the core of this change is the new `MethodUnaryDescriptor` type,
which is essentially just a self contained description of a service
method. The new code generator just outputs one of these per method and
those are passed to the hooks/methods. This new additional type brings a
huge level of flexibility about how we write additional methods, and
it's trivial to write your own hooks/methods around these simple types.

## Example usage

```tsx
import { useQuery } from "@connectrpc/connect-react-query";
import { say } from "./gen/eliza-ElizaService_connectquery";

...

const { data } = useQuery(say);
```

## Reasoning

There are a number of reasons we've decided to make this major breaking
change.

1. The API surface is much smaller, leading to less confusion about how
to use each method.
2. Smaller core code size and more modular organization leads to better
tree shaking (since each generated method doesn't come along with a
generated hook).
3. Package names are now explicit about their dependencies, making it
easier to develop other packages based on these packages/generated code.
4. More tightly integrating with `@tanstack/react-query@5` provides
better usage of Suspense versions of the API.
5. New generated code is easier to expand and build your own custom
hooks for.

## Migration

The migration process is easy:

### Before

```tsx
import { getUserOrganization } from "@apigen/org/alpha/registry/v1alpha1/organization-OrganizationService_connectquery";
import { useQuery } from "@tanstack/react-query";

...

const getUserOrganizationQuery = useQuery({
    ...getUserOrganization.useQuery({
      userId: currentUser?.id,
      organizationId,
    }),
    useErrorBoundary: false,
    enabled: currentUser !== null,
});
```

### After

```tsx
import { getUserOrganization } from "@apigen/org/alpha/registry/v1alpha1/organization-OrganizationService_connectquery";
import { useQuery } from "@connectrpc/connect-react-query";

...

const getUserOrganizationQuery = useQuery(getUserOrganization, {
      userId: currentUser?.id,
      organizationId,
    },
    {
      useErrorBoundary: false,
      enabled: currentUser !== null
    }
});
```

Fixes connectrpc#220

---------

Co-authored-by: Timo Stamm <ts@timostamm.de>
  • Loading branch information
paul-sachs and timostamm authored Dec 7, 2023
1 parent 49308a2 commit a96c192
Show file tree
Hide file tree
Showing 52 changed files with 1,868 additions and 5,248 deletions.
487 changes: 167 additions & 320 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions assets/connect-query_dependency_graph.excalidraw
Original file line number Diff line number Diff line change
Expand Up @@ -1027,12 +1027,12 @@
"locked": false,
"fontSize": 20,
"fontFamily": 3,
"text": "Connect-Query",
"text": "Connect-React-Query",
"baseline": 19,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "NLMbxwdPMz1O8gu8ku7cT",
"originalText": "Connect-Query"
"originalText": "Connect-React-Query"
},
{
"type": "rectangle",
Expand Down
3 changes: 2 additions & 1 deletion cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"tsdoc",
"corepack",
"printables",
"arethetypeswrong"
"arethetypeswrong",
"typesafe"
],
"ignorePaths": ["**/*.svg", "**/*.ai", "**/pnpm-lock.yaml", "*.excalidraw"]
}
4 changes: 2 additions & 2 deletions examples/react/basic/buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
version: v1
plugins:
- name: es
path: ./node_modules/.bin/protoc-gen-es
path: protoc-gen-es
out: src/gen
opt:
- target=ts

- name: connect-query
path: ./node_modules/.bin/protoc-gen-connect-query
path: protoc-gen-connect-query
out: src/gen
opt:
- target=ts
Expand Down
14 changes: 7 additions & 7 deletions examples/react/basic/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@connectrpc/connect-query-example-react-basic",
"name": "@connectrpc/connect-query-example-basic",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -12,13 +12,13 @@
"format": "prettier . --write && eslint . --fix && license-header"
},
"dependencies": {
"@bufbuild/buf": "1.27.2",
"@bufbuild/protobuf": "^1.4.1",
"@bufbuild/protoc-gen-es": "^1.4.1",
"@connectrpc/connect": "^1.1.3",
"@bufbuild/buf": "1.28.1",
"@bufbuild/protobuf": "^1.5.1",
"@bufbuild/protoc-gen-es": "^1.5.1",
"@connectrpc/connect": "^1.1.4",
"@connectrpc/connect-query": "workspace:*",
"@connectrpc/connect-web": "^1.1.3",
"@connectrpc/protoc-gen-connect-es": "^1.1.3",
"@connectrpc/connect-web": "^1.1.4",
"@connectrpc/protoc-gen-connect-es": "^1.1.4",
"@connectrpc/protoc-gen-connect-query": "workspace:*",
"@tanstack/react-query": "^5.4.3",
"@tanstack/react-query-devtools": "^5.4.3",
Expand Down
8 changes: 3 additions & 5 deletions examples/react/basic/src/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { useQuery } from "@tanstack/react-query";
import { useQuery } from "@connectrpc/connect-query";
import type { FC } from "react";

import { Data, Datum } from "./datum";
Expand All @@ -21,12 +21,10 @@ import { Indicator, Indicators } from "./indicator";
import { Page } from "./page";

/**
* This example demonstrates a basic usage of Connect-Query with `useQuery`
* This example demonstrates a basic usage of Connect-React-Query with `useQuery`
*/
export const Example: FC = () => {
const { status, fetchStatus, error, data } = useQuery({
...say.useQuery({}),
});
const { status, fetchStatus, error, data } = useQuery(say);

return (
<Page>
Expand Down
43 changes: 11 additions & 32 deletions examples/react/basic/src/gen/eliza-BigIntService_connectquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */

import { CountRequest, CountResponse } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
import {
createQueryService,
createUnaryHooks,
UnaryFunctionsWithHooks,
} from "@connectrpc/connect-query";

export const typeName = "connectrpc.eliza.v1.BigIntService";
import { CountRequest, CountResponse } from "./eliza_pb";

/**
* @generated from service connectrpc.eliza.v1.BigIntService
* @generated from rpc connectrpc.eliza.v1.BigIntService.Count
*/
export const BigIntService = {
typeName: "connectrpc.eliza.v1.BigIntService",
methods: {
/**
* @generated from rpc connectrpc.eliza.v1.BigIntService.Count
*/
count: {
name: "Count",
I: CountRequest,
O: CountResponse,
kind: MethodKind.Unary,
},
export const count = {
localName: "count",
name: "Count",
kind: MethodKind.Unary,
I: CountRequest,
O: CountResponse,
service: {
typeName: "connectrpc.eliza.v1.BigIntService",
},
} as const;

const $queryService = createQueryService({ service: BigIntService });

/**
* @generated from rpc connectrpc.eliza.v1.BigIntService.Count
*/
export const count: UnaryFunctionsWithHooks<CountRequest, CountResponse> = {
...$queryService.count,
...createUnaryHooks($queryService.count),
};
111 changes: 22 additions & 89 deletions examples/react/basic/src/gen/eliza-ElizaService_connectquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,108 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */

import {
ConverseRequest,
ConverseResponse,
IntroduceRequest,
IntroduceResponse,
SayRequest,
SayResponse,
} from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
import {
createQueryService,
createUnaryHooks,
UnaryFunctionsWithHooks,
} from "@connectrpc/connect-query";

export const typeName = "connectrpc.eliza.v1.ElizaService";

/**
* ElizaService provides a way to talk to Eliza, a port of the DOCTOR script
* for Joseph Weizenbaum's original ELIZA program. Created in the mid-1960s at
* the MIT Artificial Intelligence Laboratory, ELIZA demonstrates the
* superficiality of human-computer communication. DOCTOR simulates a
* psychotherapist, and is commonly found as an Easter egg in emacs
* distributions.
*
* @generated from service connectrpc.eliza.v1.ElizaService
*/
export const ElizaService = {
typeName: "connectrpc.eliza.v1.ElizaService",
methods: {
/**
* Say is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.Say
*/
say: {
name: "Say",
I: SayRequest,
O: SayResponse,
kind: MethodKind.Unary,
},
/**
* SayAgain is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.SayAgain
*/
sayAgain: {
name: "SayAgain",
I: SayRequest,
O: SayResponse,
kind: MethodKind.Unary,
},
/**
* Converse is a bidirectional RPC. The caller may exchange multiple
* back-and-forth messages with Eliza over a long-lived connection. Eliza
* responds to each ConverseRequest with a ConverseResponse.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.Converse
*/
converse: {
name: "Converse",
I: ConverseRequest,
O: ConverseResponse,
kind: MethodKind.BiDiStreaming,
},
/**
* Introduce is a server streaming RPC. Given the caller's name, Eliza
* returns a stream of sentences to introduce itself.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.Introduce
*/
introduce: {
name: "Introduce",
I: IntroduceRequest,
O: IntroduceResponse,
kind: MethodKind.ServerStreaming,
},
},
} as const;

const $queryService = createQueryService({ service: ElizaService });
import { SayRequest, SayResponse } from "./eliza_pb";

/**
* Say is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.Say
*/
export const say: UnaryFunctionsWithHooks<SayRequest, SayResponse> = {
...$queryService.say,
...createUnaryHooks($queryService.say),
};
export const say = {
localName: "say",
name: "Say",
kind: MethodKind.Unary,
I: SayRequest,
O: SayResponse,
service: {
typeName: "connectrpc.eliza.v1.ElizaService",
},
} as const;

/**
* SayAgain is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.SayAgain
*/
export const sayAgain: UnaryFunctionsWithHooks<SayRequest, SayResponse> = {
...$queryService.sayAgain,
...createUnaryHooks($queryService.sayAgain),
};
export const sayAgain = {
localName: "sayAgain",
name: "SayAgain",
kind: MethodKind.Unary,
I: SayRequest,
O: SayResponse,
service: {
typeName: "connectrpc.eliza.v1.ElizaService",
},
} as const;
43 changes: 11 additions & 32 deletions examples/react/basic/src/gen/eliza-Haberdasher_connectquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */

import { Nothing } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
import {
createQueryService,
createUnaryHooks,
UnaryFunctionsWithHooks,
} from "@connectrpc/connect-query";

export const typeName = "connectrpc.eliza.v1.Haberdasher";
import { Nothing } from "./eliza_pb";

/**
* @generated from service connectrpc.eliza.v1.Haberdasher
* @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
*/
export const Haberdasher = {
typeName: "connectrpc.eliza.v1.Haberdasher",
methods: {
/**
* @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
*/
work: {
name: "Work",
I: Nothing,
O: Nothing,
kind: MethodKind.Unary,
},
export const work = {
localName: "work",
name: "Work",
kind: MethodKind.Unary,
I: Nothing,
O: Nothing,
service: {
typeName: "connectrpc.eliza.v1.Haberdasher",
},
} as const;

const $queryService = createQueryService({ service: Haberdasher });

/**
* @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
*/
export const work: UnaryFunctionsWithHooks<Nothing, Nothing> = {
...$queryService.work,
...createUnaryHooks($queryService.work),
};
43 changes: 11 additions & 32 deletions examples/react/basic/src/gen/eliza-PaginatedService_connectquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */

import { ListRequest, ListResponse } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
import {
createQueryService,
createUnaryHooks,
UnaryFunctionsWithHooks,
} from "@connectrpc/connect-query";

export const typeName = "connectrpc.eliza.v1.PaginatedService";
import { ListRequest, ListResponse } from "./eliza_pb";

/**
* @generated from service connectrpc.eliza.v1.PaginatedService
* @generated from rpc connectrpc.eliza.v1.PaginatedService.List
*/
export const PaginatedService = {
typeName: "connectrpc.eliza.v1.PaginatedService",
methods: {
/**
* @generated from rpc connectrpc.eliza.v1.PaginatedService.List
*/
list: {
name: "List",
I: ListRequest,
O: ListResponse,
kind: MethodKind.Unary,
},
export const list = {
localName: "list",
name: "List",
kind: MethodKind.Unary,
I: ListRequest,
O: ListResponse,
service: {
typeName: "connectrpc.eliza.v1.PaginatedService",
},
} as const;

const $queryService = createQueryService({ service: PaginatedService });

/**
* @generated from rpc connectrpc.eliza.v1.PaginatedService.List
*/
export const list: UnaryFunctionsWithHooks<ListRequest, ListResponse> = {
...$queryService.list,
...createUnaryHooks($queryService.list),
};
Loading

0 comments on commit a96c192

Please sign in to comment.