Skip to content

Commit 4792b26

Browse files
authored
Revert "Revert "Add status code based error handling""
1 parent 0df783b commit 4792b26

60 files changed

Lines changed: 1945 additions & 771 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/bright-feet-behave.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@ts-rest/core': major
3+
'@ts-rest/express': major
4+
'@ts-rest/nest': major
5+
'@ts-rest/open-api': major
6+
'@ts-rest/react-query': major
7+
---
8+
9+
Change contract to support multiple responses, for different statuses
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@ts-rest/express': major
3+
'@ts-rest/core': patch
4+
'@ts-rest/nest': patch
5+
'@ts-rest/open-api': patch
6+
'@ts-rest/react-query': patch
7+
---
8+
9+
Add error handling support to express

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"cSpell.words": ["openapi", "tanstack", "TRPC", "ts-rest"],
2+
"cSpell.words": ["openapi", "tanstack", "todos", "TRPC", "ts-rest"],
33
"typescript.tsdk": "node_modules/typescript/lib",
44
"yaml.schemas": {
55
"https://json.schemastore.org/github-workflow.json": "file:///Users/olly/projects/ts-rest/.github/workflows/deploy.yml"

apps/docs/docs/comparisons/graphql-comparison.md

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
---
2-
sidebar_position: 2
3-
---
4-
51
# tRPC Comparison
62

73
I _love_ [tRPC](https://trpc.io/), [KATT (Alex Johansson)](https://github.com/KATT) and all the [other maintainers](https://github.com/trpc/trpc/graphs/contributors) have done some amazing work, and for applications with a single Next.js app, or an express server only consumed by TRPC clients, I whole heartily recommend using tRPC! Also I have undoubtedly taken inspiration from tRPC for tREST.
@@ -19,24 +15,3 @@ tREST allows you design an API as you would "normally", e.g. GET, POST, PUT, DEL
1915
tRPC structures your API as RPC calls such as `/trpc/getPosts` or `/trpc/getPostComments` etc, this provides an arguably simpler API for the client implementation, however, you loose the predictability of REST(ish) APIs if you have consumers who aren't in Typescript (able to us @ts-rest) or public consumers.
2016

2117
tRPC has many plugins to solve this issue by mapping the API implementation to a REST-like API, however, these approaches are often a bit clunky and reduce the safety of the system overall, tREST does this heavy lifting in the client and server implementations rather than requiring a second layer of abstraction and API endpoint(s) to be defined.
22-
23-
| **Features** | REST | tRPC | tREST |
24-
| ----------------- | ---- | ----- | ------ |
25-
| E2E Type Safe ||||
26-
| Protocol | REST | RPC | REST |
27-
| Public API ||||
28-
| Zod/Yup/Joi ||| 🏗 v1.0 |
29-
| WebSocket Support ||||
30-
| Cmd+Click Access || 🏗 v10 ||
31-
| Separate Contract ||||
32-
33-
tREST also supports [Nest](https://nestjs.com/), it appears adding Nest to tRPC is against the Nest controller principles, so it is not recommended.
34-
35-
| **Libraries Support** | REST | tRPC | tREST |
36-
| --------------------- | ---- | ----------- | ------ |
37-
| Client fetch/custom ||||
38-
| Client react-query ||| 🏗 v1.0 |
39-
| Client swr || ✅ (plugin) | 🏗 v1.0 |
40-
| Server Express ||||
41-
| Server Nest ||||
42-
| Server Next ||| 🏗 v1.0 |

apps/docs/docs/core/core.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Contract
2+
3+
Define a contract with the `@ts-rest/core` package, you may nest routers within a router, generally you'd want a router for each nested resource e.g. `/users/:id/posts` could have a nested router `contract.users.posts`, this path is what you'd use on the client to query the API.
4+
5+
Breaking down the contract to sub-routers also allows you to split up the backend implementation, for example in Nest.js you could have multiple controllers for the sub-routers.
6+
7+
```typescript
8+
const c = initContract();
9+
10+
export const contract = c.router({
11+
createPost: c.mutation({
12+
method: 'POST',
13+
path: () => '/posts',
14+
responses: {
15+
201: c.response<Post>(),
16+
},
17+
body: z.object({
18+
title: z.string(),
19+
content: z.string(),
20+
published: z.boolean().optional(),
21+
description: z.string().optional(),
22+
}),
23+
summary: 'Create a post',
24+
}),
25+
getPosts: c.query({
26+
method: 'GET',
27+
path: () => '/posts',
28+
responses: {
29+
200: c.response<{ posts: Post[]; total: number }>(),
30+
},
31+
query: z.object({
32+
take: z.string().transform(Number).optional(),
33+
skip: z.string().transform(Number).optional(),
34+
search: z.string().optional(),
35+
}),
36+
summary: 'Get all posts',
37+
}),
38+
});
39+
```
40+
41+
## Combining Contracts
42+
43+
You can combine contracts to create a single contract, helpful if you want many sub contracts, especially if they are huge.
44+
45+
```typescript
46+
const c = initContract();
47+
48+
export const postContract = c.router({
49+
getPosts: c.query({
50+
method: 'GET',
51+
path: () => '/posts',
52+
responses: {
53+
200: c.response<{ posts: Post[]; total: number }>(),
54+
},
55+
query: z.object({
56+
take: z.string().transform(Number).optional(),
57+
skip: z.string().transform(Number).optional(),
58+
search: z.string().optional(),
59+
}),
60+
summary: 'Get all posts',
61+
}),
62+
});
63+
64+
export const contract = c.router({
65+
posts: postContract,
66+
});
67+
```

apps/docs/docs/core/errors.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Error Handling
2+
3+
Since 2.0 ts-rest-api has a built-in error handling, all you need to do is define the response status codes in the contract. The Nest/Express libraries handle the rest for you, letting you utilise HTTP status codes fully without worrying about type safety!
4+
5+
```typescript
6+
export const routerBasic = c.router({
7+
updateUser: c.mutation({
8+
method: 'PATCH',
9+
path: ({ id }: { id: string }) => `/basic/users/${id}`,
10+
response: {
11+
200: c.response<User>(),
12+
400: c.response<{ message: string }>(),
13+
},
14+
body: c.body<{ name: string | null; email: string | null }>(),
15+
summary: 'Update a user',
16+
}),
17+
});
18+
```
19+
20+
## Client
21+
22+
The default fetch client has support for this,
23+
24+
```typescript
25+
const { status, data } = await client.user({ params: { id: '1' } });
26+
27+
if (status === 200) {
28+
console.log(data.email);
29+
} else if (status === 400) {
30+
console.log('Not found');
31+
} else {
32+
console.log('Something went wrong');
33+
}
34+
```
35+
36+
:::info
37+
38+
The typed response includes the typed status codes along with any other possible statuses
39+
40+
```typescript
41+
const updatedUser: {
42+
status: 200;
43+
data: User
44+
} | {
45+
status: 400;
46+
data: {
47+
message: string;
48+
}
49+
} | {
50+
status: 100 | 101 | 102 | 201 | 202 | 203 | ... 47 more ... | 511;
51+
data: unknown;
52+
}
53+
```
54+
55+
:::

apps/docs/docs/core/fetch.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Fetch Client
2+
3+
Connect to your tRPC instance
4+
5+
```typescript
6+
export const client = initClient(router, {
7+
baseUrl: 'http://localhost:3334',
8+
baseHeaders: {},
9+
});
10+
```
11+
12+
**Query** against the contract, a _query_ is a function that does a GET request to the api.
13+
14+
```typescript
15+
const { data } = await client.posts.get();
16+
```
17+
18+
**Mutate** against the contract, a _mutation_ is a function that does a POST, PUT, PATCH or DELETE request to the api.
19+
20+
```typescript
21+
const { data, status } = await client.posts.create({
22+
body: {
23+
title: 'My Post',
24+
content: 'This is my post',
25+
},
26+
});
27+
```
28+
29+
:::info
30+
Because we type status codes, to check if the request was successful, we can use the `status` property.
31+
32+
e.g.
33+
34+
```typescript
35+
if (status === 200) {
36+
console.log('Success');
37+
} else {
38+
console.log('Something went wrong');
39+
}
40+
```
41+
42+
Return type
43+
44+
```typescript
45+
const data: {
46+
status: 200;
47+
data: User
48+
} | {
49+
status: 400 | 100 | 101 | 102 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 300 | 301 | 302 | 303 | 304 | 305 | 307 | ... 36 more ... | 511;
50+
data: unknown;
51+
}
52+
```
53+
54+
:::

apps/docs/docs/examples.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Examples
2+
3+
## Blog
4+
5+
Full fledged CRUD example and can be found here [blog example](https://github.com/ts-rest/ts-rest/tree/main/apps/example-next)
6+
7+
- Create a new post
8+
- View all posts
9+
- Search posts
10+
- View a posts
11+
- Update a post
12+
13+
![Example banner](../static/img/blog-example.png)
Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
1-
---
2-
title: '@ts-rest/express'
3-
sidebar_position: 6
4-
---
5-
61
# Express Server
72

8-
:::caution
9-
10-
The Express implementation is a work in progress, it's missing
11-
12-
- Body and Query Parsing
13-
14-
:::
15-
163
```typescript
174
const s = initServer();
185

0 commit comments

Comments
 (0)