Implementer GraphQL/MongoDB API ved hjælp af Netlify-funktioner
Pawel Rybczynski
Software Engineer
Mål Indledende opsætning Installer afhængigheder Lad os starte Først skal du tilføje tsconfig.json til hovedmappen: Lad os nu oprette src/server.ts til serverimplementeringer. Tilføj derefter to funktioner: en til den lokale server og en anden til lambda. OK, vi har ingen resolvere eller typedefinitioner, så vi er nødt til at oprette nogle. Lad os antage, at vi i første omgang ønsker [...]
Mål
Konfigurer både lokale og lambda-servere.
Forbind begge til MongoDB.
Implementer grundlæggende godkendelse.
Implementer serverløs Apollo GraphQL API med Netlify.
OK, vi har ingen resolvere eller typedefinitioner, så vi er nødt til at skabe nogle. Lad os antage, at vi i første omgang ønsker at oprette brugere og modtage oplysninger om dem.
Glem ikke at importere typedefinition og resolver til serveren.
// src/server.ts
import { ApolloServer as ApolloServerLambda } from "apollo-server-lambda";
import { ApolloServer } fra "apollo-server";
import { typeDefs } fra "./schemas";
import { resolvers } fra "./resolvers";
{...}
Opret forbindelse til MongoDB via mongoose
Nu er det et godt tidspunkt at oprette en forbindelse til vores database. I dette tilfælde vil det være MongoDB. Den er gratis og nem at vedligeholde. Men inden da skal vi installere yderligere to afhængigheder:
Sikkerhed først! Lad os nu sikre vores adgangskoder ved at hashe dem.
npm install --save bcrypt @types/bcrypt
Nu skal du implementere password-sikkerheden i pre-middleware-kaldet. Pre-middleware-funktioner udføres en efter en, når hver middleware kalder næste gang. For at gøre adgangskoderne sikre bruger vi en teknik, der genererer et salt og en hash ved separate funktionskald.
// src/model.ts
import 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);
bruger.adgangskode = hash;
næste();
});
});
});
{...}
Tilføj derefter metoden comparePasswords til 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) => {
hvis (err) {
return cb(err, null);
}
cb(null, isMatch);
});
};
{...}
Nu kan vi etablere en forbindelse mellem servere og databaser. MONGODB_URI er en miljøvariabel, som indeholder en forbindelsesstreng, der er nødvendig for at oprette forbindelsen. Du kan få den fra dit klyngepanel, når du har logget ind på din MongoDB atlas-konto. Sæt den ind i .env
// .env
MONGODB_URI = ...;
Husk altid at tilføje filen til .gitignore. Super! Lad os nu tilføje en funktion, der gør det muligt at oprette forbindelse til db.
Konteksten er et objekt, som deles på tværs af alle resolvere. For at tilvejebringe det skal vi bare tilføje en kontekstinitialiseringsfunktion til ApolloServer-konstruktøren. Lad os gøre det.
Hvis ja, skal du åbne http://localhost:4000/ inde i din browser.
GraphQL-legepladsen burde dukke op. Lad os oprette en ny bruger!
Se, hvordan det ser ud i databasen.
Fedt! Alt fungerer fint!
Lad os prøve at få nogle brugeroplysninger.
Og tilføj adgangskodefeltet...
Flot! Vi modtager en fejl Kan ikke forespørge på feltet "password" på typen "User".". Som du kan se, har vi ikke tilføjet dette felt i brugertypedefinitionen. Det er der med vilje. Vi bør ikke gøre det muligt at forespørge på en adgangskode eller andre følsomme data.
En anden ting ... Vi kan få brugerdata uden nogen form for godkendelse ... det er ikke en god løsning. Vi er nødt til at fikse det.
Men inden...
Konfigurer Codegen
Lad os bruge GraphQL Kode generator for at få en kompatibel basistype baseret på vores skema.
Vi har også brug for en måde at oprette et sådant token på. Den bedste måde er at implementere en login-forespørgsel i vores resolver.
// resolvers.ts
import { Resolvers, Token, User } fra "./generated/graphql";
const userResolver: Resolvers = {
Forespørgsel: {
user: async (_, { id }, { models: { userModel }, auth }): Promise => {
if (!auth) throw new AuthenticationError("Du er ikke autentificeret");
const user = await userModel.findById({ _id: id }).exec();
returnerer bruger;
},
login: async (
_,
{ email, password },
{ models: { userModel } }
): Promise => {
const user = await userModel.findOne({ email }).exec();
if (!user) throw new AuthenticationError("Ugyldige legitimationsoplysninger");
const matchPasswords = bcrypt.compareSync(password, user.password);
if (!matchPasswords) throw new AuthenticationError("Ugyldige legitimationsoplysninger");
const token = jwt.sign({ id: user.id }, "riddlemethis", {
expiresIn: 60,
});
return { token };
},
},
Mutation: {
createUser: async (
_,
{ e-mail, navn, adgangskode },
{ models: { userModel } }
): Promise => {
const user = await userModel.create({
email,
name,
password,
});
returnerer bruger;
},
},
};
Du skal også opdatere brugertypedefinitioner efter Token-type og login-forespørgsel.
type Token {
token: String!
}
type Forespørgsel {
user(id: ID!): Bruger!
login(email: String!, password: String!): Token!
}
Lad os nu prøve at hente brugeren uden token
OK, det virker fint! Prøv at logge ind
Lad os prøve igen at hente brugeren, men denne gang med token tilføjet til overskrifterne
Fedt nok! Og hvad hvis vi indstiller forkerte legitimationsoplysninger?
Flot!
Gør klar til udrulning
Sidste trin: Implementer serverløs api med Netlify!
Opret mappe lambda i main dir, og læg to filer ind:
Den første indeholder en AWS-handler. Den opretter en ApolloServerLambda-serverinstans og og derefter eksponere en handler ved hjælp af createHandler for denne instans.