Frontend API calls with TypeScript and Axios

An opinionated method of supercharging frontend API call with TypeScript and Axios

By Demola Malomo

February 26th, 2021

image

Many developers have adopted the method of separating backend services from frontend applications. This approach allows them to grow and evolve independently.

In frontend development, it is important to know how dynamic requests are made to backend services.

In this tutorial, we will be using React and TypeScript to develop our frontend application. (Note: You can use this same approach on other frontend libraries/frameworks.)

For the backend services, we will be using **JSONPlaceholder**. A free online API service.

You can code along by cloning this repository (main branch) https://github.com/Mr-Malomz/api-call (If you prefer to view the complete code, checkout to the dev branch of this same repository.)

In this tutorial, we will focus on API calls only. The project UI has already been set up with **chakra UI**

Folder Structure

project folder structureproject folder structure

Let’s go over some of the key directories and files from above:

  • api: a folder to store files related to making API calls.

  • components: a folder to store the building blocks of our application.

  • models: a folder to store files describing response types.

  • App.tsx: a file connecting all the components.

Running the Project

Navigate to the project location, open your terminal and install project dependency

npm install

//or

yarn install

Then run the project using the command below

npm start

//or

yarn start

Your project should popup on http://localhost:3000

Project running on http://localhost:3000

Models

In other to take advantage of the typing system TypeScript comes with, we need to create a model to describe the API response **JSONPlaceholder **returns.

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

With the response from **JSONPlaceholder**, let's now create a model to represent the response type.

Step 1

Create a new file post.interface.ts inside the models folder.

Updated folder structureUpdated folder structure

Step 2

Create an interface to describe the response properties (userId, id, title, and body). The question mark in front of id and userId key tells TypeScript that these properties are optional since it is automatically generated by **JSONPlaceholder**.

export interface PostType {
	userId?: number;
	id?: number;
	title: string;
	body: string;
}

API Calls

With the model wrapped up, we can now start making API calls to **JSONPlaceholder** endpoint. We will be using **axios** to make API calls in this application. Axios is a promise-based HTTP client for the browser and node.js.

Step 1

Create a new file api.ts inside the api folder.

updated folder structureupdated folder structure

Step 2

We need to configure **axios, **create Requests and Post object to handle create, read, update, and delete (CRUD) as shown below

import axios, { AxiosResponse } from 'axios';
import { PostType } from '../models/post.interface';

const instance = axios.create({
	baseURL: 'http://jsonplaceholder.typicode.com/',
	timeout: 15000,
});

const responseBody = (response: AxiosResponse) => response.data;

const requests = {
	get: (url: string) => instance.get(url).then(responseBody),
	post: (url: string, body: {}) => instance.post(url, body).then(responseBody),
	put: (url: string, body: {}) => instance.put(url, body).then(responseBody),
	delete: (url: string) => instance.delete(url).then(responseBody),
};

export const Post = {
	getPosts: (): Promise<PostType[]> => requests.get('posts'),
	getAPost: (id: number): Promise<PostType> => requests.get(`posts/${id}`),
	createPost: (post: PostType): Promise<PostType> =>
		requests.post('posts', post),
	updatePost: (post: PostType, id: number): Promise<PostType> =>
		requests.put(`posts/${id}`, post),
	deletePost: (id: number): Promise<void> => requests.delete(`posts/${id}`),
};

Let’s explain this code a bit. We follow these steps:

  • Imported axios and AxiosResponse. AxiosResponse is a TypeScript response-type provided by axios. We also imported PostType, the response-type we created earlier.

  • Configured axios to use **JSONPlaceholder** as the base URL and 15 seconds timeout for our request.

  • Destructured** axios **and got the data response from its response object.

  • Created a request object to handle CRUD and returned the destructured axios body created earlier.

  • Created and exported a Post object that uses the request object created earlier to handle CRUD operation using the request objects.

Consuming Endpoint

**Get All Post **To get all post we need to make our API call using the getPosts function of the Post object we created earlier in api.ts

