Norsk

Mestre type-sikre API-kall i TypeScript for robuste, vedlikeholdbare og feilfrie webapplikasjoner. Lær beste praksiser og avanserte teknikker.

Type-sikre API-kall med TypeScript: En omfattende guide

I moderne webutvikling er interaksjon med APIer en grunnleggende oppgave. TypeScript, med sitt kraftige typesystem, gir en betydelig fordel når det gjelder å sikre påliteligheten og vedlikeholdbarheten til applikasjonene dine ved å muliggjøre type-sikre API-kall. Denne guiden vil utforske hvordan du kan utnytte TypeScript sine funksjoner for å bygge robuste og feilfrie API-interaksjoner, og dekke beste praksiser, avanserte teknikker og eksempler fra den virkelige verden.

Hvorfor typesikkerhet er viktig for API-kall

Når du arbeider med APIer, har du i hovedsak å gjøre med data som kommer fra en ekstern kilde. Disse dataene er kanskje ikke alltid i det formatet du forventer, noe som kan føre til kjøretidsfeil og uventet oppførsel. Typesikkerhet gir et viktig lag med beskyttelse ved å verifisere at dataene du mottar samsvarer med en forhåndsdefinert struktur, og fanger opp potensielle problemer tidlig i utviklingsprosessen.

Sette opp TypeScript-prosjektet ditt

Før du dykker ned i API-kall, må du sørge for at du har et TypeScript-prosjekt satt opp. Hvis du starter fra bunnen av, kan du initialisere et nytt prosjekt ved å bruke:

npm init -y
npm install typescript --save-dev
tsc --init

Dette vil opprette en `tsconfig.json`-fil med standard TypeScript-kompilatoralternativer. Du kan tilpasse disse alternativene for å passe prosjektets behov. Du kan for eksempel aktivere streng modus for strengere typekontroll:

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Definere typer for API-responser

Det første trinnet i å oppnå type-sikre API-kall er å definere TypeScript-typer som representerer strukturen til dataene du forventer å motta fra APIet. Dette gjøres vanligvis ved hjelp av `interface`- eller `type`-deklarasjoner.

Bruke grensesnitt

Grensesnitt er en kraftig måte å definere formen på et objekt. Hvis du for eksempel henter en liste over brukere fra et API, kan du definere et grensesnitt som dette:

interface User {
  id: number;
  name: string;
  email: string;
  address?: string; // Valgfri egenskap
  phone?: string; // Valgfri egenskap
  website?: string; // Valgfri egenskap
  company?: {
    name: string;
    catchPhrase: string;
    bs: string;
  };
}

`?` etter et egenskapsnavn indikerer at egenskapen er valgfri. Dette er nyttig for å håndtere API-responser der visse felt kan mangle.

Bruke typer

Typer ligner på grensesnitt, men tilbyr mer fleksibilitet, inkludert muligheten til å definere unionstyper og krysstyper. Du kan oppnå det samme resultatet som grensesnittet ovenfor ved å bruke en type:

type User = {
  id: number;
  name: string;
  email: string;
  address?: string; // Valgfri egenskap
  phone?: string; // Valgfri egenskap
  website?: string; // Valgfri egenskap
  company?: {
    name: string;
    catchPhrase: string;
    bs: string;
  };
};

For enkle objektstrukturer er grensesnitt og typer ofte utskiftbare. Typer blir imidlertid kraftigere når du har å gjøre med mer komplekse scenarier.

Gjøre API-kall med Axios

Axios er en populær HTTP-klient for å gjøre API-forespørsler i JavaScript og TypeScript. Det gir et rent og intuitivt API, noe som gjør det enkelt å håndtere forskjellige HTTP-metoder, forespørselshoder og responsdata.

Installere Axios

npm install axios

Gjøre et typet API-kall

For å gjøre et type-sikkert API-kall med Axios, kan du bruke `axios.get`-metoden og spesifisere forventet responstype ved hjelp av generiske typer:

import axios from 'axios';

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('Feil ved henting av brukere:', error);
    throw error;
  }
}

fetchUsers().then(users => {
  users.forEach(user => {
    console.log(user.name);
  });
});

I dette eksemplet forteller `axios.get('...')` TypeScript at responsdataene forventes å være en array av `User`-objekter. Dette lar TypeScript gi typekontroll og autofullføring når du arbeider med responsdataene.

Håndtere forskjellige HTTP-metoder

Axios støtter forskjellige HTTP-metoder, inkludert `GET`, `POST`, `PUT`, `DELETE` og `PATCH`. Du kan bruke de tilsvarende metodene for å gjøre forskjellige typer API-forespørsler. For eksempel, for å opprette en ny bruker, kan du bruke `axios.post`-metoden:

async function createUser(user: Omit): Promise {
  try {
    const response = await axios.post('https://jsonplaceholder.typicode.com/users', user);
    return response.data;
  } catch (error) {
    console.error('Feil ved oppretting av bruker:', error);
    throw error;
  }
}

