Skip to content

Commit

Permalink
chore: update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed Apr 29, 2024
1 parent b96e556 commit f23a25d
Showing 1 changed file with 178 additions and 9 deletions.
187 changes: 178 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
```

0 comments on commit f23a25d

Please sign in to comment.