Distribuere GraphQL/MongoDB API ved hjelp av Netlify-funksjoner
Pawel Rybczynski
Software Engineer
Mål Innledende oppsett Installer avhengigheter La oss starte Først legger vi til tsconfig.json i hovedkatalogen: Nå oppretter vi src/server.ts for serverimplementeringer. Legg deretter til to funksjoner: en for lokal server og en for lambda. OK, vi har ingen resolvere eller typedefinisjoner, så vi må lage noen. La oss anta at vi først ønsker [...]
Målsettinger
Konfigurer både lokale servere og lambda-servere.
Koble begge til MongoDB.
Implementer grunnleggende autentisering.
Distribuere serverløse Apollo GraphQL API med Netlify.
OK, vi har ingen resolvere eller typedefinisjoner, så vi må lage noen. La oss anta at vi i første omgang ønsker å opprette brukere og motta informasjon om dem.
Ikke glem å importere typedefinisjon og resolver til serveren.
// src/server.ts
import { ApolloServer as ApolloServerLambda } fra "apollo-server-lambda";
import { ApolloServer } fra "apollo-server";
import { typeDefs } fra "./schemas";
import { resolvers } fra "./resolvers";
{...}
Koble til MongoDB via mongoose
Nå er det på tide å opprette en forbindelse til databasen vår. I dette tilfellet vil det være MongoDB. Den er gratis og enkel å vedlikeholde. Men før det, la oss installere to avhengigheter til:
Sikkerhet først! La oss nå sikre passordene våre ved å hashe dem.
npm install --save bcrypt @types/bcrypt
Nå implementerer du passordsikkerheten i tilbakekallingen før mellomvare. Pre-middleware-funksjoner kjøres etter hverandre, når hver mellomvare kaller neste gang. For å sikre passordene bruker vi en teknikk som genererer et salt og en hash ved separate funksjonsanrop.
// src/model.ts
importer bcrypt fra "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);
bruker.passord = hash;
neste();
});
});
});
{...}
Legg deretter til metoden comparePasswords i 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);
});
};
{...}
Nå kan vi opprette en forbindelse mellom servere og databaser. MONGODB_URI er en miljøvariabel som inneholder en tilkoblingsstreng som trengs for å opprette tilkoblingen. Du kan hente den fra klyngepanelet etter at du har logget inn på MongoDB Atlas-kontoen din. Sett den inn i .env
// .env
MONGODB_URI = ...;
Husk alltid å legge til filen i .gitignore. Flott! La oss nå legge til en funksjon som gjør det mulig å koble til db.
Konteksten er et objekt som deles på tvers av alle resolvere. For å tilby det trenger vi bare å legge til en kontekstinitialiseringsfunksjon i ApolloServer-konstruktøren. La oss gjøre det.
Hvis ja, åpner du http://localhost:4000/ i nettleseren din.
GraphQL-lekeplassen bør vises. La oss opprette en ny bruker!
Ta en titt på hvordan det ser ut i databasen.
Kult! Alt fungerer fint!
La oss prøve å få litt brukerinformasjon.
Og legg til passordfeltet...
Bra! Vi mottar feil Kan ikke søke i feltet "password" på typen "User".". Som du kan se, har vi ikke lagt til dette feltet i brukertypedefinisjonen. Det er der med vilje. Vi skal ikke gjøre det mulig å spørre etter passord eller andre sensitive data.
En annen ting ... Vi kan få brukerdata uten autentisering ... det er ikke en god løsning. Vi må fikse det.
Men før...
Konfigurere Codegen
La oss bruke GraphQL kode generatoren for å få en kompatibel basistype, basert på skjemaet vårt.
Opprett checkAuth funksjon for å verifisere om tokenet er gyldig. Vi legger til resultatet i konteksten, slik at vi kan få tilgang til det i resolverne.
Vi trenger også en måte å opprette et slikt token på. Den beste måten er å implementere et påloggingsspørsmål i resolveren vår.
// resolvers.ts
import { Resolvers, Token, User } fra "./generated/graphql";
const userResolver: Resolvers = {
Query: {
user: async (_, { id }, { models: { userModel }, auth }): Promise => {
if (!auth) throw new AuthenticationError("Du er ikke autentisert");
const user = await userModel.findById({ _id: id }).exec();
return user;
},
login: async (
_,
{ e-post, passord },
{ models: { userModel } }
): Promise => {
const user = await userModel.findOne({ email }).exec();
if (!user) throw new AuthenticationError("Ugyldig legitimasjon");
const matchPasswords = bcrypt.compareSync(password, user.password);
if (!matchPasswords) kast new AuthenticationError("Ugyldig legitimasjon");
const token = jwt.sign({ id: user.id }, "riddlemethis", {
expiresIn: 60,
});
return { token };
},
},
Mutasjon: {
createUser: async (
_,
{ e-post, navn, passord },
{ models: { userModel } }
): Promise => {
const user = await userModel.create({
e-post,
name,
passord,
});
return user;
},
},
};
Du må også oppdatere brukertypedefinisjonene etter Token-type og påloggingsspørsmål.
type Token {
token: String!
}
type Query {
user(id: ID!): User!
login(e-post: String!, passord: String!): Token!
}
La oss nå prøve å hente brukeren uten token
OK, fungerer fint! Prøv å logge inn
La oss prøve igjen for å hente brukeren, men denne gangen med token lagt til i overskriftene
Kult! Og hva om vi angir feil legitimasjon?
Fint!
Gjør deg klar til å distribuere
Siste trinn: distribuer serverløs api med Netlify!
Opprett mappe lambda i hoveddirectoren og legg inn to filer:
Først inneholder AWS-håndteringen. Den oppretter en ApolloServerLambda-serverinstans og og deretter eksponere en handler ved hjelp av createHandler for den instansen.