import './App.css';
import Header from './components/Header';
import { Box, useDisclosure } from '@chakra-ui/react';
import Card from './components/Card';
import Create from './components/Create';
import { useEffect, useState } from 'react'; //add
import Edit from './components/Edit';
import { PostType } from './models/post.interface'; //add
import { Post } from './api/api'; //add

function App() {
	const { isOpen, onOpen, onClose } = useDisclosure();
	const [isCreate, setIsCreate] = useState(false);
	const [isEdit, setIsEdit] = useState(false);

	//add
	const [posts, setPosts] = useState<PostType[]>([]);
	const [isError, setIsError] = useState<boolean>(false);

	//add
	useEffect(() => {
		Post.getPosts()
			.then((data) => {
				setPosts(data);
			})
			.catch((err) => {
				setIsError(true);
			});
		return () => {};
	}, []);

	return (
		<>
			<div>
				<Header onOpen={onOpen} setIscreate={setIsCreate} />
				<Box mt='20' mx='auto' width={{ sm: '100%', lg: '80%' }}>
					{/* Modify this section */}
					{isError ? (
						<Box
							mt='1'
							fontWeight='bold'
							fontSize='xl'
							as='h6'
							isTruncated
							color='red'
						>
							Oop!!! Error getting posts
						</Box>
					) : (
						posts.map((post) => (
							<Card
								key={post.id}
								onOpen={onOpen}
								setIsEdit={setIsEdit}
								post={post}
							/>
						))
					)}
				</Box>
			</div>

			{/* Create post form */}
			{isCreate && (
				<Create
					isOpen={isOpen}
					onClose={onClose}
					setIsCreate={setIsCreate}
				/>
			)}
			{isEdit && (
				<Edit isOpen={isOpen} onClose={onClose} setIsEdit={setIsEdit} />
			)}
		</>
	);
}

export default App;

We took the following steps in the code above:

  • Imported useEffect, useState, PostType(response type), and Post (API object).

  • Created states to manage and update posts and errors respectively. We also define the response-type for both posts (Array of type PostType) and isError(boolean).

  • Make API call using the Post.getPosts function inside the useEffect hook. We then updated the app state with post and catch errors if there are any.

  • Set an error boundary and display an appropriate message if there is any. Then we looped through the card component, pass the key, and the post as parameters.

TypeScript will complain about the card component not having the post property. We will fix this.

To fix this, navigate to the components folder and edit Card.tsx as shown below

import { Box, Button, Flex, Image, Link } from '@chakra-ui/react';
import { FC } from 'react';
import { PostType } from '../models/post.interface'; //add

interface CardProps {
	onOpen: () => void;
	setIsEdit: (state: boolean) => void;
	post: PostType; //add
}

const Card: FC<CardProps> = ({ onOpen, setIsEdit, post }) => {
	return (
		<Box w='100%' borderWidth='1px' borderRadius='lg'>
			<Flex direction={{ sm: 'column', lg: 'row' }}>
				<Image
					src='https://static.thenounproject.com/png/1087123-200.png'
					alt='place holder image'
					w={{ sm: '100%', lg: '20%' }}
				/>
				<Box p='6' w={{ sm: '100%', lg: '80%' }}>
					<Box
						mt='1'
						fontWeight='bold'
						fontSize='2xl'
						as='h1'
						lineHeight='tight'
						isTruncated
					>
						{post.title}
					</Box>
					<Box mt='4'>{post.body}</Box>
					<Flex mt='10' align='center' justify='center'>
						<Button colorScheme='red' variant='outline' mr='4'>
							Delete
						</Button>
						<Button
							color='white'
							borderRadius='lg'
							background='teal.500'
							_hover={{ background: 'teal' }}
							onClick={() => {
								onOpen();
								setIsEdit(true);
							}}
						>
							Edit
						</Button>
					</Flex>
				</Box>
			</Flex>
		</Box>
	);
};

export default Card;

Noticed in the above code that we added the post property to the CardProps and destructured it in the Card component.

Lastly, we displayed the title and body property in the Card.tsx elements.

