JamStack boomt mit Tools wie Next.js und Hasura. Sie denken, nur weil Sie React kennen, heisst das, dass Sie für den Rest deines Lebens To-Do-Listen erstellen werden? Ich habe einige Neuigkeiten für Sie.
Um nicht aus dem gängigsten Webentwicklungsklischee auszubrechen, Tiere als Gegenstand eines lustigen Nebenprojekts zu verwenden, werden wir eine einfache Hundeheim-Website erstellen. Sie wird vorerst aus drei Seiten bestehen: der Startseite, der allgemeinen /Hunde-Ansicht und der Detailansicht eines einzelnen Hundes. In diesem Blog-Post werden wir API-mässig nur die Daten mit einer GraphQL-Abfrage auslesen - keine Mutationen oder Abonnements (noch).
Das allgemeine Ziel ist es, eine ordentliche Grundlage für eine SSR-React-Anwendung zu präsentieren, die Sie später für einige grössere Projekte verwenden können. Wir werden Next.js + TypeScript als UI-Framework verwenden und unsere Daten werden in einem Service namens Hasura gespeichert, von dem wir sie über eine automatisch generierte GraphQL-API abrufen werden.
Ich werde nicht zu viel Zeit darauf verwenden, irgendetwas Auffälliges zu tun, zumindest nicht am Anfang dieser Serie, wenn Sie also hier sind, um Ihre Augen zu erfreuen, kann ich Ihnen wohl nur empfehlen, zum Ende dieser Seite zu scrollen und sich dort mit einem hübschen Kerl zu treffen. Die Richtung dieser Initiative ist jedoch ganz Ihnen überlassen - wenn Sie das Gefühl haben, dass ich damit irgendwohin gehen soll, lassen Sie es mich einfach auf Twitter wissen: @pilarenko.
Sie können von mir erwarten, dass ich Sie nicht im Regen stehen lasse, mehrere Schritte im Prozess überspringe und davon ausgehe, dass Sie alles wissen, aber ich muss Sie warnen die elementaren React-, TypeScript- und GraphQL-Kenntnisse sind ratsam. Ich werde mein Bestes tun, um die Best Practices bei der Verwendung dieser Tools zu befolgen, aber Sie können gerne einen Pull Request erstellen, wenn etwas zusätzliche Arbeit benötigt. Das Repository ist hier verfügbar.
Inhalt
- Next.js + TypeScript-Anwendung einrichten
- Grundlegende Einrichtung (kann mit
npx create-next-appübersprungen werden) - Hinzufügen von TypeScript
- Hinzufügen von Routen
- Grundlegende Einrichtung (kann mit
- Hasura Backend einrichten
- Next.js + Hasura
- Umgebungsvariablen
- Vom Server zum Client
- Apollo-Konfiguration
useFetch- Daten auf der Server-Seite abrufen
- Deploy
Nützliche Links
1. Next.js + TypeScript Anwendung einrichten
1.1 Grundlegende Einrichtung
Ich habe mich entschieden, das gesamte Projekt von Grund auf neu zu starten, weil 1) ich denke, dass es lehrreich sein könnte 2) die App so einfach ist, dass es nicht notwendig ist, eine Vorlage aus dem offiziellen Next.js-Repository zu schnappen, nur um im ersten Schritt die Hälfte des Boilerplates zu pflügen. Also nehmen wir das gute alte:
npm init
Sie können “das CLI eingeben”, da die Fragen, die das CLI stellt, für unser Projekt nicht super relevant sind. Am Ende haben wir einen package.json, der dringend seine ersten Abhängigkeiten braucht. Bringen wir sie:
npm install react react-dom next
Lassen Sie uns auch eine .gitignore-Datei erstellen und wir fügen dort unseren Chunky Boi node_modules ein.
touch .gitignore
# .gitignore
/node_modules
Dann müssen wir package.json bereitstellen, um Next-Skripte hinzuzufügen, die für die Ausführung des Projekts oder die Erstellung benötigt werden.
// package.json
{
...
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
...
}
Die Struktur der Next.js Anwendung dreht sich um die Verwendung des Verzeichnisses pages für die Definition der Routen. Das bedeutet, dass jede .jsx/.tsx-Datei, unabhängig davon, wie tief sie in ihren eigenen Unterordnern verschachtelt ist, einer Unterroute entspricht. Fürs Erste erstellen wir die Homepage (”/”), die Next.js als pages/index.jsx erwartet.
mkdir pages
touch pages/index.jsx
Und lassen Sie uns das mit etwas grundlegendem React auffüllen:
// pages/index.jsx
import React from 'react'
const Index = () => {
return (
<div>
<h1>Homepage</h1>
</div>
)
}
export default Index
Hatte ich Ihnen nicht gesagt, dass es TypeScript geben würde? Keine Sorge, ich erinnere mich. Lassen Sie uns nur schnell überprüfen, ob alles wie erwartet funktioniert und wir werden es in .tsx konvertieren. Führen Sie folgendes aus:
npm run dev
1.2 Hinzufügen von TypeScript
Nun, lasst uns endlich zu TypeScript kommen und die süsse Next.js Magie nutzen. Geben Sie …
touch tsconfig.json
… ein, um eine Datei zu erstellen, die zur Angabe von Compiler-Optionen für TypeScript verwendet wird. Von diesem Moment an wird Next.js Sie durch den Rest führen. Zunächst müssen Sie sich nicht einmal um das Ausfüllen der Konfiguration kümmern, denn sobald Sie npm run dev erneut starten, wird sie automatisch erzeugt.
Dann überprüfen Sie die Konsole auf die Liste der Pakete, die zur Fertigstellung des Setups erforderlich sind. Höchstwahrscheinlich werden es die Folgenden sein:
npm install --save-dev typescript @types/react @types/node
Das erste ist der Star selbst, gefolgt von Typdefinitionen für React und Node, die wir in unserer Entwicklungsumgebung nutzen können.
Die Änderung der Erweiterung bringt an sich nichts, aber ich schlage vor, dass wir unsere schöne Liebesgeschichte zwischen React und TypeScript beginnen, indem wir den Typ von React Functional Component (React: FC) zu unserer Index Funktion ändern.
// pages/index.tsx
import React from 'react';
const Index: React.FC = () => {
return (
<div>
<h1>Homepage</h1>
</div>
);
};
export default Index;
1.3 Hinzufügen von Seiten
Nun, da wir unsere Indexseite haben, fügen wir die restlichen Unterrouten hinzu, beginnend mit /dogs.
// pages/dogs.tsx
import React from 'react';
const Dogs: React.FC = () => {
return (
<div>
<h1>D'ya like dagz?</h1>
</div>
);
};
export default Dogs;
Die Art und Weise, wie ich mir die Anwendung vorstelle, ist, den Benutzer auf die hundespezifische Detailseite umzuleiten, nachdem er auf das Miniaturfoto des Hundes auf der /dogs Seite geklickt hat. Dazu benötigen wir die besagte Hundedetailseite, die an der ID des Hundes erkannt wird. Wir erwarten, dass die URL etwa so aussieht: www.dog-shelter.com/dog/f1297fb9-1097-45be-a0a3-6ca63f5a8ed9, wobei nach dem Teil “/dog/” die UUID steht, die dynamisch ist - sie ist für jeden Hund anders. Wir können das in Next.js-Routen durch die Klammer-Syntax übersetzen, wobei wir den dynamischen Parameter in einer [Klammer] halten.
// pages/dog/[id].tsx
import React from 'react';
import { useRouter } from 'next/router';
const Dog: React.FC = () => {
const router = useRouter();
const { id } = router.query;
return (
<div>
<h2>{`Dog: ${id}`}</h2>
</div>
);
};
export default Dog;
Nun haben wir eine einfache Next.js-Anwendung mit drei Routen und einer nicht existierenden Benutzeroberfläche. Yaay. Es ist Zeit, es ein bisschen aufregender zu machen.
2. Hasura
Nichts bringt den Geist von JamStack besser zum Ausdruck als Hasura. Ich empfehle Ihnen, sich anzuschnallen, denn dank dieses Dienstes werden wir in wenigen Minuten ein vollständiges Backend mit GraphQL API erstellen.
Hasura tut Wunder, indem es Ihnen so wenig wie möglich von einer Datenbank, API und sogar etwas Backend-Logik verrät, obwohl es sicherlich einige Türen offen hält, wenn Sie daran interessiert sind, herumzuschnüffeln. Wir werden es für den Moment einfach halten, also lasst uns einfach auf den Punkt kommen.
Besuchen Sie https://cloud.hasura.io/ und loggen Sie sich mit dem Dienst Ihrer Wahl ein, dann entscheiden Sie sich für “Testen Sie eine kostenlose Datenbank mit Heroku”. Heroku mag im JamStack-Universum ein wenig in Vergessenheit geraten sein, aber hier erfüllt es definitiv seinen Zweck.
Nachdem der Dienst erfolgreich initialisiert wurde, fahren Sie fort und gehen Sie direkt zur Seite “Data”, wo wir uns endlich die Hände schmutzig machen können. Klicken Sie auf “Tabelle hinzufügen”, wo wir einige Spalten zu unserer Tabelle und damit, dank Hasura und GraphQL-Magie, zu unserer API hinzufügen können.
Lassen Sie uns damit beginnen, unserer Tabelle einen Namen zu geben (in meinem Fall: “Hunde”) und eine Reihe von Feldern hinzufügen. Ich schlage vor, den Abschnitt “Häufig verwendete Spalten” zu verwenden und id mit der UUID-Voreinstellung zu nehmen, created_at und updated_at, wobei Sie damit nach Belieben herumspielen können. Der Rest der Felder ist Ihnen überlassen, aber ich werde mich an mein “Hunde”-Thema halten und name hinzufügen, age, bio and image.
Erledigt! Und ich meine wirklich fertig - das Einzige, was Sie jetzt auf der Seite von Hasura tun können, ist mit der GraphiQL-Schnittstelle herumzuspielen, um Ihre API abzufragen.
Mit zunehmender Erfahrung mit GraphQL-APIs werden Sie mehr und mehr mit dieser Ansicht vertraut werden: lernen Sie GraphiQL kennen. Dies ist Ihr interaktiver GraphQL-Spielplatz, von dem aus Sie auch ohne umfangreiche Kenntnisse der Sprache eine GraphQL-Abfrage erstellen können.
3. Next.js + Hasura
3.1 Umgebungsvariablen
Nun, gehen wir zurück zu unserer Next.js-Anwendung.
Um in Zukunft nicht damit belästigt zu werden, fügen wir zunächst die Hasura-Endpunkt-URL als Umgebungsvariable zu unserem Projekt hinzu. Glücklicherweise hat Next.js in dieser Hinsicht massive Verbesserungen in eine der letzten Versionen, so dass es so einfach ist wie:
.env-Datei erstellen- Hinzufügen zu
.gitignore - Speichern Sie unsere URL dort und denken Sie daran, dass sie der Namenskonvention „NEXT_PUBLIC_XYZ” folgen muss, damit sie sowohl auf dem Client als auch auf dem Server zugänglich ist.
Daher sollte unsere .env wie folgt aussehen:
// .env
NEXT_PUBLIC_API_HOST=abc.hasura.app/v1/graphql
3.2 Vom Server zum Client
Next.js glänzt am meisten, wenn man seine Fähigkeiten auf der Seite des Datenabrufs richtig nutzt. Das Framework bietet Ihnen eine Reihe von Methoden, die Sie auf pages/Routenkomponenten ausführen können, die festlegen, wie dieser Teil der Anwendung aufgebaut und bedient wird.
Das perfekte Szenario für eine solche Seite wäre, wenn wir während des Builds die Anzahl und den Inhalt aller URLs unter dieser Subroute nicht kennen, da sie ständig hinzugefügt werden können. Denken Sie an eine grosse E-Commerce-Website, auf der jede Minute neue Produkte und Angebote hinzugefügt/entfernt werden können.
Unser Anwendungsfall ist einfacher - die Anzahl der Hunde im Tierheim wird sich nicht allzu sehr ändern, so dass wir in der Lage sein werden, sie statisch zu rendern.
3.3 Apollo-Konfiguration
Für den Datenabruf brauchen wir Apollo:
npm install --save @apollo/client graphql
Apollo lässt sich am besten als Brücke zwischen dem UI-Framework (diese Rolle wird in unserem Stack von React/Next.js übernommen) und GraphQL beschreiben.
Kümmern wir uns zuerst um unseren ApolloClient:
// pages/_app.tsx
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: `https://${process.env.NEXT_PUBLIC_API_HOST}`,
cache: new InMemoryCache(),
});
Wie wir sehen können, braucht es nicht viel, um zu funktionieren: nur die URL unserer Hasura Cloud GraphQL API (gespeichert in einer env var) und eine neue Instanz von InMemoryCache(), die das Caching der Anfragen für uns übernimmt.
// pages/_app.tsx
import React from 'react';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
import { AppProps } from 'next/app';
const client = new ApolloClient({
uri: `https://${process.env.NEXT_PUBLIC_API_HOST}`,
cache: new InMemoryCache(),
});
const App = ({ Component, pageProps }: AppProps) => (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
export default App;
3.4 useFetch
Jetzt können wir alle Hunde abrufen und einige grundlegende Informationen über sie anzeigen. Hier kommt uns die GraphiQL-Spielwiese zu Hilfe - wir können unsere Abfrage dort spezifizieren und sie einfach in unseren Editor kopieren, sobald sie fertig ist.
// pages/dogs.tsx
import { gql } from '@apollo/client';
const GET_DOGS = gql`
query GetDogs {
dogs {
id
name
age
bio
}
}
`;
Dann bringen wir den Hook namens useQuery ein. Dieser Hook gibt drei Dinge zurück: loading, error and data.
// pages/dogs.tsx
type DogsResponseData = {
dogs: Dog[];
};
const Dogs = () => {
const { loading, error, data } = useQuery<DogsResponseData>(GET_DOGS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<div>
<h1>Dogs:</h1>
{data.dogs.map((dog, index) => (
<ul key={`dog-${index}`}>
<li>{`name: ${dog.name}`}</li>
<li>{`age: ${dog.age}`}</li>
<li>{`bio: ${dog.bio}`}</li>
</ul>
))}
</div>
);
};
export default Dogs;
3.5 Daten auf der Server-Seite abrufen
Hier kommt der letzte Teil der Implementierung: die API auf der Server-Seite abfragen.
// pages/dogs.tsx
type ServerSideProps = NextPageContext & { apolloClient: ApolloClient<NormalizedCacheObject> };
type DogsResponse = {
dogs: Dog[];
};
Dogs.getInitialProps = async ({ apolloClient }: ServerSideProps) => {
const response = await apolloClient.query<DogsResponse>({
query: GET_DOGS,
});
return {
dogs: response.data.dogs,
};
};
export default withApollo({ ssr: true })(Dogs);
Und das war’s! Das Ergebnis ist im Grunde dasselbe, der einzige Unterschied ist, dass wir das loading nicht jedes Mal für einen Sekundenbruchteil sehen, wenn wir die Seite aufrufen. Die Daten werden gnädigerweise auf der Serverseite abgerufen und in der Komponente gerendert, als ob sie schon immer da gewesen wären.
4. Deploy
Seien Sie nicht schüchtern! Diese hochmoderne Anwendung verdient es auf jeden Fall, dass man mit ihr prahlt, also lassen Sie uns damit prahlen. Für die Bereitstellung werden wir uns ein letztes Tool schnappen, das diesen Prozess extrem angenehm machen wird: Vercel. Installieren wir seine CLI global auf unserem Rechner:
npm i -g vercel
und führen Sie diesen kleinen Befehl im Projektverzeichnis aus, um es in der Cloud bereitzustellen:
vercel
Nachdem Sie alles erfolgreich eingerichtet haben, müssen Sie noch eine letzte Sache zu Ihrer Produktionsumgebung hinzufügen: Ihre Umgebungsvariablen. Das geht ganz einfach, indem Sie im Projekt auf „Einstellungen” und dann auf die Registerkarte „Umgebungsvariablen” gehen und sie dort einfach einfügen.
Boom! Das war’s eigentlich schon. Sie haben soeben eine vollständige JamStack-Anwendung mit Routen, TypeScript, CI/CD, GraphQL-API und SSR erstellt, Sie Verrückter! Aber die Show muss auf jeden Fall weitergehen - in den nächsten Blog-Beiträgen werden wir unserem Hundeheim weitere CRUD-Operationen hinzufügen, unsere Flügel mit GraphQL & Hasura ausbreiten, komplexere Datenabrufszenarien angehen und wer weiss, vielleicht sogar eine einfache Benutzeroberfläche bauen. Vielen Dank fürs Vorbeischauen und bis zum nächsten Beitrag!