Back to Blog
Lesson 34 of the TypeScript: Typing JavaScript with Confidence course
TypeScriptAugust 21, 20263 min read

Implementing GET Requests in TypeScript: A Practical Guide

Learn to implement type-safe GET requests in your TypeScript API client. We'll cover mapping JSON responses, ensuring data integrity, and finishing getTasks.

TypeScriptAPIFetchGETDevelopment
Wooden Scrabble tiles arranged to form the phrase 'send help' on a light pink background.

Previously in this course, we covered Query Parameters with Generics: Type-Safe API Requests, which allowed us to build dynamic, type-safe URLs. Now, we'll shift our focus to the actual data fetching layer: implementing getTasks to bridge the gap between raw HTTP responses and our internal application state.

Bridging the Gap: The GET Request Implementation

When building a production-grade API client, you cannot simply trust the data coming back from the server. Even if your Building the Task API Client: Setup and Architecture is sound, the runtime data might deviate from your expectations.

Our goal for getTasks is to use the generic request wrapper we built previously to fetch data and ensure that the response adheres to our Task interface before it ever reaches our UI components.

The Implementation Strategy

We will structure our getTasks method to handle the asynchronous call, validate the payload, and return a typed array of tasks.

TYPESCRIPT
// src/api/taskClient.ts
import { Task } from CE9178">'../models/task';
import { apiClient } from CE9178">'./requestWrapper';

export const getTasks = async (params?: Record<string, string>): Promise<Task[]> => {
  // We use our generic wrapper to handle the fetch
  const response = await apiClient.get<Task[]>(CE9178">'/tasks', { params });

  // Data Integrity: Ensure the response is an array
  if (!Array.isArray(response)) {
    throw new Error(CE9178">'Invalid response structure: Expected an array of tasks');
  }

  return response;
};

Ensuring Data Integrity on Fetch

While TypeScript provides compile-time safety, it does not exist at runtime. A common pitfall is assuming that because you've defined a Task type, the API will always return objects that match that shape.

To ensure data integrity, we perform a "runtime check." In the example above, we check if the response is an array. If your API structure is more complex, you might consider using a schema validation library like Zod, but for now, we rely on basic structural validation to prevent "undefined is not a function" errors downstream.

Hands-on Exercise: Implementing Search

Now it's your turn. Extend the getTasks function to accept a status filter, ensuring that the filter parameter is strictly typed using your existing status union.

  1. Create a TaskStatus type if you haven't already.
  2. Update the getTasks function signature to accept an optional status property.
  3. Pass this property through the apiClient correctly.

Hint: Remember that query parameters are always strings when they arrive at the API layer, so you may need to cast or map them appropriately.

Common Pitfalls to Avoid

  • Trusting the API Implicitly: Never use as Task[] to force a type assertion on the result of fetch. This hides potential runtime errors. Always validate the structure (e.g., checking if it's an array or if essential keys exist).
  • Ignoring HTTP Errors: Our wrapper should throw errors for non-200 status codes. Ensure that getTasks either handles these errors or propagates them so the UI can show a notification.
  • Over-complicating the Return Type: Stick to returning Task[] or Promise<Task[]>. Avoid returning the full Axios or Fetch response object if your UI only cares about the data.

FAQ

Q: Should I validate every single property of the Task object? A: In a small project, structural validation (checking if it's an array) is often enough. In larger enterprise apps, validation libraries like Zod or Joi are recommended to ensure every field matches the expected type at runtime.

Q: Why not use any for the response type? A: Using any defeats the purpose of the course. It allows invalid data to propagate through your app, leading to bugs that are difficult to track. Always strive for explicit types.

Recap

In this lesson, we implemented the getTasks method, ensuring that our API client not only requests data correctly but also performs basic validation on the incoming JSON. By combining our generic wrappers with runtime structural checks, we've solidified the foundation of our task manager.

Up next: Mapping API Data to Local Models, where we'll learn how to transform raw API responses into clean, domain-specific objects that are easier to work with in your UI.

Similar Posts