axios.get() returns an AxiosResponse<any> object, where response.data is any.
axios.get<Todo[]>() returns an AxiosResponse<Todo[]> object, where response.data is Todo[].
So you can type response as:
const response: AxiosResponse<Todo[]> = await axios.get("blabla");
Answer from rickdenhaan on Stack OverflowWhat response type should I return from Axios if I want to return response in cases where it success or fails?
how does axios handle blob vs arraybuffer as responseType?
reactjs - React and TypeScript—which types for an Axios response? - Stack Overflow
How to Type Axios.js 'AxiosResponse' data generic
Videos
From axios docs:
// `responseType` indicates the type of data that the server will respond with // options are: 'arraybuffer', 'document', 'json', 'text', 'stream' // browser only: 'blob' responseType: 'json', // default
'blob' is a "browser only" option.
So from node.js, when you set responseType: "blob", "json"will actually be used, which I guess fallbacks to "text" when no parse-able JSON data has been fetched.
Fetching binary data as text is prone to generate corrupted data. Because the text returned by Body.text() and many other APIs are USVStrings (they don't allow unpaired surrogate codepoints ) and because the response is decoded as UTF-8, some bytes from the binary file can't be mapped to characters correctly and will thus be replaced by � (U+FFDD) replacement character, with no way to get back what that data was before: your data is corrupted.
Here is a snippet explaining this, using the header of a .png file 0x89 0x50 0x4E 0x47 as an example.
(async () => {
const url = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
// fetch as binary
const buffer = await fetch( url ).then(resp => resp.arrayBuffer());
const header = new Uint8Array( buffer ).slice( 0, 4 );
console.log( 'binary header', header ); // [ 137, 80, 78, 61 ]
console.log( 'entity encoded', entityEncode( header ) );
// [ "U+0089", "U+0050", "U+004E", "U+0047" ]
// You can read more about (U+0089) character here
// https://www.fileformat.info/info/unicode/char/0089/index.htm
// You can see in the left table how this character in UTF-8 needs two bytes (0xC2 0x89)
// We thus can't map this character correctly in UTF-8 from the UTF-16 codePoint,
// it will get discarded by the parser and converted to the replacement character
// read as UTF-8
const utf8_str = await new Blob( [ header ] ).text();
console.log( 'read as UTF-8', utf8_str ); // "�PNG"
// build back a binary array from that string
const utf8_binary = [ ...utf8_str ].map( char => char.charCodeAt( 0 ) );
console.log( 'Which is binary', utf8_binary ); // [ 65533, 80, 78, 61 ]
console.log( 'entity encoded', entityEncode( utf8_binary ) );
// [ "U+FFDD", "U+0050", "U+004E", "U+0047" ]
// You can read more about character � (U+FFDD) here
// https://www.fileformat.info/info/unicode/char/0fffd/index.htm
//
// P (U+0050), N (U+004E) and G (U+0047) characters are compatible between UTF-8 and UTF-16
// For these there is no encoding lost
// (that's how base64 encoding makes it possible to send binary data as text)
// now let's see what fetching as text holds
const fetched_as_text = await fetch( url ).then( resp => resp.text() );
const header_as_text = fetched_as_text.slice( 0, 4 );
console.log( 'fetched as "text"', header_as_text ); // "�PNG"
const as_text_binary = [ ...header_as_text ].map( char => char.charCodeAt( 0 ) );
console.log( 'Which is binary', as_text_binary ); // [ 65533, 80, 78, 61 ]
console.log( 'entity encoded', entityEncode( as_text_binary ) );
// [ "U+FFDD", "U+0050", "U+004E", "U+0047" ]
// It's been read as UTF-8, we lost the first byte.
})();
function entityEncode( arr ) {
return Array.from( arr ).map( val => 'U+' + toHex( val ) );
}
function toHex( num ) {
return num.toString( 16 ).padStart(4, '0').toUpperCase();
}
There is natively no Blob object in node.js, so it makes sense axios didn't monkey-patch it just so they can return a response no-one else would be able to consume anyway.
From a browser, you'd have exactly the same responses:
function fetchAs( type ) {
return axios( {
method: 'get',
url: 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png',
responseType: type
} );
}
function loadImage( data, type ) {
// we can all pass them to the Blob constructor directly
const new_blob = new Blob( [ data ], { type: 'image/jpg' } );
// with blob: URI, the browser will try to load 'data' as-is
const url = URL.createObjectURL( new_blob );
img = document.getElementById( type + '_img' );
img.src = url;
return new Promise( (res, rej) => {
img.onload = e => res(img);
img.onerror = rej;
} );
}
[
'json', // will fail
'text', // will fail
'arraybuffer',
'blob'
].forEach( type =>
fetchAs( type )
.then( resp => loadImage( resp.data, type ) )
.then( img => console.log( type, 'loaded' ) )
.catch( err => console.error( type, 'failed' ) )
);
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<figure>
<figcaption>json</figcaption>
<img id="json_img">
</figure>
<figure>
<figcaption>text</figcaption>
<img id="text_img">
</figure>
<figure>
<figcaption>arraybuffer</figcaption>
<img id="arraybuffer_img">
</figure>
<figure>
<figcaption>blob</figcaption>
<img id="blob_img">
</figure>
Open Console/inspect window in chrome browser and paste this
axios.get('https://images.pexels.com/photos/56866/garden-rose-red-pink-56866.jpeg?cs=srgb&dl=pexels-pixabay-56866.jpg&fm=jpg',{responseType: 'blob'})
.then(response => {
console.log(response.data)
window.open(URL.createObjectURL(response.data))
}).catch(error => {
alert("something goes wrong! Maybe image url broken, try another img url.")
})
There is generic get method defined in axios/index.d.ts
get<T = never, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig<T>): Promise<R>;
Example
interface User {
id: number;
firstName: string;
}
axios.get<User[]>('http://localhost:8080/admin/users')
.then(response => {
console.log(response.data);
setUserList( response.data );
});
I think you are passing list the wrong way to child component.
const [users, setUserList] = useState<User[]>([]);
<UserList items={users} />
interface UserListProps {
items: User[];
};
const UserList: React.FC<UserListProps> = ({items}) => {
return (
<Fragment>
<ul>
{items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
</li>
))}
</ul>
</Fragment>
);
};
You need to provide a type argument when calling axios.get if you do not want Axios to infer the type for the value response as any.
And you are passing an incorrect type argument when you useState to create the array of users.
The correct way
interface User {
id: number;
firstName: string;
}
// Initialized as an empty array
const [users, setUserList] = useState<User[]>([]); // 'users' will be an array of users
For example,
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios from 'axios';
interface User {
id: number;
firstName: string;
}
// You can export the type TUserList to use as -
// props type in your `UserList` component
export type TUserList = User[]
const Users: React.FC = (props) => {
// You can also use User[] as a type argument
const [users, setUserList] = useState<TUserList>();
useEffect(() => {
// Use [] as a second argument in useEffect for not rendering each time
axios.get<TUserList>('http://localhost:8080/admin/users')
.then((response) => {
console.log(response.data);
setUserList(response.data);
});
}, []);
return (
<Fragment>
<UserList {...users} />
</Fragment>
);
};
export default Users;
If you choose to export the type type TUserList = User[], you can use it in your UserList component as the type for props. For example,
import React, {Fragment } from 'react';
import { TUserList } from './Users';
interface UserListProps {
items: TUserList // Don't have to redeclare the object again
};
const UserList: React.FC<UserListProps> = (props) => {
return (
<Fragment>
<ul>
{props.items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
{ /* Do not call the delete function. Just point
to it. Set this to null in bind(). */}
</li>
))}
</ul>
</Fragment>
);
};
export default UserList;
Hi all!
I'm using the axios http library for Node to make requests to a public API as follows:
const response = await axios.get(url) logPaginatedResponse(response)
After receiving a response and storing it in the response variable above, I am passing the variable to the logPaginatedResponse function to log the response to the console.
My logPaginatedResponse function looks like this:
import { AxiosResponse } from "axios"
export const logPaginatedResponse = async (response: AxiosResponse) => {
console.dir({
total_records: response.data.meta.pagination.total,
records_per_page: response.data.meta.pagination.per_page,
current_page: response.data.meta.pagination.current_page,
total_pages: response.data.meta.pagination.total_pages,
})
}
The AxiosResponse interface looks like this:
export interface AxiosResponse<T = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: AxiosRequestConfig;
request?: any;
}
In the logPaginatedResponse, I am trying to get autocompletion for response.data.meta.pagination.total, response.data.meta.pagination.per_page, etc.
The AxiosResponse interface gives me autocompletion for response, but no autocompletion for anything in the data object.
The problem is, the data object on the response could be one of many interfaces. See below:
interface CustomerResponse {
data: {
customer_id: number
},
meta: {
pagination: {
total: number,
count: number,
per_page: number,
current_page: number,
total_pages: number,
links: {
previous: string,
current: string,
next: string
}
}
},
}
interface ProductResponse {
data: {
product_id: number
},
meta: {
pagination: {
total: number,
count: number,
per_page: number,
current_page: number,
total_pages: number,
links: {
previous: string,
current: string,
next: string
}
}
},
}
export const logPaginatedResponse = async (response: AxiosResponse<CustomerResponse | ProductResponse>) => { // console.dir here }
Is there any way to give logPaginatedResponse either the CustomerResponse or ProductResponse interface dynamically?
I tried passing CustomerResponse or ProductResponse to the logPaginatedResponse function at the time of function invocation as follows:
const response = await axios.get(url) logPaginatedResponse<CustomerResponse>(response)
But that isn't doing the trick. Is this not possible? It almost sounds like I'm looking for what this SO post describes: https://stackoverflow.com/a/50512697 but I'm not entirely sure.
Any suggestions would be super appreciated!! Thank you so much.
EDIT:
I ended up refactoring per u/__gc's suggestion in the comments