Create a custom HTTP hook with TypeScript
Published: 2020-09-29 17:01:11

---
title: Create a custom HTTP hook with TypeScript
published: true
description:
tags: 
//cover_image: https://direct_url_to_image.jpg
---


Hey, React community, in this article, you will find how to create a simple-to-use HTTP hook for making HTTP calls on your React or React Native app.


First, let's create a simple hook

```
export function useHttp<T>() {}
```

- **T** is the type of data our hook and the HTTP response will return


Let's add some parameters
```
export function useHttp<T>(
  request: HttpRequest<T>,
  cb?: HttpCallback<T>,
  config?: HttpConfig<T>,
): HttpHook<T> {}
```

- Request: ``request: HttpRequest<T>``

```
type HttpRequest<T> = (body?: any) => Promise<T>
```

The **HttpRequest** should be the actual API call our hook will execute. The **body** object is the data you will pass every time you make a request.

This could be simplified by passing an URL instead of a promise, but what if you want to call multiple APIs at the same time?

- Callback: ```cb?: HttpCallback<T>```
```
type HttpCallback<T> = (data: T, error?: HttpError) => void
```
A callback function that's going to be called every time the API call is resolved.


- Config: ``config?: HttpConfig<T>``
```
type HttpConfig<T> = {
  headers: any,
  timeout: number
}
```
Here you can add any custom configuration you want to your API call, or maybe something that alters the behavior of your hook.

- Return type

```
type HttpHook<T> = [(body?: any) => Promise<void>, {
  loading: boolean, 
  data: T, 
  error: HttpError | null
}]
```
Our hook will return 2 values
   1. The actual HTTP request
   2. An object with the loading state, the error state, and the response data

The error type could be whatever you API returns. E.g: 

```
export class HttpError {
  status: number
  message: string

  constructor(status: number, message: string) {
    this.status = status
    this.message = message
  }
}
```


Now let's go with the actual logic of our hook: 

```
// Create some states for our 
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<HttpError | null>(null)

async function fetchApi(body?: any): Promise<void> {
  setLoading(true)
  let err = null
  let data: T = {} as T
  try {
    // Await for the request
    const response: any = await request(body)
    // This might vary based on your API
    if(response.data) {
      data = response.data;
    } else {
      data = response;
    }
  } catch (_err) {
    err = _err
  } finally {
    // Set loading to false and verify the response status, if there's an error or whatever you need
    setLoading(false)
    if (err) {
      if(cb) cb(data, err)
      setError(err)
    } else {
      setResponse(data)
      setError(null)
      if(cb) cb(data)
    }
  }
}

return [fetchApi, {loading, data: response, error}]
```

This is the simplest logic for our hook, from this you can add your own configuration params, change the validation based on your API, you could also create a dataExtractor function if you need to use multiple APIs.


Now! Let's go with some examples.


First, create a data type and a dummy API call.

```
type LightsaberType = {
  color: string,
  material: string
}

let lightsabers: LightsaberType[] = [{
  color: 'blue',
  material: 'Electrum Plated'
}, {
  color: 'red',
  material: 'Durasteel'
}]

const fetchLightsaberList = async () => {
  return lightsabers
}
```


And it's as simple as this

```
export const LightsaberListScreen = () => {
  const [getLightsabers, { loading, data }] = useHttp<LightsaberType[]>(fetchLightsaberList)

  useEffect(() => {
    getLightsabers()
  }, [])

  return (
    <View>
      {loading? (
        <ActivityIndicator />
      ) : (
        <FlatList
          data={data || []}
          renderItem={({item}) => null}
        />
      )}
    </View>
  )
}

```

But, what if you want to handle your own state...

```
export const LightsaberListScreen = () => {
  const [lightsabers, setLightsabers] = useState<LightsaberType[]>([])
  const [getLightsabers, { loading }] = useHttp<LightsaberType[]>(
    fetchLightsaberList,
    (data, err) => {
      if(err) showCustomToast('Oh no, I don\'t have any lightsaber!')
      setLightsabers(data)
    }
  )

  useEffect(() => {
    getLightsabers()
  }, [])

  return (
    <View>
      {loading? (
        <ActivityIndicator />
      ) : (
        <FlatList
          data={lightsabers}
          renderItem={({item}) => null}
        />
      )}
    </View>
  )
}

```

OR you need to pass a body

```
const postLightsaber = async (lightsaber: LightsaberType) => {
  lightsabers.push(lightsaber)
  return lightsabers
}


...LightsaberListScreen component
const [createLightsaber] = useHttp<LightsaberType[]>(
    (body: LightsaberType) => postLightsaber(body),
    (data, err) => {
      if(err) showCustomToast('Oh no, you\'ll need to go back to Ilum')
      setLightsabers(data)
    }
  )

return (
  <Button 
    text="Create your own lightsaber"
    onPress={() =>  createLightsaber({
        color: 'cian',
        material:'Aurodium'
  })}/>
)

```

This is my first article so I really hope you like it and I'm completely open to suggestions!

Happy coding 🚀