Design with Code

Exercise 14

Fetch Real Data from an API

Fetch real weather data from an external API using server actions, async/await, and Promise.all.

What you'll learn

Key takeaways from this exercise

  • Fetching data from external APIs using fetch() and async/await
  • Creating server actions with the "use server" directive
  • Defining TypeScript interfaces for API response data
  • Transforming and formatting API data for display
  • Using Promise.all to fetch data for multiple locations in parallel
  • Passing React components as props for dynamic icons

Introduction

Your weather app has pages, routes, and components — but every location shows the same placeholder data. It's time to connect to a real weather service and display actual, live weather for real cities. This is where the app goes from a static prototype to something genuinely useful.

What is an API?

We already mentioned APIs before. API stands for Application Programming Interface, and it's how programs communicate with each other. Think of it like a waiter in a restaurant: your app makes a request ("give me the weather for London"), the API processes it, and sends back a response with the data. You don't need to collect weather data yourself or build a weather station. Someone else has done that work, and they offer an API so you can access it.

JSON

The data that APIs send back is usually in JSON format — JavaScript Object Notation. JSON looks almost identical to JavaScript objects: { "temperature": 22, "description": "Partly cloudy" }. It's the standard format for data exchange on the web, and since it maps so naturally to JavaScript, it's easy to work with.

We'll use the Open-Meteo API for weather data. It's completely free, requires no registration or API key, and provides accurate weather data worldwide. Not all APIs are this easy — many require authentication, but this one lets you get started immediately.

Server actions and fetch

To make API calls from your app, you'll use server actions — functions that run on the server, not in the browser. In Next.js, you mark a file with "use server" at the top, and everything in it stays on the server side. This is important for security: if an API required a key, you wouldn't want it exposed to users in their browser.

The actual request is made with the fetch() function — a built-in JavaScript tool for making HTTP requests. You pass it a URL, it contacts the API, and you get a response back. Since network requests take time, JavaScript uses async and await to handle them: async marks a function as asynchronous, and await pauses execution until the request completes. This makes asynchronous code read like regular, sequential code.

Fetching multiple locations

When you have multiple locations to fetch weather for, doing them one at a time is slow. Promise.all solves this by running all requests simultaneously. Instead of waiting for London, then Paris, then Berlin, you fire all three requests at once and wait for all of them to finish. This is significantly faster.

This approach is not the most responsible or performant for a real production app, but it's fine for getting started. Since we're using a free API plan and making multiple requests at once, you might run into rate limiting. Rate limiting is when an API restricts how many requests you can make within a certain time window, usually to control costs and prevent abusive traffic.

TypeScript interfaces define the shape of the data you expect back from the API. You'll create interfaces like CurrentWeather (with temperature, weather code, and timezone) and DailyForecast (with date, high/low temperatures, and weather code). This ensures your components always receive data in the format they expect.

Environment variables

Since we're using a free API without authentication, it works out of the box with no extra configuration. In the real world, you'll often use secrets, such as API keys, to connect to APIs that are not open to everyone. In those cases, it's important to understand environment variables. When deploying to a hosting service like Vercel, sensitive values should be stored as environment variables instead of being hardcoded in your code. They're defined in a local .env file (which is never committed to Git) and configured separately in the Vercel dashboard. This keeps your secrets safe.

Frontend and backend

The distinction between frontend and backend becomes concrete here. The frontend is what runs in the user's browser — the UI components you've been building. The backend is what runs on the server — things like API calls, database queries, and business logic. In this project, the weather API is your backend: it provides the data, and your frontend renders it. Understanding this flow — data comes from a backend source, your UI renders it — is what lets you work in real product codebases.

By the end of this exercise, your weather app will show real, live weather data for multiple locations, updated every time the page loads.

Create a free account to access the full course

In order to access full course content and track your progress, please sign in