Wdrażanie API GraphQL/MongoDB przy użyciu funkcji Netlify
Paweł Rybczyński
Software Engineer
Cele Początkowa konfiguracja Zainstaluj zależności Zacznijmy Najpierw dodaj tsconfig.json do katalogu głównego: Teraz stwórzmy src/server.ts dla implementacji serwerów. Następnie dodajmy dwie funkcje: jedną dla serwera lokalnego i drugą dla lambdy. OK, nie mamy żadnych resolverów ani definicji typów, więc musimy je stworzyć. Załóżmy, że na początku chcemy [...]
Cele
Skonfiguruj zarówno serwer lokalny, jak i serwer lambda.
Podłącz oba do MongoDB.
Wdrożenie podstawowego uwierzytelniania.
Wdrożenie bezserwerowego Apollo GraphQL API z Netlify.
OK, nie mamy żadnych resolverów ani definicji typów, więc musimy je stworzyć. Załóżmy, że na początku chcemy tworzyć użytkowników i otrzymywać informacje o nich.
Nie zapomnij zaimportować definicji typu i resolvera na serwer.
// src/server.ts
import { ApolloServer as ApolloServerLambda } from "apollo-server-lambda";
import { ApolloServer } z "apollo-server";
import { typeDefs } from "./schemas";
import { resolvers } from "./resolvers";
{...}
Połączenie z MongoDB za pośrednictwem mongoose
Teraz jest dobry moment na utworzenie połączenia z naszą bazą danych. W tym konkretnym przypadku będzie to MongoDB. Jest darmowa i łatwa w utrzymaniu. Ale zanim to nastąpi, zainstalujmy jeszcze dwie zależności:
npm install --save mongoose dotenv
Pierwszym krokiem jest utworzenie modelu użytkownika.
Bezpieczeństwo przede wszystkim! Zabezpieczmy teraz nasze hasła poprzez ich haszowanie.
npm install --save bcrypt @types/bcrypt
Teraz zaimplementuj zabezpieczenie hasłem wewnątrz wywołania zwrotnego pre-middleware. Funkcje pre-middleware są wykonywane jedna po drugiej, gdy każde oprogramowanie pośredniczące wywołuje następne. Aby hasła były bezpieczne, używamy techniki, która generuje sól i hash w oddzielnych wywołaniach funkcji.
// src/model.ts
import bcrypt from "bcrypt";
{...}
const SALT_WORK_FACTOR: number = 10;
UserSchema.pre("save", function (next) {
const user = this as User;
if (!this.isModified("password")) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
{...}
Następnie dodaj metodę comparePasswords do UserSchema:
// src/model.ts
{...}
UserSchema.methods.comparePasswords = function (
candidatePassword: string,
cb: (err: Error | null, same: boolean | null) => void
) {
const user = this as User;
bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
if (err) {
return cb(err, null);
}
cb(null, isMatch);
});
};
{...}
Teraz możemy zorganizować połączenie między serwerami i bazami danych. MONGODB_URI to zmienna środowiskowa, która zawiera ciąg połączenia potrzebny do utworzenia połączenia. Można go uzyskać z panelu klastra po zalogowaniu się na konto atlas MongoDB. Umieść ją wewnątrz .env
// .env
MONGODB_URI = ...;
Zawsze należy pamiętać o dodaniu tego pliku do .gitignore. Świetnie! Teraz dodajmy funkcję, która pozwoli połączyć się z db.
Kontekst jest obiektem współdzielonym przez wszystkie resolvery. Aby go udostępnić, wystarczy dodać funkcję inicjalizacji kontekstu do konstruktora ApolloServer. Zróbmy to.
Jeśli tak, otwórz http://localhost:4000/ wewnątrz przeglądarki.
Powinien pojawić się plac zabaw GraphQL. Utwórzmy nowego użytkownika!
Zobacz, jak to wygląda w bazie danych.
Super! Wszystko działa jak należy!
Spróbujmy uzyskać informacje o użytkowniku.
I dodaj pole hasła...
Nieźle! Otrzymujemy błąd Nie można wykonać zapytania w polu "hasło" dla typu "Użytkownik".". Jak możesz sprawdzić, nie dodaliśmy tego pola wewnątrz definicji typu użytkownika. Jest tam celowo. Nie powinniśmy umożliwiać zapytania o hasło lub inne wrażliwe dane.
Kolejna rzecz... Możemy uzyskać dane użytkownika bez żadnego uwierzytelniania... to nie jest dobre rozwiązanie. Musimy to naprawić.
Ale zanim...
Konfiguracja Codegen
Użyjmy GraphQL kod aby uzyskać kompatybilny typ bazowy, oparty na naszym schemacie.
Należy również zaktualizować definicje typów użytkowników według typu tokena i zapytania logowania.
typ Token {
token: String!
}
type Query {
user(id: ID!): User!
login(email: String!, password: String!): Token!
}
Spróbujmy teraz pobrać użytkownika bez tokena
OK, działa dobrze! Spróbuj się zalogować
Spróbujmy ponownie uzyskać użytkownika, ale tym razem z tokenem dodanym do nagłówków
Super! A co jeśli ustawimy złe poświadczenia?
Nieźle!
Przygotowanie do wdrożenia
Ostatni krok: wdrożenie bezserwerowego API za pomocą Netlify!
Utwórz folder lambda w katalogu głównym i umieść w nim dwa pliki:
Pierwszy zawiera obsługę AWS. Tworzy on instancję serwera ApolloServerLambda i a następnie wystawić procedurę obsługi za pomocą createHandler tej instancji.