-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b96e556
commit f23a25d
Showing
1 changed file
with
178 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,7 @@ node ace tuyau:generate | |
|
||
Once installed, you must create a file called `tuyau.ts` in your frontend project. This file must contain the following code : | ||
|
||
```typescript | ||
```ts | ||
/// <reference path="../../adonisrc.ts" /> | ||
|
||
import { createTuyau } from '@tuyau/client' | ||
|
@@ -71,18 +71,187 @@ Multiple things to note here : | |
- We must reference the `adonisrc.ts` file at the top of the file. By doing that, the frontend project will be aware of some types defined in the AdonisJS project. | ||
- We must import the `AdonisApi` type from the `.adonisjs/types/api` file in your AdonisJS project. You should change the path to match your project structure. | ||
|
||
Now, you can use the `tuyau` object to make requests to your AdonisJS API. Here is an example : | ||
|
||
```typescript | ||
#### Initializing the client | ||
|
||
Once installed, you must create the tuyau client in your frontend project : | ||
|
||
```ts | ||
/// <reference path="../../adonisrc.ts" /> | ||
|
||
import { createTuyau } from '@tuyau/client' | ||
import type { AdonisApi } from '@your-monorepo/server/.adonisjs/types/api' | ||
|
||
export const tuyau = createTuyau<AdonisApi>('http://localhost:3333') | ||
``` | ||
|
||
Multiple things to note here : | ||
|
||
- We must reference the `adonisrc.ts` file at the top of the file. By doing that, the frontend project will be aware of some types defined in the AdonisJS project. | ||
- We must import the `AdonisApi` type from the `.adonisjs/types/api` file in your AdonisJS project. You should change the path to match your project structure. | ||
|
||
##### Options | ||
|
||
Tuyau client is built on top of [Ky](https://github.com/sindresorhus/ky). Make sure to check the documentation of Ky to see all the available options. You can pass options to the client like this : | ||
|
||
```ts | ||
const tuyau = createTuyau<AdonisApi>('http://localhost:3333', { | ||
timeout: 10_000, | ||
headers: { 'X-Custom-Header': 'foobar' }, | ||
hooks: { | ||
beforeRequest: [ | ||
(request) => { | ||
const token = getToken() | ||
if (token) { | ||
request.headers.set('Authorization', `Bearer ${token}`) | ||
} | ||
} | ||
] | ||
} | ||
}) | ||
``` | ||
|
||
#### Making requests | ||
|
||
Making requests with Tuyau is pretty straightforward. Basically, what you need to chain the different parts of the route you want to call, use `.` instead of `/` and finally call the `$method` you want to use. | ||
|
||
```ts | ||
import { tuyau } from './tuyau' | ||
|
||
const result = await tuyau.users.$get() | ||
const result = await tuyau.users.$post({ name: 'John Doe' }) | ||
// GET /users | ||
await tuyau.users.$get() | ||
|
||
// POST /users { name: 'John Doe' } | ||
await tuyau.users.$post({ name: 'John Doe' }) | ||
|
||
// PUT /users/1 { name: 'John Doe' } | ||
await tuyau.users({ id: 1 }).$put({ name: 'John Doe' }) | ||
|
||
// GET /users/1/posts?limit=10&page=1 | ||
await tuyau.users.$get({ query: { page: 1, limit: 10 } }) | ||
``` | ||
|
||
#### Path parameters | ||
|
||
If you need to call a route that has some path parameters, you can pass an object to the related function. Here is an example : | ||
|
||
```ts | ||
// Backend | ||
router.get('/users/:id/posts/:postId/comments/:commentId', '...') | ||
|
||
// Frontend | ||
const result = await tuyau.users({ id: 1 }) | ||
.posts({ postId: 2 }) | ||
.comments({ commentId: 3 }) | ||
.$get() | ||
``` | ||
|
||
#### Request Parameters | ||
|
||
const result = await tuyau.users({ id: 1 }).$get() | ||
const result = await tuyau.users({ id: 1 }).$put({ name: 'John Doe' }) | ||
You can pass specific `Ky` options to the request by passing a second argument to the request method : | ||
|
||
const result = await tuyau.users.$get({ | ||
query: { page: 1, limit: 10 }, | ||
```ts | ||
await tuyau.users.$post({ name: 'John Doe' }, { | ||
headers: { | ||
'X-Custom-Header': 'foobar' | ||
} | ||
}) | ||
``` | ||
|
||
When using the `$get` method you can pass a `query` object to the request : | ||
|
||
```ts | ||
await tuyau.users.$get({ | ||
headers: { | ||
'X-Custom-Header': 'foobar' | ||
}, | ||
query: { | ||
page: 1, | ||
limit: 10 | ||
} | ||
}) | ||
``` | ||
|
||
#### Responses | ||
|
||
For every request, Tuyau will return a promise with the following types : | ||
|
||
- `data` : The response data if status is 2xx | ||
- `error` : The error data if status is 3xx | ||
- `status` : The status code of the response | ||
- `response` : The full response object | ||
|
||
Once returned, you must narrow the type of the response. That means you must check if the status is 2xx or 3xx and then use the `data` or `error` property accordingly. | ||
|
||
Let's take a dumb example. A simple route that will return a 401 if the password is not `password`, otherwise it will return a secret token : | ||
|
||
```ts | ||
// Backend | ||
class MyController { | ||
public async login({ request, response }) { | ||
const { email, password } = request.validateUsing(schema) | ||
if (password !== 'password') { | ||
return response.unauthorized({ message: 'Invalid credentials' }) | ||
} | ||
|
||
return { token: 'secret-token' } | ||
} | ||
} | ||
|
||
router.post('/login', [MyController, 'login']) | ||
|
||
// Frontend | ||
const { data, error } = await tuyau.login.$post({ email: '[email protected]', password: 'password' }) | ||
|
||
data | ||
// ^? { token: string } | null | ||
|
||
if (error?.status === 401) { | ||
console.error('Wrong password !!') | ||
return | ||
} | ||
|
||
console.log(data.token) | ||
// ^? { token: string } | ||
// data.token will be available and unwrapped here | ||
``` | ||
|
||
Without narrowing the type of the response, `data` and `error` could be `undefined`, so you must check that in your code before using them. | ||
|
||
##### Unwrapping the response | ||
|
||
Sometimes, you may not want to check or handle the error specifically in your code. You can use the `unwrap` method to unwrap the response and throw an error if the status is not 2xx. | ||
|
||
```ts | ||
const result = await tuyau.login.$post({ email: '[email protected]' }).unwrap() | ||
console.log(result.token) | ||
``` | ||
|
||
#### Infering request and responses | ||
|
||
The client package expose some helpers for inferring the request and response types of a route. Here is an example : | ||
|
||
```ts | ||
import type { InferResponseType, InferErrorType, InferRequestType } from '@tuyau/client'; | ||
|
||
// InferResponseType | ||
type LoginRequest = InferRequestType<typeof tuyau.login.post>; | ||
|
||
// InferResponseType | ||
type LoginResponse = InferResponseType<typeof tuyau.login.post>; | ||
|
||
// InferErrorType | ||
type LoginError = InferErrorType<typeof tuyau.login.post>; | ||
``` | ||
|
||
#### Generating URL | ||
|
||
If you need to generate the URL of a route without making the request, you can use the `$url` method : | ||
|
||
```ts | ||
const url = tuyau.users.$url() | ||
console.log(url) // http://localhost:3333/users | ||
|
||
const url = tuyau.users({ id: 1 }).posts({ postId: 2 }).$url() | ||
console.log(url) // http://localhost:3333/users/1/posts/2 | ||
``` |