Skip to content

Commit 8525876

Browse files
committed
docs: update api rest chapter
1 parent 317f609 commit 8525876

File tree

1 file changed

+104
-44
lines changed

1 file changed

+104
-44
lines changed

docs/api-rest.md

Lines changed: 104 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ Podemos acceder desde el navegador, que por defecto hace peticiones GET, y visua
5050
Para crear datos, necesitamos usar POST. No podemos usar el navegador, pero podemos usar un cliente como `curl`:
5151

5252
!!! example
53-
Crear un nuevo contador
53+
Vamos a crear un par de contadores
5454

5555
```shell
56-
curl -X POST http://localhost:4000/counters --json '{"value": 0}'
56+
curl -X POST http://localhost:4000/counters --json '{"id": "2", "value": 0}'
57+
curl -X POST http://localhost:4000/counters --json '{"id": "3", "value": -10}'
5758
```
5859

5960
Para actualizar datos, tendremos que usar la url del contador. Dependiendo del servidor, deberemos usar POST, PUT o PATCH.
@@ -71,83 +72,130 @@ Para actualizar datos, tendremos que usar la url del contador. Dependiendo del s
7172
curl -X PUT http://localhost:4000/counters/2 --json '{"value": 10}'
7273
```
7374

75+
Podemos ver ahora que deberíamos tener tres contadores:
76+
77+
```shell
78+
curl -X GET http://localhost:4000/counters
79+
```
80+
7481
## Accediendo a los datos del servidor desde React
7582

7683
Para manejar las solicitudes HTTP de manera efectiva y eficiente en React, podemos utilizar la combinación de Axios para realizar las peticiones y la librería SWR para el fetching y caching de datos. Esto nos permite crear aplicaciones más rápidas y con mejor respuesta al usuario.
7784

78-
### Instalación de Axios y SWR
85+
### Instalación de SWR
7986

80-
Primero, necesitas instalar `axios` para manejar las peticiones HTTP y `swr` para el manejo de datos. Ejecuta el siguiente comando en tu terminal para instalar ambas librerías:
87+
Primero, instala `swr`, que nos ayudará con el fetching, el cacheo y la revalidación de datos. Ejecuta el siguiente comando en tu terminal:
8188

8289
```shell
83-
npm add axios swr
90+
npm add swr
8491
```
8592

86-
Para simplificar las peticiones HTTP y establecer una URL base para todas las llamadas, configura un cliente Axios. Crea un archivo en src/utils/fetcher.ts y define tu cliente Axios y una función fetcher que utilizará SWR para hacer las peticiones.
93+
Para simplificar las peticiones HTTP vamos a centralizar la URL base del servicio y una función `fetcher` que reutilizaremos desde SWR. Crea el archivo `src/utils/fetcher.ts` con el siguiente contenido.
8794

88-
### Configuración del Cliente Axios
95+
### Configuración del fetcher
8996

9097
```ts title="src/utils/fetcher.ts"
91-
import axios from 'axios'
92-
93-
export const client = axios.create({
94-
baseURL: 'http://localhost:4000',
95-
})
98+
export const API_BASE_URL = 'http://localhost:4000'
9699

97100
export async function fetcher(url: string) {
98-
return client.get(url).then((response) => response.data)
101+
const response = await fetch(`${API_BASE_URL}${url}`)
102+
103+
if (!response.ok) {
104+
throw new Error('Request failed')
105+
}
106+
107+
return response.json()
99108
}
100109
```
101110

111+
102112
### Uso de SWR con el Fetcher
103113

104-
Una vez que tienes tu función fetcher definida, puedes usarla con SWR en tus componentes React para acceder a los datos del servidor. SWR manejará automáticamente el caching, la revalidación, y otras optimizaciones.
114+
Una vez que tienes tu función fetcher definida, puedes usarla con SWR en tus componentes React para acceder a los datos del servidor. SWR manejará automáticamente el caching, la revalidación y otras optimizaciones. Para mantener el ejemplo ordenado, vamos a separar la lógica en un servicio y un hook reutilizable antes de llegar al componente.
105115

106-
```
107-
'use client'
116+
### Servicio del contador
108117

