Distribuera GraphQL/MongoDB API med hjälp av Netlify Functions
Pawel Rybczynski
Software Engineer
Mål Initial installation Installera beroenden Låt oss börja Först lägger vi till tsconfig.json i huvudkatalogen: Låt oss nu skapa src/server.ts för implementeringar av servrar. Lägg sedan till två funktioner: en för den lokala servern och en för lambda. OK, vi har inga resolvers eller typdefinitioner så vi måste skapa några. Låt oss anta att vi till en början vill [...]
Mål
Konfigurera både lokala servrar och lambdaservrar.
Anslut båda till MongoDB.
Implementera grundläggande autentisering.
Driftsättning av serverlösa Apollo GraphQL API med Netlify.
OK, vi har inga resolvers eller typdefinitioner så vi måste skapa några. Låt oss anta att vi till en början vill skapa användare och få information om dem.
Glöm inte att importera typdefinition och resolver till servern.
// src/server.ts
importera { ApolloServer as ApolloServerLambda } från "apollo-server-lambda";
importera { ApolloServer } från "apollo-server";
import { typeDefs } från "./schemas";
import { resolvers } från "./resolvers";
{...}
Anslut med MongoDB via mongoose
Nu är det dags att skapa en anslutning till vår databas. I det här fallet kommer det att vara MongoDB. Den är gratis och lätt att underhålla. Men innan dess, låt oss installera ytterligare två beroenden:
Säkerheten först! Låt oss nu säkra våra lösenord genom att hasha dem.
npm installera --spara bcrypt @types/bcrypt
Nu implementerar du lösenordssäkerheten i återuppringningen av pre-middleware. Pre-middleware-funktioner körs en efter en när varje middleware anropar nästa. För att göra lösenorden säkra använder vi en teknik som genererar ett salt och en hash vid separata funktionsanrop.
// src/modell.ts
importera bcrypt från "bcrypt";
{...}
const SALT_WORK_FACTOR: number = 10;
UserSchema.pre("save", function (next) {
const user = detta som User;
if (!this.isModified("password")) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
bcrypt.hash(användare.lösenord, salt, function (err, hash) {
if (err) return next(err);
användarens.lösenord = hash;
nästa();
});
});
});
{...}
Lägg sedan till metoden comparePasswords i UserSchema:
Nu kan vi ordna en förbindelse mellan servrar och databaser. MONGODB_URI är en miljövariabel som innehåller en anslutningssträng som behövs för att skapa anslutningen. Du kan hämta den från din klusterpanel efter att du har loggat in på ditt MongoDB Atlas-konto. Placera den inuti .env
// .env
MONGODB_URI = ...;
Kom alltid ihåg att lägga till den filen i .gitignore. Jättebra! Låt oss nu lägga till en funktion som gör det möjligt att ansluta till db.
Kontexten är ett objekt som delas mellan alla resolvers. För att tillhandahålla det behöver vi bara lägga till en kontextinitialiseringsfunktion i ApolloServer-konstruktören. Låt oss göra det.
Om ja, öppna http://localhost:4000/ i din webbläsare.
GraphQL-lekplatsen bör visas. Låt oss skapa en ny användare!
Ta en titt på hur det ser ut i databasen.
Coolt! Allt fungerar bra!
Låt oss försöka få lite användarinformation.
Och lägg till lösenordsfältet...
Trevligt att träffas! Vi får ett felmeddelande Kan inte fråga fältet "lösenord" på typen "Användare".". Som du kan se har vi inte lagt till det här fältet i definitionen av användartypen. Det finns där med avsikt. Vi bör inte göra det möjligt att fråga efter lösenord eller andra känsliga uppgifter.
En annan sak ... Vi kan få användardata utan någon autentisering ... det är inte en bra lösning. Vi måste fixa det.
Men innan dess...
Konfigurera Codegen
Låt oss använda GraphQL kod generator för att få en kompatibel bastyp, baserad på vårt schema.
Vi behöver också ett sätt att skapa en sådan token. Det bästa sättet är att implementera en inloggningsfråga i vår resolver.
// resolvers.ts
import { Resolvers, Token, User } från "./generated/graphql";
const userResolver: Resolvers = {
Förfrågan: {
user: async (_, { id }, { models: { userModel }, auth }): Promise => {
if (!auth) throw new AuthenticationError("Du är inte autentiserad");
const user = await userModel.findById({ _id: id }).exec();
returnerar användaren;
},
login: async (
_,
{ e-post, lösenord },
{ modeller: { userModel } }
): Promise => {
const user = await userModel.findOne({ email }).exec();
if (!user) throw new AuthenticationError("Ogiltiga autentiseringsuppgifter");
const matchPasswords = bcrypt.compareSync(lösenord, användarens.lösenord);
if (!matchPasswords) throw new AuthenticationError("Ogiltiga referenser");
const token = jwt.sign({ id: user.id }, "riddlemethis", {
expiresIn: 60,
});
returnera { token };
},
},
Mutation: {
createUser: asynkron (
_,
{ e-post, namn, lösenord },
{ modeller: { userModel } }
): Promise => {
const user = await userModel.create({
e-post,
namn,
lösenord,
});
returnerar användaren;
},
},
};
Du måste också uppdatera definitionerna av användartyper för Token-typ och inloggningsfråga.
typ Token {
token: String!
}
typ Frågeställning {
user(id: ID!): Användare!
login(email: String!, password: String!): Token!
}
Låt oss nu försöka hämta användaren utan token
OK, det fungerar bra! Försök att logga in
Låt oss försöka igen att hämta användaren, men den här gången med token tillagd i rubrikerna
Coolt! Vad händer om vi anger fel inloggningsuppgifter?
Snyggt!
Förbered för driftsättning
Sista steget: distribuera serverlöst api med Netlify!
Skapa mapp lambda i huvuddirektören och lägg in två filer inuti:
Först innehåller AWS hanteraren. Den skapar en ApolloServerLambda-serverinstans och och exponera sedan en hanterare med hjälp av createHandler för den instansen.