Your outcome should be like the image below.

List of postsList of posts

**Create A Post **To create a post, we need to pass the posts and setPosts state to Create component as parameters. We will also need to update the CreateProps interface to reflect these changes. These properties will help us update our UI from the Create component.

import './App.css';
import Header from './components/Header';
import { Box, useDisclosure } from '@chakra-ui/react';
import Card from './components/Card';
import Create from './components/Create';
import { useEffect, useState } from 'react';
import Edit from './components/Edit';
import { PostType } from './models/post.interface';
import { Post } from './api/api';

function App() {
	const { isOpen, onOpen, onClose } = useDisclosure();
	const [isCreate, setIsCreate] = useState(false);
	const [isEdit, setIsEdit] = useState(false);

	const [posts, setPosts] = useState<PostType[]>([]);
	const [isError, setIsError] = useState<boolean>(false);

	useEffect(() => {
		Post.getPosts()
			.then((data) => {
				setPosts(data);
			})
			.catch((err) => {
				setIsError(true);
			});
		return () => {};
	}, []);

	return (
		<>
			<div>
				<Header onOpen={onOpen} setIscreate={setIsCreate} />
				<Box mt='20' mx='auto' width={{ sm: '100%', lg: '80%' }}>
					{/* Modify this section */}
					{isError ? (
						<Box
							mt='1'
							fontWeight='bold'
							fontSize='xl'
							as='h6'
							isTruncated
							color='red'
						>
							Oop!!! Error getting posts
						</Box>
					) : (
						posts.map((post) => (
							<Card
								key={post.id}
								onOpen={onOpen}
								setIsEdit={setIsEdit}
								post={post}
							/>
						))
					)}
				</Box>
			</div>

			{/* Create post form */}
			{/* modify create component with posts and setPosts */}
			{isCreate && (
				<Create
					isOpen={isOpen}
					onClose={onClose}
					setIsCreate={setIsCreate}
					posts={posts}
					setPosts={setPosts}
				/>
			)}
			{isEdit && (
				<Edit isOpen={isOpen} onClose={onClose} setIsEdit={setIsEdit} />
			)}
		</>
	);
}

export default App;

We also need to update Create.tsx with posts and setPosts state which we passed to it earlier.

import { Box, Button, Input, Text, Textarea } from '@chakra-ui/react';
import React, { FC, useState } from 'react';
import { ModalProp } from '../models/modal.interface';
import ModalWrap from './ModalWrap';
import { PostType } from '../models/post.interface'; //add

interface CreateProps extends ModalProp {
	setIsCreate: (state: boolean) => void;
	posts: PostType[]; //add
	setPosts: (updatedPost: PostType[]) => void; //add
}

const Create: FC<CreateProps> = ({ isOpen, onClose, setIsCreate, posts, setPosts }) => {
	const [value, setValue] = useState({
		title: '',
		body: '',
	});

	const handleChange = (e: React.FormEvent<EventTarget>) => {
		let target = e.target as HTMLInputElement;
		setValue({ ...value, [target.name]: target.value });
	};

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		console.log(value);
	};

	return (
		<ModalWrap
			isOpen={isOpen}
			onClose={() => {
				onClose();
				setIsCreate(false);
			}}
			title='Create New Post'
		>
			<form onSubmit={handleSubmit}>
				<Box mb='8'>
					<Text mb='8px'>Title</Text>
					<Input
						type='text'
						value={value.title}
						onChange={handleChange}
						name='title'
						required
					/>
				</Box>
				<Box mb='8'>
					<Text mb='8px'>Body</Text>
					<Textarea
						value={value.body}
						onChange={handleChange}
						name='body'
						required
					/>
				</Box>
				<Button type='submit' w='100%' colorScheme='teal' mb='8'>
					Submit
				</Button>
			</form>
		</ModalWrap>
	);
};

export default Create;

With that done, we can make a post request to the API.