109-
import useSWR from 'swr'
118+
```ts title="src/services/counter.ts"
119+
import { API_BASE_URL } from '@/utils/fetcher'
110120

111-
import { client, fetcher } from '@/utils/fetcher'
121+
export async function updateCounter(id: number, value: number) {
122+
const response = await fetch(`${API_BASE_URL}/counters/${id}`, {
123+
body: JSON.stringify({ value }),
124+
headers: { 'Content-Type': 'application/json' },
125+
method: 'PATCH',
126+
})
112127

113-
import { CounterProperties } from './types'
128+
if (!response.ok) {
129+
throw new Error('Unable to update counter')
130+
}
114131

115-
async function updateCounter(id: number, value: number) {
116-
return await client.patch(`/counters/${id}`, { value })
132+
return response.json()
117133
}
134+
```
135+
136+
### Hook `useCounter`
137+
138+
```ts title="src/hooks/use-counter.ts"
139+
'use client'
140+
141+
import useSWR from 'swr'
142+
143+
import { fetcher } from '@/utils/fetcher'
144+
import { updateCounter } from '@/services/counter'
118145

119-
function useCounter(id: number) {
146+
export function useCounter(id: number) {
120147
const { data, isLoading, mutate } = useSWR(`/counters/${id}`, fetcher)
121148

122149
const counter: number = data?.value ?? 0
123150

124151
const onIncrement = async (value: number) => {
125-
const {
126-
data: { value: updatedValue },
127-
} = await updateCounter(id, counter + value)
128-
mutate({ ...data, value: updatedValue }, false)
129-
}
152+
const updated = await updateCounter(id, counter + value)
130153

131-
const onDecrement = async (value: number) => {
132-
const {
133-
data: { value: updatedValue },
134-
} = await updateCounter(id, counter - value)
135-
mutate({ ...data, value: updatedValue }, false)
154+
mutate({ ...(data ?? {}), value: updated.value }, false)
136155
}
137156

138157
const onReset = async () => {
139-
const {
140-
data: { value: updatedValue },
141-
} = await updateCounter(id, 0)
142-
mutate({ ...data, value: updatedValue }, false)
158+
const updated = await updateCounter(id, 0)
159+
160+
mutate({ ...(data ?? {}), value: updated.value }, false)
143161
}
144162

145-
return { counter, isLoading, onDecrement, onIncrement, onReset }
163+
return { counter, isLoading, onIncrement, onReset }
146164
}
165+
```
166+
167+
### Componente `Counter`
168+
169+
Vamos a actualizar nuestro componente `Counter` para que ahora use la API en
170+
vez de almacenar el estado localmente. Si recargamos la página, lo primero que
171+
notaremos es que ahora los contadores tienen el valor por defecto que les hemos
172+
colocado. Si usamos los botones para modificarlos y recargamos, el nuevo valor
173+
se reflejará.
174+
175+
```ts title="src/components/counter/counter.tsx"
176+
'use client'
177+
178+
import {
179+
Button,
180+
ButtonGroup,
181+
Card,
182+
CardFooter,
183+
CardHeader,
184+
Skeleton,
185+
} from '@heroui/react'
186+
import {
187+
ArrowPathIcon,
188+
ChevronDoubleLeftIcon,
189+
ChevronDoubleRightIcon,
190+
ChevronLeftIcon,
191+
ChevronRightIcon,
192+
} from '@heroicons/react/24/solid'
193+
194+
import { useCounter } from '@/hooks/use-counter'
195+
import { CounterProperties } from './types'
147196

148197
export function Counter({ id, step }: CounterProperties) {
149-
const { counter, isLoading, onDecrement, onIncrement, onReset } =
150-
useCounter(id)
198+
const { counter, isLoading, onIncrement, onReset } = useCounter(id)
151199

152200
if (isLoading) {
153201
return (
@@ -173,7 +221,12 @@ export function Counter({ id, step }: CounterProperties) {
173221
</CardHeader>
174222
<CardFooter className="justify-center">
175223
<ButtonGroup>
176-
<Button isIconOnly size="md" aria-label="Decrement counter by step">
224+
<Button
225+
isIconOnly
226+
size="md"
227+
aria-label="Decrement counter by step"
228+
onClick={() => onIncrement(-step * 10)}
229+
>
177230
<ChevronDoubleLeftIcon
178231
className="text-gray-600 dark:text-gray-400"
179232
height="1.3rem"
@@ -186,7 +239,7 @@ export function Counter({ id, step }: CounterProperties) {
186239
isIconOnly
187240
size="md"
188241
aria-label="Decrement counter"
189-
onClick={() => onDecrement(1)}
242+
onClick={() => onIncrement(-step)}
190243
>
191244
<ChevronLeftIcon
192245
className="text-gray-600 dark:text-gray-400"
@@ -211,7 +264,7 @@ export function Counter({ id, step }: CounterProperties) {
211264
isIconOnly
212265
size="md"
213266
aria-label="Increment counter"
214-
onClick={() => onIncrement(1)}
267+
onClick={() => onIncrement(step)}
215268
>
216269
<ChevronRightIcon
217270
className="text-gray-600 dark:text-gray-400"
@@ -221,7 +274,12 @@ export function Counter({ id, step }: CounterProperties) {
221274
+{step}
222275
</div>
223276
</Button>
224-
<Button isIconOnly size="md" aria-label="Increment counter by step">
277+
<Button
278+
isIconOnly
279+
size="md"
280+
aria-label="Increment counter by step"
281+
onClick={() => onIncrement(step * 10)}
282+
>
225283
<ChevronDoubleRightIcon
226284
className="text-gray-600 dark:text-gray-400"
227285
height="1.3rem"
@@ -235,4 +293,6 @@ export function Counter({ id, step }: CounterProperties) {
235293
</Card>
236294
)
237295
}
238-
```
296+
```
297+
298+
Dentro del hook reaprovechamos la misma función `onIncrement` para sumar o restar valores, pasando números negativos cuando queremos decrementar. Así evitamos duplicar lógica y mantenemos el ejemplo centrado en un único flujo de mutación: llamar al endpoint con el nuevo valor y refrescar la caché de SWR.

0 commit comments

Comments
 (0)