const newUser = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  address: '123 Main St',
  phone: '555-1234',
  website: 'example.com',
  company: {
    name: 'Example Corp',
    catchPhrase: 'Leading the way',
    bs: 'Innovative solutions'
  }
};

createUser(newUser).then(user => {
  console.log('Opprettet bruker:', user);
});

I dette eksemplet oppretter `Omit` en type som er den samme som `User`, men uten `id`-egenskapen. Dette er nyttig fordi `id` vanligvis genereres av serveren når du oppretter en ny bruker.

Bruke Fetch API

Fetch API er et innebygd JavaScript API for å gjøre HTTP-forespørsler. Selv om det er mer grunnleggende enn Axios, kan det også brukes med TypeScript for å oppnå type-sikre API-kall. Du foretrekker det kanskje for å unngå å legge til en avhengighet hvis det passer dine behov.

Gjøre et typet API-kall med Fetch

For å gjøre et type-sikkert API-kall med Fetch, kan du bruke `fetch`-funksjonen og deretter parse responsen som JSON, og spesifisere den forventede responstypen:

async function fetchUsers(): Promise {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data: User[] = await response.json();
    return data;
  } catch (error) {
    console.error('Feil ved henting av brukere:', error);
    throw error;
  }
}

fetchUsers().then(users => {
  users.forEach(user => {
    console.log(user.name);
  });
});

I dette eksemplet forteller `const data: User[] = await response.json();` TypeScript at responsdataene skal behandles som en array av `User`-objekter. Dette lar TypeScript utføre typekontroll og autofullføring.

Håndtere forskjellige HTTP-metoder med Fetch

For å gjøre forskjellige typer API-forespørsler med Fetch, kan du bruke `fetch`-funksjonen med forskjellige alternativer, for eksempel `method`- og `body`-alternativene. For eksempel, for å opprette en ny bruker, kan du bruke følgende kode:

async function createUser(user: Omit): Promise {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(user)
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data: User = await response.json();
    return data;
  } catch (error) {
    console.error('Feil ved oppretting av bruker:', error);
    throw error;
  }
}

const newUser = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  address: '123 Main St',
  phone: '555-1234',
  website: 'example.com',
  company: {
    name: 'Example Corp',
    catchPhrase: 'Leading the way',
    bs: 'Innovative solutions'
  }
};

createUser(newUser).then(user => {
  console.log('Opprettet bruker:', user);
});

Håndtere API-feil

Feilhåndtering er et kritisk aspekt ved API-kall. APIer kan mislykkes av mange årsaker, inkludert problemer med nettverkstilkobling, serverfeil og ugyldige forespørsler. Det er viktig å håndtere disse feilene på en elegant måte for å forhindre at applikasjonen din krasjer eller viser uventet oppførsel.

Bruke Try-Catch-blokker

Den vanligste måten å håndtere feil i asynkron kode er å bruke try-catch-blokker. Dette lar deg fange opp eventuelle unntak som kastes under API-kallet og håndtere dem på riktig måte.

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('Feil ved henting av brukere:', error);
    // Håndter feilen, f.eks. vis en feilmelding til brukeren
    throw error; // Kast feilen på nytt for å tillate at kallende kode også kan håndtere den
  }
}

Håndtere spesifikke feilkoder

APIer returnerer ofte spesifikke feilkoder for å indikere hvilken type feil som oppstod. Du kan bruke disse feilkodene til å gi mer spesifikk feilhåndtering. For eksempel kan det hende du vil vise en annen feilmelding for en 404 Not Found-feil enn for en 500 Internal Server Error.

async function fetchUser(id: number): Promise {
  try {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      console.log(`Bruker med ID ${id} ble ikke funnet.`);
      return null; // Eller kast en tilpasset feil
    } else {
      console.error('Feil ved henting av bruker:', error);
      throw error;
    }
  }
}

fetchUser(123).then(user => {
  if (user) {
    console.log('Bruker:', user);
  } else {
    console.log('Bruker ble ikke funnet.');
  }
});

Opprette tilpassede feiltyper

For mer komplekse feilhåndteringsscenarier kan du opprette tilpassede feiltyper for å representere forskjellige typer API-feil. Dette lar deg gi mer strukturert feilinformasjon og håndtere feil mer effektivt.

class ApiError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
    this.name = 'ApiError';
  }
}

async function fetchUser(id: number): Promise {
  try {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      throw new ApiError(404, `Bruker med ID ${id} ble ikke funnet.`);
    } else {
      console.error('Feil ved henting av bruker:', error);
      throw new ApiError(500, 'Intern serverfeil'); // Eller en annen passende statuskode
    }
  }
}

fetchUser(123).catch(error => {
  if (error instanceof ApiError) {
    console.error(`API-feil: ${error.statusCode} - ${error.message}`);
  } else {
    console.error('En uventet feil oppstod:', error);
  }
});