import { Box, Button, Input, Text, Textarea } from '@chakra-ui/react';
import React, { FC, useState } from 'react';
import { ModalProp } from '../models/modal.interface';
import ModalWrap from './ModalWrap';
import { PostType } from '../models/post.interface';
import { Post } from '../api/api';

interface CreateProps extends ModalProp {
	setIsCreate: (state: boolean) => void;
	posts: PostType[];
	setPosts: (updatedPost: PostType[]) => void;
}

const Create: FC<CreateProps> = ({
	isOpen,
	onClose,
	setIsCreate,
	posts,
	setPosts,
}) => {
	const [value, setValue] = useState({
		title: '',
		body: '',
	});
	const [isError, setIsError] = useState<boolean>(false);

	const handleChange = (e: React.FormEvent<EventTarget>) => {
		let target = e.target as HTMLInputElement;
		setValue({ ...value, [target.name]: target.value });
	};

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		//add
		Post.createPost(value)
			.then((data) => {
				setPosts([data, ...posts]);
				setValue({ ...value, title: '', body: '' });
				onClose();
			})
			.then((err) => {
				setIsError(true)
			});
	};

	return (
		<ModalWrap
			isOpen={isOpen}
			onClose={() => {
				onClose();
				setIsCreate(false);
			}}
			title='Create New Post'
		>
			{/* Add this part */}
			{isError && (
				<Box
					mt='1'
					fontWeight='bold'
					fontSize='sm'
					as='p'
					isTruncated
					color='red'
				>
					Oop!!! Error creating post
				</Box>
			)}
			<form onSubmit={handleSubmit}>
				<Box mb='8'>
					<Text mb='8px'>Title</Text>
					<Input
						type='text'
						value={value.title}
						onChange={handleChange}
						name='title'
						required
					/>
				</Box>
				<Box mb='8'>
					<Text mb='8px'>Body</Text>
					<Textarea
						value={value.body}
						onChange={handleChange}
						name='body'
						required
					/>
				</Box>
				<Button type='submit' w='100%' colorScheme='teal' mb='8'>
					Submit
				</Button>
			</form>
		</ModalWrap>
	);
};

export default Create;

Let’s explain this code a bit.

  • First, we added a state to manage errors when creating the form.

  • Secondly, we updated the handleSubmit function by making an API call using **Post.createPost() **function, update the states, and close the modal, accordingly.

  • Lastly, we conditionally handled errors that might occur while creating the form.

Since our API calls are not persisted in a database, it will be deleted when we refresh our browser.

Updating A Post

Updating a post is almost the same thing as creating one. We need to pass the posts and setPosts state to the Edit component as parameters. We will also need to update the EditProps interface to reflect these changes. These properties will help us update our UI from the Edit component. In addition to this, we also need to create a new state to track the selected post id whenever the edit button is clicked.

import './App.css';
import Header from './components/Header';
import { Box, useDisclosure } from '@chakra-ui/react';
import Card from './components/Card';
import Create from './components/Create';
import { useEffect, useState } from 'react';
import Edit from './components/Edit';
import { PostType } from './models/post.interface';
import { Post } from './api/api';

function App() {
	const { isOpen, onOpen, onClose } = useDisclosure();
	const [isCreate, setIsCreate] = useState(false);
	const [isEdit, setIsEdit] = useState(false);
	const [postID, setPostID] = useState<number | null>(null) //add

	const [posts, setPosts] = useState<PostType[]>([]);
	const [isError, setIsError] = useState<boolean>(false);

	useEffect(() => {
		Post.getPosts()
			.then((data) => {
				setPosts(data);
			})
			.catch((err) => {
				setIsError(true);
			});
		return () => {};
	}, []);

	return (
		<>
			<div>
				<Header onOpen={onOpen} setIscreate={setIsCreate} />
				<Box mt='20' mx='auto' width={{ sm: '100%', lg: '80%' }}>
					{/* Modify this section */}
					{isError ? (
						<Box
							mt='1'
							fontWeight='bold'
							fontSize='xl'
							as='h6'
							isTruncated
							color='red'
						>
							Oop!!! Error getting posts
						</Box>
					) : (
						posts.map((post) => (
							<Card
								key={post.id}
								onOpen={onOpen}
								setIsEdit={setIsEdit}
								post={post}
								setPostID={setPostID}
							/>
						))
					)}
				</Box>
			</div>

			{/* Create post form */}
			{isCreate && (
				<Create
					isOpen={isOpen}
					onClose={onClose}
					setIsCreate={setIsCreate}
					posts={posts}
					setPosts={setPosts}
				/>
			)}
			{/* modify edit component with posts and setPosts */}
			{isEdit && (
				<Edit
					isOpen={isOpen}
					onClose={onClose}
					setIsEdit={setIsEdit}
					posts={posts}
					setPosts={setPosts}
					postID= {postID}
					setPostID ={setPostID}
				/>
			)}
		</>
	);
}

