Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions country-explorer/src/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,36 @@ This list includes a handful of dependent territories alongside sovereign states

## Data fields used

| Field (from the wireframe) | API key(s) | Notes |
| -------------------------- | -------------------------- | --------------------------------------------------------------------------------------- |
| Name | `name` | |
| Flag | `flags.svg`, `flags.png` | `flag` also gives an emoji flag |
| Capital | `capital` | |
| Region | `region`, `subregion` | Used by `RegionFilter` |
| Population | `population` | |
| Area | `area` | In km² |
| Currency | `currencies` | |
| Languages | `languages` | |
|| `alpha2Code`, `alpha3Code` | Not shown in the UI; used internally as a stable id/key and for `/alpha/{code}` lookups |
| Field (from the wireframe) | API key(s) | Notes |
| -------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Name | `name` | |
| Flag | `Mapped to flagUrl, `flags.png` | `flag` also gives an emoji flag |
| Capital | `capital` | |
| Region | `region`, `subregion` | Used by `RegionFilter` |
| Population | `population` | |
| Area | `area` | In km² |
| Currency | `currencies` | |
| Languages | `languages` | |
|| `alpha2Code`, `Mapped to code for selection, favorites, and lookups` | Not shown in the UI; used internally as a stable id/key and for `/alpha/{code}` lookups |

## TypeScript models

The shared types are defined in `src/types/country.ts`:

- `CountryApiResponse` describes the API fields used for one country.
- `CountriesApiResponse` describes the array returned by the region endpoint.
- `Country` describes the internal data model used by components.

The `toCountry` function in `src/api/countries.ts` converts API data
into the internal model:

- `alpha3Code` becomes `code`, a stable country identifier.
- `flags.svg` becomes `flagUrl`.
- Currencies become strings containing the currency name and code.
- Languages become strings containing the language name.

Only fields required by the application are retained.
Favorite status is managed separately as application state.

## Error handling

Expand Down
45 changes: 10 additions & 35 deletions country-explorer/src/api/countries.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { CountriesApiResponse, Country, CountryApiResponse } from '../types/country';

const BASE_URL = 'https://countries.dev';

// Thrown for a failed request: a non-2xx response (status holds the real
Expand All @@ -12,40 +14,9 @@ export class ApiError extends Error {
}
}

// The internal shape this module returns to the rest of the app. This is a
// placeholder until #8 ("Define TypeScript Types for Country Data") lands
// its own Country type in src/types/country.ts — once it does, delete this
// and the RawCountry type below, import that one instead, and point
// `toCountry` at it. Kept local to this file (instead of touching
// src/types/country.ts) so the two issues can be worked on in parallel
// without both branches editing the same file.
export interface Country {
name: string;
capital: string;
region: string;
subregion: string;
population: number;
area: number;
flagUrl: string;
currencies: string[];
languages: string[];
}

// The relevant slice of the raw shape returned by https://countries.dev.
interface RawCountry {
name: string;
capital: string;
region: string;
subregion: string;
population: number;
area: number;
flags: { svg: string; png: string };
currencies: { code: string; name: string; symbol: string }[];
languages: { name: string }[];
}

function toCountry(raw: RawCountry): Country {
function toCountry(raw: CountryApiResponse): Country {
return {
code: raw.alpha3Code,
name: raw.name,
capital: raw.capital,
region: raw.region,
Expand Down Expand Up @@ -84,12 +55,16 @@ async function fetchJson<T>(url: string): Promise<T> {

// GET /region/{region} — every country in a region, e.g. "europe".
export async function getCountriesByRegion(region: string): Promise<Country[]> {
const raw = await fetchJson<RawCountry[]>(`${BASE_URL}/region/${encodeURIComponent(region)}`);
const raw = await fetchJson<CountriesApiResponse>(
`${BASE_URL}/region/${encodeURIComponent(region)}`,
);

return raw.map(toCountry);
}

// GET /alpha/{code} — a single country by ISO alpha-2 or alpha-3 code.
export async function getCountryByAlpha(code: string): Promise<Country> {
const raw = await fetchJson<RawCountry>(`${BASE_URL}/alpha/${encodeURIComponent(code)}`);
const raw = await fetchJson<CountryApiResponse>(`${BASE_URL}/alpha/${encodeURIComponent(code)}`);

return toCountry(raw);
}
61 changes: 25 additions & 36 deletions country-explorer/src/types/country.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,37 @@
export interface Currency {
code: string;
name: string;
symbol: string;
}

export interface Language {
// Relevant fields from a successful single-country API response.
export interface CountryApiResponse {
alpha3Code: string;
name: string;
nativeName: string;
iso639_1: string;
iso639_2: string;
capital: string;
region: string;
subregion: string;
population: number;
area: number;
flags: {
svg: string;
};
currencies: {
code: string;
name: string;
}[];
languages: {
name: string;
}[];
}

export interface Flags {
png: string;
svg: string;
}
// Successful response from /region/{region}.
export type CountriesApiResponse = CountryApiResponse[];

// Shape returned by https://countries.dev for both `/region/{region}` (as an
// array) and `/alpha/{code}` (as a single object). A few fields are missing
// on some entries (e.g. dependent territories don't have `cioc`/`gini`, and
// some don't have `borders`), so they're marked optional here.
// Internal country model used by application components.
export interface Country {
code: string;
name: string;
nativeName: string;
alpha2Code: string;
alpha3Code: string;
numericCode: string;
capital: string;
region: string;
subregion: string;
population: number;
populationDensity: number;
area: number;
currencies: Currency[];
languages: Language[];
timezones: string[];
latlng: [number, number];
demonym: string;
flag: string;
flags: Flags;
independent: boolean;
callingCodes: string[];
topLevelDomain: string[];
borders?: string[];
cioc?: string;
gini?: number;
flagUrl: string;
currencies: string[];
languages: string[];
}