Datavalidering

Selv med TypeScript sitt typesystem er det avgjørende å validere dataene du mottar fra APIer ved kjøretid. APIer kan endre responsstrukturen sin uten varsel, og TypeScript-typene dine er kanskje ikke alltid perfekt synkronisert med APIets faktiske respons.

Bruke Zod for kjøretidsvalidering

Zod er et populært TypeScript-bibliotek for kjøretidsdatavalidering. Det lar deg definere skjemaer som beskriver den forventede strukturen til dataene dine, og deretter validere dataene mot disse skjemaene ved kjøretid.

Installere Zod

npm install zod

Validere API-responser med Zod

For å validere API-responser med Zod, kan du definere et Zod-skjema som tilsvarer TypeScript-typen din, og deretter bruke `parse`-metoden for å validere dataene.

import { z } from 'zod';

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  address: z.string().optional(),
  phone: z.string().optional(),
  website: z.string().optional(),
  company: z.object({
    name: z.string(),
    catchPhrase: z.string(),
    bs: z.string(),
  }).optional(),
});

type User = z.infer;

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    const data = z.array(userSchema).parse(response.data);
    return data;
  } catch (error) {
    console.error('Feil ved henting av brukere:', error);
    throw error;
  }
}

I dette eksemplet validerer `z.array(userSchema).parse(response.data)` at responsdataene er en array av objekter som samsvarer med `userSchema`. Hvis dataene ikke samsvarer med skjemaet, vil Zod kaste en feil, som du deretter kan håndtere på riktig måte.

Avanserte teknikker

Bruke generiske typer for gjenbrukbare API-funksjoner

Generiske typer lar deg skrive gjenbrukbare API-funksjoner som kan håndtere forskjellige typer data. For eksempel kan du opprette en generisk `fetchData`-funksjon som kan hente data fra et hvilket som helst API-endepunkt og returnere dem med riktig type.

async function fetchData(url: string): Promise {
  try {
    const response = await axios.get(url);
    return response.data;
  } catch (error) {
    console.error(`Feil ved henting av data fra ${url}:`, error);
    throw error;
  }
}

// Bruk
fetchData('https://jsonplaceholder.typicode.com/users').then(users => {
  console.log('Brukere:', users);
});

fetchData<{ title: string; body: string }>('https://jsonplaceholder.typicode.com/todos/1').then(todo => {
    console.log('Todo', todo)
});

Bruke interceptors for global feilhåndtering

Axios tilbyr interceptors som lar deg fange opp forespørsler og responser før de håndteres av koden din. Du kan bruke interceptors til å implementere global feilhåndtering, for eksempel å logge feil eller vise feilmeldinger til brukeren.

axios.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error('Global feilhåndterer:', error);
    // Vis en feilmelding til brukeren
    return Promise.reject(error);
  }
);

Bruke miljøvariabler for API-URLer

For å unngå å hardkode API-URLer i koden din, kan du bruke miljøvariabler til å lagre URLene. Dette gjør det enklere å konfigurere applikasjonen din for forskjellige miljøer, for eksempel utvikling, testing og produksjon.

Eksempel ved bruk av `.env`-fil og `dotenv`-pakke.

// .env
API_URL=https://api.example.com
// Installer dotenv
npm install dotenv
// Importer og konfigurer dotenv
import * as dotenv from 'dotenv'
dotenv.config()

const apiUrl = process.env.API_URL || 'http://localhost:3000'; // provide a default value

async function fetchData(endpoint: string): Promise {
  try {
    const response = await axios.get(`${apiUrl}/${endpoint}`);
    return response.data;
  } catch (error) {
    console.error(`Feil ved henting av data fra ${apiUrl}/${endpoint}:`, error);
    throw error;
  }
}

Konklusjon

Type-sikre API-kall er avgjørende for å bygge robuste, vedlikeholdbare og feilfrie webapplikasjoner. TypeScript gir kraftige funksjoner som lar deg definere typer for API-responser, validere data ved kjøretid og håndtere feil på en elegant måte. Ved å følge beste praksiser og teknikker som er skissert i denne guiden, kan du forbedre kvaliteten og påliteligheten til API-interaksjonene dine betydelig.

Ved å bruke TypeScript og biblioteker som Axios og Zod, kan du sikre at API-kallene dine er type-sikre, dataene dine er validert og feilene dine håndteres på en elegant måte. Dette vil føre til mer robuste og vedlikeholdbare applikasjoner.

Husk å alltid validere dataene dine ved kjøretid, selv med TypeScript sitt typesystem. APIer kan endres, og typene dine er kanskje ikke alltid perfekt synkronisert med APIets faktiske respons. Ved å validere dataene dine ved kjøretid kan du fange opp potensielle problemer før de forårsaker problemer i applikasjonen din.

God koding!

Type-sikre API-kall med TypeScript: En omfattende guide | MLOG