GraphQL + TypeScript - wygeneruj sobie typy!
- Czas potrzebny na przeczytanie:4 minuty
- Opublikowane:
Cześć 👋
Wracamy, po krótkiej przerwie do GraphQLa. Tym chciałbym Ci przybliżyć świetne narzędzie jakim jest GraphQL Code Generator. Dzięki niemu zaoszczędzimy czas i na postawie GraphQLowej schemy wygenerujemy w pełni poprawne TypeScriptowe typy, Reaktowe komponenty, czy też hooki!
Instalacja
Zacznijmy od zainstalowania generatora:
npm install --save-dev @graphql-codegen/cli
Ustawienia
Gdy instalacje mamy już za sobą, dodajmy odpowiedni skrypt do package.json
:
{
"scripts": {
"generate": "graphql-codegen"
}
}
Plik konfiguracyjny
Stwórzmy plik codegen.yml
:
touch codegen.yml
To tutaj dzieje się cała magia, możemy tutaj podać masę opcji, ale podstawowymi będą schema
, documents
i generates
.
schema
- ścieżka do Twojej schemydocuments
- ścieżka do dokumentów, które opisują zapytania, mutacje, subskrypcje i fragmentygenerates
- opisuje co ma gdzie zostać wygenerowane i z jakich pluginów ma korzystać
TypeScript
Zacznijmy od podstawowego połączenia generatora z TypeScriptem, żeby całość działała musimy zainstalować odpowiedni plugin:
npm install --save-dev @graphql-codegen/typescript
Następnie, stwórzmy scheme, która będzie opisywać użytkownika.
type Language {
name: String!
}
type User {
name: String!
age: Int!
id: ID!
languages: [Language]!
}
Super, teraz rozbudujemy nasz plik konfiguracyjny, tak abyśmy po odpaleniu komendy generate
dostali gotowe typy na podstawie powyższego schematu.
schema: './schema.graphql'
generates:
generated.ts:
plugins:
- typescript
W schema
podaliśmy ścieżkę do naszego schematu, a w generates
ustawiliśmy plik wyjściowy dla typów - generated.ts
.
Wystarczy teraz, że odpalimy komendę generate
i powinniśmy zobaczyć plik generated.ts
:
npm run generate
Ten przykład był bardzo prosty i raczej mało użyteczny, albowiem najczęściej będziemy zaciągać nasz schemat z backendu, co wtedy? Nic nie stoi nam na przeszkodzie, żeby podać po prostu do niego ścieżkę.
schema: 'http://localhost:3000/graphql'
Schematów możemy podać wiele i na różnych poziomach, na przykład, chcemy stworzyć dwa pliki z dwóch niezależnych od siebie schematów, nic trudnego!
generates:
./generated1.ts:
schema: ./schema.graphql
plugins:
- typescript
./generated1.ts:
schema: http://localhost:3000/graphql
plugins:
- typescript
Wspominałem przed chwilą o documents
czyli o plikach gdzie przechowujemy nasze zapytania, mutacje czy też fragmenty, spójrzmy na przykładzie.
Na początek, stwórzmy plik schema.graphql
z naszym schematem:
type Query {
animal(id: ID!): Animal
allAnimals: [Animal]
}
type Animal {
id: ID!
name: String!
age: Int!
}
Chcemy stworzyć zapytanie, które będzie pobierało danego zwierzaka na podstawie id
, dokładnie tak, jak podaliśmy na powyższym schemacie.
Utwórzmy plik getAnimal.graphql
query GetAnimal($animalId: ID!) {
animal(id: $animalId) {
...AnimalFields
}
}
fragment AnimalFields on Animal {
id
name
}
Zainstalujmy odpowiedni plugin i zmodyfikujmy plik konfiguracyjny:
npm install --save-dev @graphql-codegen/typescript-operations
schema: 'schema.graphql'
documents: 'getAnimal.graphql'
generates:
operations-types.ts:
plugins:
- typescript
- typescript-operations
Voilà dostaliśmy wygenerowane typy na podstawie zapytania i schematu.
React
Wspominałem na początku, że za pomocą tego narzędzia możemy wygenerować hooki, które będą w pełni otypowane, nie ma co zwlekać, bierzemy się do pracy!
Zacznijmy od zainstalowania pluginu:
npm install --save-dev @graphql-codegen/typescript-react-apollo
Nie traćmy czasu i zostawmy nasz schemat i operacje takimi jakie są, a w pliku konfiguracyjnym dodajmy zainstalowany plugin.
schema: 'schema.graphql'
documents: 'getAnimal.graphql'
generates:
operations-types.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
Jeśli wygenerujesz teraz kod i spojrzysz do wygenerowanego pliku, na samym dole zobaczysz wygenerowane hooki!
export function useGetAnimalQuery(
baseOptions?: Apollo.QueryHookOptions<GetAnimalQuery, GetAnimalQueryVariables>,
) {
return Apollo.useQuery<GetAnimalQuery, GetAnimalQueryVariables>(GetAnimalDocument, baseOptions);
}
export function useGetAnimalLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<GetAnimalQuery, GetAnimalQueryVariables>,
) {
return Apollo.useLazyQuery<GetAnimalQuery, GetAnimalQueryVariables>(
GetAnimalDocument,
baseOptions,
);
}
Dostaliśmy dwa otypowe hooki, gdybyś chciał użyć mutacji czy też subskrypcji to nie ma najmniejszego problemu, działa to na tej samej zasadzie.
Jak możemy z nich skorzystać? Dokładnie tak jak z dostarczanych przez apollo hooków useQuery
i useLazyQuery
, wystarczy je zaimportować.
import { useGetAnimalQuery } from "operations-types.ts";
const Animal = () => {
const { data } = useGetAnimalQuery(...);
};
TypedDocumentNode
Generowanie hooków jest super, ale to zawsze dużo dodatkowego kod. TypedDocumentNode
wskakuje na poziom wyżej i nie dość, że skraca nam wygenerowany kod to jeszcze dzięki niemu TypeScript będzie podpowiadał i uzupełniał typy na podstawie ustalonych wcześniej operacji!
Zamiast wygenerowanych hooków dostajemy gotowe do wykorzystania typy:
Podsumowanie
GraphQL Code Generator jest na prawdę potężnym narzędziem, które niewyobrażalnie ułatwia nam pracę z GraphQLem i TypeScriptem, odwala ono połowę brudnej roboty za nas!
Generować możemy zarówno typy dla frontendu jak i backendu. Hooki w Reakcie, composition components we Vue, komponenty w Angularze to tylko niektóre opcje, warto się przyjrzeć temu narzędziu bliżej, polecam z całego serca 💜