Skip to content

Commit

Permalink
feat: working version
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed May 8, 2024
1 parent 1231753 commit e26f6b2
Show file tree
Hide file tree
Showing 21 changed files with 1,692 additions and 543 deletions.
90 changes: 90 additions & 0 deletions .changeset/tasty-pots-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
"@tuyau/client": minor
"@tuyau/utils": minor
"@tuyau/core": minor
"playground": patch
---

Lots of changes here. Tuyau now has a system that's a bit like [Ziggy](https://github.com/tighten/ziggy). Basically, we can use the names of our routes defined in AdonisJS, but in the frontend. Let's start by listing the breaking changes:

## Breaking Changes & Migration

- You will first need to download the latest versions of `@tuyau/utils` `@tuyau/core` and `@tuyau/client` for everything to work correctly.
- The codegen file has changed location. It is now located in `.adonisjs/api.ts`. You will therefore need to change the import path of your client to access this new file.
- To initialise the client, you will now need to do the following:
```ts
/// <reference path="../../adonisrc.ts" />

import { createTuyau } from '@tuyau/client'
import type { api } from '@your-monorepo/server/.adonisjs/api'

export const tuyau = createTuyau({
api,
baseUrl: 'http://localhost:3333',
})
```

As you can see, you first need to change the import path and the imported value. Next, you need to pass this `api` object as an argument to the `createTuyau` function.

Remember to re-generate your `.adonisj/api.ts` file via `node ace tuyau:generate`, and you should be fine for the rest.

## Features

### Making Requests using the route name

If you want to use the names of the routes instead of paths, you can use the `$route` method :

```ts
// Backend
router.get('/posts/:id/generate-invitation', '...').as('posts.generateInvitation')

// Client
await tuyau.$route('posts.generateInvitation', { id: 1 }).$get({
query: { limit: 10, page: 1 }
})
```

### Generating URL from route name