export default App;

We will update Card.tsx component to enable us to update the postID.

import { Box, Button, Flex, Image, Link } from '@chakra-ui/react';
import { FC } from 'react';
import { PostType } from '../models/post.interface';

interface CardProps {
	onOpen: () => void;
	setIsEdit: (state: boolean) => void;
	post: PostType;
	setPostID: (id: number) => void //add
}

const Card: FC<CardProps> = ({ onOpen, setIsEdit, post, setPostID }) => {
	return (
		<Box w='100%' borderWidth='1px' borderRadius='lg'>
			<Flex direction={{ sm: 'column', lg: 'row' }}>
				<Image
					src='https://static.thenounproject.com/png/1087123-200.png'
					alt='place holder image'
					w={{ sm: '100%', lg: '20%' }}
				/>
				<Box p='6' w={{ sm: '100%', lg: '80%' }}>
					<Box
						mt='1'
						fontWeight='bold'
						fontSize='2xl'
						as='h1'
						lineHeight='tight'
						isTruncated
					>
						{post.title}
					</Box>
					<Box mt='4'>{post.body}</Box>
					<Flex mt='10' align='center' justify='center'>
						<Button colorScheme='red' variant='outline' mr='4'>
							Delete
						</Button>
						<Button
							color='white'
							borderRadius='lg'
							background='teal.500'
							_hover={{ background: 'teal' }}
							onClick={() => {
								onOpen();
								setIsEdit(true);
								setPostID(post.id!) //add
							}}
						>
							Edit
						</Button>
					</Flex>
				</Box>
			</Flex>
		</Box>
	);
};

export default Card;

PS: The bang(!) in front of setPostID(post.id!) tells the compiler to relax the non-null constraint error(Meaning the parameter cannot be null or undefined)

Next, we need to make changes below in the Edit component.

import { Box, Button, Input, Text, Textarea } from '@chakra-ui/react';
import React, { FC, useState } from 'react';
import { ModalProp } from '../models/modal.interface';
import ModalWrap from './ModalWrap';
import { PostType } from '../models/post.interface'; //add

interface EditProps extends ModalProp {
	setIsEdit: (state: boolean) => void;
	posts: PostType[]; //add
	setPosts: (updatedPost: PostType[]) => void; //add
	postID: number | null; //add
	setPostID: (id: number) => void; //add
}

const Edit: FC<EditProps> = ({ isOpen, onClose, setIsEdit, posts, setPosts, postID, setPostID }) => {
	const [value, setValue] = useState({
		title: '',
		body: '',
	});

	const handleChange = (e: React.FormEvent<EventTarget>) => {
		let target = e.target as HTMLInputElement;
		setValue({ ...value, [target.name]: target.value });
	};

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		console.log(value);
	};

	return (
		<ModalWrap
			isOpen={isOpen}
			onClose={() => {
				onClose();
				setIsEdit(false);
			}}
			title='Edit Post'
		>
			<form onSubmit={handleSubmit}>
				<Box mb='8'>
					<Text mb='8px'>Title</Text>
					<Input
						type='text'
						value={value.title}
						onChange={handleChange}
						name='title'
						required
					/>
				</Box>
				<Box mb='8'>
					<Text mb='8px'>Body</Text>
					<Textarea
						value={value.body}
						onChange={handleChange}
						name='body'
						required
					/>
				</Box>
				<Button type='submit' w='100%' colorScheme='teal' mb='8'>
					Submit
				</Button>
			</form>
		</ModalWrap>
	);
};

