Endpointy w 5 minut - Apollo Server i Fastify

Aleksander Patschek

Czas czytania: 3 min
Liczba słów: 431
Data: 25-05-2021
Udostępnij:

Graphql jest coraz popularniejszy i widzę go coraz częściej w rozwiązaniach produkcyjnych. Chyba jednym z najbardziej popularnych rozwiązań do implementacji tego rozwiązania na backendzie jest Apollo Server. Zaimplementowałem go w aplikacji napisanej w Fastify (klon Linktree).

Instalacja i konfiguracja Apollo Server

Standardowo, zanim będzie można cokolwiek zaimplementować, trzeba zainstalować biblioteki.

npm install apollo-server-[email protected]-v3 graphql --save

Zwracam uwagę na wersję biblioteki, jaką wykorzystałem. Jest to wersja alpha, która jest zgodna z wersją 3 Fastify, którą wykorzystuję.

Teraz można zarejestrować bibliotekę w Fastify.

import { ApolloServer } from 'apollo-server-fastify';

const server = new ApolloServer({ typeDefs, resolvers });
fastify.register(server.createHandler())

Jak widać, inicjalizuję Apollo Server przekazując dwa elementy: typeDefs i resolvers. Są to elementy, które muszę zaimplementować, by API działało.

Apollo Server - Schema & Resolvers

Dwa najważniejsze elementy przy tworzeniu API Graphql są:

  • schema - jest to opis typów w naszym API wraz z dostępnymi Queries i Mutations
  • resolvers - są to faktyczne funkcje, które będą wywoływane by obsłużyć zapytania od użytkownika

Całą pracę najlepiej zacząć od zdefiniowania Schemy - od niej będą zależały inne elementy. Dla klona Linktree potrzebuję następujące typy:

import { gql } from 'apollo-server-fastify'

export const typeDefs = gql`
    type Link {
        url: String,
        name: String,
    }

    type Query {
        links: [Link]
    }

    type Mutation {
        addLink(url: String, name: String): Link
    }
`

Więcej o budowaniu schemy możesz przeczytać u mnie na blogu - Budowanie schemy w GraphQL

Resolvers powstają z typów Query i Mutations. Definiują one jakie operacje może wykonywać użytkownik. I trzeba je zaimplementować. Query links jest proste, bo będzie zwracać tylko nasze linki. Natomiast w Mutation addLink muszę wybrać zmienne przekazane w zapytaniu i utworzyć nowy link. Dodatkowo muszę zwrócić nowo utworzony link, ponieważ w typie zdefiniowałem, że taki link jest zwracany.

const resolvers = {
  Query: {
    links: () => links,
  },
  Mutation: {
    addLink: async (_, { url, name }) => {
      links.push({ url, name });

      return { url, name }
    }
  }
};

Query i Mutation przyjmują w funkcji 4 argumenty:

  • parent - czyli wartość z poprzedniego resolvera
  • args - przekazane argumenty
  • context - globalny obiekt, wspólny dla wszystkich resolver'ów
  • info - dodatkowe informacje o operacji

Context jest o tyle ciekawy, że możemy w nim przechowywać połączenie do bazy danych. Dzięki temu w każdej funkcji mamy dostęp do danych. Pozwala to również na zrobienie szybkiego mocka np.: na potrzeby testów. Jak to wygląda? Na sam początek przeniosłem operacje do osobnego pliku.

//db.mjs

const links = []

const addLink = (link)=>{
    links.push(link);
    return link;
};

const getLinks = ()=>links;

export const db = {
    links: {addLink, getLinks}
}

Następnie przy inicjalizacji naszego serwera musimy dodać pole context.

import { db } from './db.mjs';

const server = new ApolloServer({
  typeDefs, 
	resolvers, 
	context: () => ({
    db,
  })
});

I wtedy mamy dostęp do tego w naszych funkcjach

const resolvers = {
  Query: {
    links: (_, args, {db}) => db.links.getLinks(),
  },
  Mutation: {
    addLink: async (_, { url, name }, {db}) => {
      return db.links.addLink({ url, name });
    }
  }
};

Testowanie

Na sam koniec zostawiłem kwestię testowania. Najprościej testować api Graphql przy pomocy wbudowanego Graphql Playground. W Apollo Server uruchamia się on automatycznie pod adresem http://localhost:3000/graphql (jeśli masz aplikację uruchomioną na innym porcie to użyj swojego portu).

Query jest proste do zrobienia, ponieważ wystarczy podać nazwę interesującego nas query i pola, jakie chcemy pobrać.

{
  links {
    url,
    name
  }
}

testowanie query

Mutacja jest minimalnie trudniejsza, ponieważ musimy przekazać wartości do zapytania.

mutation AddLink($url: String, $name: String) {
  addLink(url: $url, name: $name){
    name,
    url
  }
}

Dane przekazujemy w okienku niżej - Query variables(zobacz na zdjęciu poniżej).

testowanie mutacji

Co więcej, takie same Query i Mutation będziemy musieli zaimplementować na frontendzie, by pobrać dane. Ale to już w innym wpisie.

A ty co sądzisz o GraphQL? Miałeś okazję, już z tym pracować? Jak widzisz, kod stał się czytelniejszy i od razu mamy walidację danych. Ja jestem dużym zwolennikiem tego sposobu komunikacji i widzę wiele zalet.

Jeśli podoba Ci się moja twórczość, to dołącz do newslettera WebDev News. Znajdziesz tam jeszcze więcej wiedzy i ciekawych artykułów, które pomogą Ci w karierze  WebDev News - newsletter tworzony przez programistę dla programistów