If you need to generate the URL of a route using the route name, you can use the `$url` method. This method is pretty similar to [Ziggy](https://github.com/tighten/ziggy) behavior :

```ts
tuyau.$url('users.posts', { id: 1, postId: 2 }) // http://localhost:3333/users/1/posts/2
tuyau.$url('venues.events.show', [1, 2]) // http://localhost:3333/venues/1/events/2
tuyau.$url('users', { query: { page: 1, limit: 10 } }) // http://localhost:3333/users?page=1&limit=10
```

If you are used to Ziggy and prefer to have a `route` method instead of `$url`, you can define a custom method in your client file pretty easily :

```ts
export const tuyau = createTuyau({
api,
baseUrl: 'http://localhost:3333'
})

window.route = tuyau.$url.bind(tuyau)
```

Then you can use the `route` method in your frontend code :

```tsx
export function MyComponent() {
return (
<div>
<a href={route('users.posts', { id: 1, postId: 2 })}>Go to post</a>
</div>
)
}
```

### Check if a route exists

If you need to check if a route name exists, you can use the `$has` method. You can also use wildcards in the route name :

```ts
tuyau.$has('users') // true
tuyau.$has('users.posts') // true
tuyau.$has('users.*.comments') // true
tuyau.$has('users.*') // true
tuyau.$has('non-existent') // false
```
93 changes: 69 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Set of tools to create typesafe APIs using AdonisJS. The monorepo includes the f
The main goal of this project is to provide some utilities to have better typesafety when creating APIs with AdonisJS. Goals on the long term are :

- **Done (Experimental)** : Provide an RPC-like client that is fully e2e typesafe ( like tRPC, Elysia Eden, Hono etc. )
- **In Progress** : Provide a [Ziggy](https://github.com/tighten/ziggy)-like helper to generate and use routes in the frontend.
- **Done (Experimental)** : Provide a [Ziggy](https://github.com/tighten/ziggy)-like helper to generate and use routes in the frontend.
- **Not started** : Provide some Inertia helpers to have better typesafety when using Inertia in your AdonisJS project. Things like typesafe `<Link />` and `useForm`.
- **Not started** : Provide a specific Controller class that will allow to have better typesafety when creating your endpoints.
- **Not started** : Having an automatic OpenAPI generation + Swagger/Scalar UI viewer based on Tuyau codegen. Still not sure about this one.
Expand Down Expand Up @@ -54,25 +54,6 @@ To run the command manually, you must run :
node ace tuyau:generate
```

### Client package

Once installed, you must create a file called `tuyau.ts` in your frontend project. This file must contain the following code :

```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.


#### Initializing the client

Once installed, you must create the tuyau client in your frontend project :
Expand All @@ -81,22 +62,27 @@ Once installed, you must create the tuyau client in your frontend project :
/// <reference path="../../adonisrc.ts" />

import { createTuyau } from '@tuyau/client'
import type { AdonisApi } from '@your-monorepo/server/.adonisjs/types/api'
import { api } from '@your-monorepo/server/.adonisjs/api'

export const tuyau = createTuyau<AdonisApi>('http://localhost:3333')
export const tuyau = createTuyau({
api,
baseUrl: '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.
- We must import `api` from the `.adonisjs/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', {
const tuyau = createTuyau({
api,
baseUrl: 'http://localhost:3333',
timeout: 10_000,
headers: { 'X-Custom-Header': 'foobar' },
hooks: {
Expand Down Expand Up @@ -132,6 +118,20 @@ await tuyau.users({ id: 1 }).$put({ name: 'John Doe' })
await tuyau.users.$get({ query: { page: 1, limit: 10 } })
```

#### Making Requests using the route name

If you want to use the names of the routes instead of paths, you can use the `$route` method :

```ts
// Backend
router.get('/posts/:id/generate-invitation', '...').as('posts.generateInvitation')

// Client
await tuyau.$route('posts.generateInvitation', { id: 1 }).$get({
query: { limit: 10, page: 1 }
})
```

#### 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 :
Expand Down Expand Up @@ -293,6 +293,51 @@ const url = tuyau.users({ id: 1 }).posts({ postId: 2 }).$url()
console.log(url) // http://localhost:3333/users/1/posts/2
```

##### Generating URL from route name

If you need to generate the URL of a route using the route name, you can use the `$url` method. This method is pretty similar to [Ziggy](https://github.com/tighten/ziggy) behavior :

```ts
tuyau.$url('users.posts', { id: 1, postId: 2 }) // http://localhost:3333/users/1/posts/2
tuyau.$url('venues.events.show', [1, 2]) // http://localhost:3333/venues/1/events/2
tuyau.$url('users', { query: { page: 1, limit: 10 } }) // http://localhost:3333/users?page=1&limit=10
```

If you are used to Ziggy and prefer to have a `route` method instead of `$url`, you can define a custom method in your client file pretty easily :

```ts
export const tuyau = createTuyau({
api,
baseUrl: 'http://localhost:3333'
})

window.route = tuyau.$url.bind(tuyau)
```

Then you can use the `route` method in your frontend code :

```tsx
export function MyComponent() {
return (
<div>
<a href={route('users.posts', { id: 1, postId: 2 })}>Go to post</a>
</div>
)
}
```

#### Check if a route exists

If you need to check if a route name exists, you can use the `$has` method. You can also use wildcards in the route name :

```ts
tuyau.$has('users') // true
tuyau.$has('users.posts') // true
tuyau.$has('users.*.comments') // true
tuyau.$has('users.*') // true
tuyau.$has('non-existent') // false
```

## Sponsors

![](https://github.com/julien-r44/static/blob/main/sponsorkit/sponsors.png?raw=true)
Expand Down
4 changes: 2 additions & 2 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"build": "tsup-node",
"test": "c8 node --loader ts-node/esm bin/test.ts",
"quick:test": "node --loader ts-node/esm bin/test.ts",
"test": "c8 node --enable-source-maps --loader ts-node/esm bin/test.ts",
"quick:test": "node --enable-source-maps --loader ts-node/esm bin/test.ts",
"checks": "pnpm lint && pnpm typecheck"
},
"dependencies": {
Expand Down
Loading

0 comments on commit e26f6b2

Please sign in to comment.