export default Edit;

With these changes sorted out, we can now make our API calls to get details of a single post and then edit accordingly.

import { Box, Button, Input, Text, Textarea } from '@chakra-ui/react';
import React, { FC, useEffect, useState } from 'react';
import { ModalProp } from '../models/modal.interface';
import ModalWrap from './ModalWrap';
import { PostType } from '../models/post.interface'; //add
import { Post } from '../api/api';

interface EditProps extends ModalProp {
	setIsEdit: (state: boolean) => void;
	posts: PostType[]; //add
	setPosts: (updatedPost: PostType[]) => void; //add
	postID: number | null; //add
	setPostID: (id: number) => void; //add
}

const Edit: FC<EditProps> = ({
	isOpen,
	onClose,
	setIsEdit,
	posts,
	setPosts,
	postID,
	setPostID,
}) => {
	const [value, setValue] = useState({
		title: '',
		body: '',
	});
	const [isError, setIsError] = useState<boolean>(false);

	useEffect(() => {
		Post.getAPost(postID!)
			.then((data) =>
				setValue({ ...value, title: data.title, body: data.body })
			)
			.catch((err) => setIsError(true));
		return () => {};
	}, []);

	const handleChange = (e: React.FormEvent<EventTarget>) => {
		let target = e.target as HTMLInputElement;
		setValue({ ...value, [target.name]: target.value });
	};

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		//add
		Post.updatePost(value, postID!)
			.then((data) => {
				let updatedPost = posts.filter((post) => post.id !== postID);
				setPosts([data, ...updatedPost]);
				setValue({ ...value, title: '', body: '' });
				onClose();
			})
			.then((err) => {
				setIsError(true);
			});
	};

	return (
		<ModalWrap
			isOpen={isOpen}
			onClose={() => {
				onClose();
				setIsEdit(false);
			}}
			title='Edit Post'
		>
			{/* Add this part */}
			{isError && (
				<Box
					mt='1'
					fontWeight='bold'
					fontSize='sm'
					as='p'
					isTruncated
					color='red'
				>
					Oop!!! Error occured.
				</Box>
			)}
			<form onSubmit={handleSubmit}>
				<Box mb='8'>
					<Text mb='8px'>Title</Text>
					<Input
						type='text'
						value={value.title}
						onChange={handleChange}
						name='title'
						required
					/>
				</Box>
				<Box mb='8'>
					<Text mb='8px'>Body</Text>
					<Textarea
						value={value.body}
						onChange={handleChange}
						name='body'
						required
					/>
				</Box>
				<Button type='submit' w='100%' colorScheme='teal' mb='8'>
					Submit
				</Button>
			</form>
		</ModalWrap>
	);
};

export default Edit;

Let’s explain this code a bit.

  • We made an API call to our endpoint to get that particular post and update the form title and body respectively.

  • We updated the **handleSubmit **function by making API calls using **Post.updatePost() **function, update the states, and close the modal, accordingly.

Delete a Post

To delete a post, we need to update Card.tsx component with handleDelete function and make an API call to our delete endpoint.

We also need to update Card.tsx and App.tsxcomponent with Posts and setPosts state. This will allow us to update the application state accordingly.

import './App.css';
import Header from './components/Header';
import { Box, useDisclosure } from '@chakra-ui/react';
import Card from './components/Card';
import Create from './components/Create';
import { useEffect, useState } from 'react';
import Edit from './components/Edit';
import { PostType } from './models/post.interface';
import { Post } from './api/api';

