Skip to content

Commit

Permalink
feat(api): add useQuery, useMutation, useGet composables (#409)
Browse files Browse the repository at this point in the history
Co-authored-by: Kia King Ishii <kia.king.08@gmail.com>
  • Loading branch information
brc-dd and kiaking committed Dec 7, 2023
1 parent 93a4508 commit a9a77e7
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function sidebar(): DefaultTheme.SidebarItem[] {
text: 'Composables',
collapsed: false,
items: [
{ text: 'Api', link: '/composables/api' },
{ text: 'Image', link: '/composables/image' },
{ text: 'Url', link: '/composables/url' },
{ text: 'Utils', link: '/composables/utils' }
Expand Down
90 changes: 90 additions & 0 deletions docs/composables/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Api <Badge text="3.9.0" />

`Api` module provides a set of composable functions to work with the network requests. This composable has direct integration with [`Http`](../network-requests/http) module.

## `useQuery`

Executes given promise function immediately and set the result to `data` ref.

```ts
interface Query<Data = any> {
loading: Ref<boolean>
data: Ref<Data | undefined>
execute(): Promise<Data>
}

interface UseQueryOptions {
/**
* controls whether the query should execute immediately.
*
* @default true
*/
immediate?: boolean
}

function useQuery<Data = any>(
req: (http: Http) => Promise<Data>,
options?: UseQueryOptions
): Query<Data>
```

```ts
import { useQuery } from '@globalbrain/sefirot/lib/composables/Api.vue'
const { data, loading } = useQuery((http) => {
return http.get('/api/users')
})
```

## `useGet`

Alias to [`useMutation`](#usemutation). Use this method when you want to execute a request on user interaction, but not mutating data. Good example is when downloading a file.

```ts
type Get<
Data = any,
Args extends any[] = any[]
> = Mutation<Data, Args>
function useGet<Data = any, Args extends any[] = any[]>(
req: (http: Http, ...args: Args) => Promise<Data>
): Get<Data, Args>
```

```ts
import { useGet } from '@globalbrain/sefirot/lib/composables/Api.vue'
const { execute: download } = useGet((http, path: string) => {
return http.download(`/api/users/${path}`)
})
download('avatar.png')
```

## `useMutation`

Creates a composable that can execute given promise function at later time.

```ts
interface Mutation<Data = any, Args extends any[] = any[]> {
loading: Ref<boolean>
data: Ref<Data | undefined>
execute(...args: Args): Promise<Data>
}
function useMutation<Data = any, Args extends any[] = any[]>(
req: (http: Http, ...args: Args) => Promise<Data>
): Mutation<Data, Args>
```

```ts
import { useMutation } from '@globalbrain/sefirot/lib/composables/Api.vue'
const { execute: createUser } = useMutation((http, name: string) => {
return http.post(`/api/users`, {
name
})
})
updateUser(1, 'Jane Doe')
```
71 changes: 71 additions & 0 deletions lib/composables/Api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { type Ref, ref } from 'vue'
import { Http } from '../http/Http'

export interface Query<Data = any> {
loading: Ref<boolean>
data: Ref<Data | undefined>
execute(): Promise<Data>
}

export interface UseQueryOptions {
/**
* controls whether the query should execute immediately
* @default true
*/
immediate?: boolean
}

export interface Mutation<Data = any, Args extends any[] = any[]> {
loading: Ref<boolean>
data: Ref<Data | undefined>
execute(...args: Args): Promise<Data>
}

export type Get<Data = any, Args extends any[] = any[]> = Mutation<Data, Args>

export function useQuery<Data = any>(
req: (http: Http) => Promise<Data>,
options: UseQueryOptions = {}
): Query<Data> {
const loading = ref(false)
const data = ref<Data | undefined>()

if (options.immediate !== false) {
execute()
}

async function execute(): Promise<Data> {
loading.value = true

const res: Data = await req(new Http())
data.value = res

loading.value = false
return res
}

return { loading, data, execute }
}

export function useMutation<Data = any, Args extends any[] = any[]>(
req: (http: Http, ...args: Args) => Promise<Data>
): Mutation<Data, Args> {
const loading = ref(false)
const data = ref<Data | undefined>()

async function execute(...args: Args): Promise<Data> {
loading.value = true

const res: Data = await req(new Http(), ...args)
data.value = res

loading.value = false
return res
}

return { loading, data, execute }
}

export const useGet: <Data = any, Args extends any[] = any[]>(
req: (http: Http, ...args: Args) => Promise<Data>
) => Get<Data, Args> = useMutation

0 comments on commit a9a77e7

Please sign in to comment.