Step 2
Connecting to our API
We have a Book component, but it does not do anything. In fact, we do not have any data to display. Luckily, in our previous workshop, we created a Node JS bookstore API, which we will use as our backend API for this workshop. You can get the final version here:
$ git clone -b deploy https://github.com/PurdueHackers/NodeJSWorkshop
$ cd NodeJSWorkshop
$ npm install
$ npm start
If you open localhost:5000, it should take you to the API documentation. If you change the scheme from "HTTPS" to "HTTP", then you can click through the documentation and try it out. There are 2 versions of the API. Version 1 has no authentication and version 2 has authentication implemented on some of the routes. To switch versions, put "/v1" or "/v2" before "/api". For example, to use version 1, you can make a request to http://localhost:5000/v1/api/books. For the purposes of this workshop, we will not be implementing authentication, so we will be using version 1.
Now that we have our API, fetch some data and display it in our application.
Let's add some application config
src/config/index.ts
export const CONFIG = {
NODE_ENV: process.env.NODE_ENV || 'development',
SERVER_URL: process.env.REACT_APP_SERVER_URL || 'http://localhost:5000/v1'
};
If you want to change the defaults, you can create a ".env" file in the root of the project and provide environment variables, like:
REACT_APP_SERVER_URL=http://www.some-url.com
Let's first create a Book type
src/types/index.d.ts
export interface IBook {
id: number;
title: string;
author: string;
createdAt: string;
updatedAt: string;
}
It is convention to put a capital "I" in front of interfaces in Typescript.
We are going to use a popular HTTP request library called Axios
src/actions/index.ts
import axios from 'axios';
import { CONFIG } from '../config';
import { IBook } from '../types';
const { SERVER_URL } = CONFIG;
const api = axios.create({
baseURL: SERVER_URL
});
export const fetchBooks = async (): Promise<IBook[]> => {
try {
const { data } = await api.get('/api/books');
return data.response;
} catch (error) {
throw error.response.data.error;
}
};
export const fetchBook = async (id: number | string): Promise<IBook> => {
try {
const { data } = await api.get(`/api/books/${id}`);
return data.response;
} catch (error) {
throw error.response.data.error;
}
};
Here, we are creating an HTTP client called "api" and specifying the base URL so we don't have to write the whole URL every time. Then, we declare 2 helper functions to fetch data from the server, fetching all books or an individual book. The "fetchBook" function will only accept a number or a string, so if you pass in a boolean, the application won't compile. Axios will return an object with metadata about the response, but we don't care about that, so we wan't the data part. The API returns data in the form of a Javascript Object with a "response" field, and since we only want that, we have a nested destructure. If there is an error, we want to throw an error up to whatever function called it, and since the API sends a string in a field called "error", that's what we throw. Each function returns a promise, which will resolve to a single or multiple books.
Last updated
Was this helpful?