function App() {
	const { isOpen, onOpen, onClose } = useDisclosure();
	const [isCreate, setIsCreate] = useState(false);
	const [isEdit, setIsEdit] = useState(false);
	const [postID, setPostID] = useState<number | null>(null)

	const [posts, setPosts] = useState<PostType[]>([]);
	const [isError, setIsError] = useState<boolean>(false);

	useEffect(() => {
		Post.getPosts()
			.then((data) => {
				setPosts(data);
			})
			.catch((err) => {
				setIsError(true);
			});
		return () => {};
	}, []);

	return (
		<>
			<div>
				<Header onOpen={onOpen} setIscreate={setIsCreate} />
				<Box mt='20' mx='auto' width={{ sm: '100%', lg: '80%' }}>
					{isError ? (
						<Box
							mt='1'
							fontWeight='bold'
							fontSize='xl'
							as='h6'
							isTruncated
							color='red'
						>
							Oop!!! Error getting posts
						</Box>
					) : (
						//Modify this section 
						posts.map((post) => (
							<Card
								key={post.id}
								onOpen={onOpen}
								setIsEdit={setIsEdit}
								post={post}
								setPostID={setPostID}
								posts={posts} //add
								setPosts={setPosts} //add
							/>
						))
					)}
				</Box>
			</div>

			{isCreate && (
				<Create
					isOpen={isOpen}
					onClose={onClose}
					setIsCreate={setIsCreate}
					posts={posts}
					setPosts={setPosts}
				/>
			)}
			{/* modify edit component with posts and setPosts */}
			{isEdit && (
				<Edit
					isOpen={isOpen}
					onClose={onClose}
					setIsEdit={setIsEdit}
					posts={posts}
					setPosts={setPosts}
					postID= {postID}
					setPostID ={setPostID}
				/>
			)}
		</>
	);
}

export default App;

We also need to modify Card.tsxand make API calls to delete posts

import { Box, Button, Flex, Image, Link } from '@chakra-ui/react';
import { FC, useState } from 'react';
import { Post } from '../api/api';
import { PostType } from '../models/post.interface';

interface CardProps {
	onOpen: () => void;
	setIsEdit: (state: boolean) => void;
	post: PostType;
	setPostID: (id: number) => void;
	posts: PostType[]; //add
	setPosts: (updatedPost: PostType[]) => void; //add
}

const Card: FC<CardProps> = ({
	onOpen,
	setIsEdit,
	post,
	setPostID,
	posts,
	setPosts,
}) => {
	//add
	const [isError, setIsError] = useState<boolean>(false);

	const handleDelete = (id: number) => {
		Post.deletePost(id)
			.then((data) => {
				let updatedPost = posts.filter((post) => post.id !== id);
				setPosts(updatedPost);
			})
			.then((err) => {
				setIsError(true);
			});
	};

	return (
		<Box w='100%' borderWidth='1px' borderRadius='lg'>
			<Flex direction={{ sm: 'column', lg: 'row' }}>
				<Image
					src='https://static.thenounproject.com/png/1087123-200.png'
					alt='place holder image'
					w={{ sm: '100%', lg: '20%' }}
				/>
				<Box p='6' w={{ sm: '100%', lg: '80%' }}>
					<Box
						mt='1'
						fontWeight='bold'
						fontSize='2xl'
						as='h1'
						lineHeight='tight'
						isTruncated
					>
						{post.title}
					</Box>
					<Box mt='4'>{post.body}</Box>
					<Flex mt='10' align='center' justify='center'>
						<Button colorScheme='red' variant='outline' mr='4' onClick={() => handleDelete(post.id!)}>
							Delete
						</Button>
						<Button
							color='white'
							borderRadius='lg'
							background='teal.500'
							_hover={{ background: 'teal' }}
							onClick={() => {
								onOpen();
								setIsEdit(true);
								setPostID(post.id!); //add
							}}
						>
							Edit
						</Button>
					</Flex>
				</Box>
			</Flex>
		</Box>
	);
};

export default Card;

Conclusion

This approach of using axios with TypeScript makes code easily testable, reusable, and cleaner.

**References **https://github.com/axios/axios https://chakra-ui.com/ https://www.typescriptlang.org/ https://reactjs.org/



Continue Learning