Setting Up API Client

Introduction: Connecting Your Frontend and Backend

Welcome to the first lesson of this course, where we will set up the API client for your project. In modern web applications, the frontend (what users see and interact with) often needs to communicate with the backend (where data and business logic live). In our case, the frontend is built with React, and the backend uses NestJS.

To make this communication possible, we use an API client. The API client is a set of functions that help your React app send requests to the backend and handle the responses. Setting up this client is the first step in allowing your app to fetch data, show updates, and interact with users in real time.

This lesson makes your React app capable of talking to the backend by creating a small, environment-aware API client and then verifying connectivity from HomePage. We will:

  1. Define environment helpers and base URL candidates (isBrowser, isLocalhost, PORT_HOST, cachedBase, BASE_CANDIDATES) and explain them in depth.
  2. Centralize endpoint paths in a paths helper.
  3. Implement a tiny fetch-based apiClient and a hello() function that calls /api/hello.
  4. Use useEffect in HomePage to test the API status and display a user-friendly indicator.

By the end, you’ll understand exactly how URLs are chosen, why we cache the base, how the /api/hello request works, and how HomePage safely updates state after an async call.

Breaking Down the API Client Code

Let’s look at the main parts of the API client, which lives in src/api/client.ts. Here is the code:

// src/api/client.ts

export const paths = {
  hello: () => `/api/hello`,
};

export const hello = async () => {
  try {
    const res = await apiClient.get(paths.hello());
    console.log('API /api/hello response:', res);
    return res;
  } catch (err) {
    console.error('Error calling /api/hello:', err);
    throw err;
  }
};

export const apiClient = {
  get: <T = any>(path: string) => request<T>(path),
};

Let’s break this down:

  • Centralized endpoint paths: paths.hello() returns the canonical string "/api/hello". Keeping paths in one object avoids typos and makes future refactors (e.g., prefix changes) a single-edit operation.

  • Hello call flow: hello() performs apiClient.get(paths.hello()), which sends a GET request to /api/hello. It logs successes for dev visibility, throws on errors to let the caller decide how to handle failures, and returns the parsed JSON (the backend “envelope”).

  • Expected envelope: For this endpoint, the backend replies with { success: true, data: "Hello World!" }. Your UI reads success (for status) and may read data (the string) for confirmation.

  • hello is an asynchronous function. It uses await to wait for the API response.

  • It calls apiClient.get(paths.hello()), which sends a GET request to /api/hello.

  • If the request is successful, it logs the response and returns it.

  • If there is an error (for example, if the server is down), it logs the error and throws it so the calling code can handle it.

Example output in the browser console:

API /api/hello response: { success: true, message: "Hello from the backend!" }

or, if there is an error:

Error calling /api/hello: Error: Network Error

This function is a simple example of how your frontend can talk to your backend using the API client.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal