This repository has been archived by the owner on Oct 21, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: a tiny GraphQL POC served from AWS Lambda
- Loading branch information
1 parent
dae8378
commit 349da8d
Showing
11 changed files
with
10,588 additions
and
5,430 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,4 +58,11 @@ typings/ | |
.env | ||
|
||
# next.js build output | ||
.next | ||
.next | ||
|
||
# Webpack | ||
.webpack | ||
|
||
# Serverless | ||
.serverless | ||
.webpack |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const mongoose = require("mongoose"); | ||
const bluebird = require("bluebird"); | ||
mongoose.Promise = bluebird; | ||
mongoose.Promise = global.Promise; | ||
|
||
// Only reconnect if needed. State is saved and outlives a handler invocation | ||
let isConnected; | ||
|
||
const connectToDatabase = () => { | ||
if (isConnected) { | ||
console.log("Re-using existing database connection"); | ||
return Promise.resolve(); | ||
} | ||
|
||
console.log("Creating new database connection"); | ||
return mongoose.connect(process.env.MONGODB_URL).then(db => { | ||
isConnected = db.connections[0].readyState; | ||
}); | ||
}; | ||
|
||
module.exports = connectToDatabase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { graphqlLambda, graphiqlLambda } from "apollo-server-lambda"; | ||
import lambdaPlayground from "graphql-playground-middleware-lambda"; | ||
import { makeExecutableSchema } from "graphql-tools"; | ||
import { mergeResolvers, mergeTypes } from "merge-graphql-schemas"; | ||
import { userType } from "./types/user"; | ||
import { userResolver } from "./resolvers/user"; | ||
|
||
const types = mergeTypes([userType]); | ||
const solvers = mergeResolvers([userResolver]); | ||
const graphqlSchema = makeExecutableSchema({ | ||
typeDefs: types, | ||
resolvers: solvers, | ||
logger: console | ||
}); | ||
|
||
// Database connection logic lives outside of the handler for performance reasons | ||
const connectToDatabase = require("./db"); | ||
|
||
const server = require("apollo-server-lambda"); | ||
|
||
exports.graphqlHandler = function graphqlHandler(event, context, callback) { | ||
/* Cause Lambda to freeze the process and save state data after | ||
the callback is called the effect is that new handler invocations | ||
will be able to re-use the database connection. | ||
See https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html | ||
and https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs */ | ||
context.callbackWaitsForEmptyEventLoop = false; | ||
|
||
function callbackFilter(error, output) { | ||
if (!output.headers) { | ||
output.headers = {}; | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
output.headers["Access-Control-Allow-Origin"] = "*"; | ||
output.headers["Access-Control-Allow-Credentials"] = true; | ||
output.headers["Content-Type"] = "application/json"; | ||
|
||
callback(error, output); | ||
} | ||
|
||
const handler = server.graphqlLambda({ schema: graphqlSchema }); | ||
|
||
connectToDatabase() | ||
.then(() => { | ||
return handler(event, context, callbackFilter); | ||
}) | ||
.catch(err => { | ||
console.log("MongoDB connection error: ", err); | ||
// TODO: return 500? | ||
process.exit(); | ||
}); | ||
}; | ||
|
||
exports.apiHandler = lambdaPlayground({ | ||
endpoint: process.env.GRAPHQL_ENDPOINT_URL | ||
? process.env.GRAPHQL_ENDPOINT_URL | ||
: "/production/graphql" | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
const mongoose = require("mongoose"); | ||
const validator = require("validator"); | ||
|
||
const Schema = mongoose.Schema; | ||
const SchemaTypes = Schema.Types; | ||
|
||
const userSchema = new Schema({ | ||
email: { | ||
type: "string" | ||
}, | ||
newEmail: { | ||
type: "string" | ||
}, | ||
emailVerifyTTL: { | ||
type: "date" | ||
}, | ||
emailVerified: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
emailAuthLinkTTL: { | ||
type: "date" | ||
}, | ||
password: { | ||
type: "string" | ||
}, | ||
progressTimestamps: { | ||
type: "array", | ||
default: [] | ||
}, | ||
isBanned: { | ||
type: "boolean", | ||
description: "User is banned from posting to camper news", | ||
default: false | ||
}, | ||
isCheater: { | ||
type: "boolean", | ||
description: | ||
"Users who are confirmed to have broken academic honesty policy are marked as cheaters", | ||
default: false | ||
}, | ||
isGithubCool: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
githubId: { | ||
type: "string" | ||
}, | ||
githubURL: { | ||
type: "string" | ||
}, | ||
githubEmail: { | ||
type: "string" | ||
}, | ||
joinedGithubOn: { | ||
type: "date" | ||
}, | ||
website: { | ||
type: "string" | ||
}, | ||
githubProfile: { | ||
type: "string" | ||
}, | ||
_csrf: { | ||
type: "string" | ||
}, | ||
isMigrationGrandfathered: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
username: { | ||
type: "string" | ||
}, | ||
bio: { | ||
type: "string", | ||
default: "" | ||
}, | ||
about: { | ||
type: "string", | ||
default: "" | ||
}, | ||
name: { | ||
type: "string", | ||
default: "" | ||
}, | ||
gender: { | ||
type: "string", | ||
default: "" | ||
}, | ||
location: { | ||
type: "string", | ||
default: "" | ||
}, | ||
picture: { | ||
type: "string", | ||
default: "" | ||
}, | ||
linkedin: { | ||
type: "string" | ||
}, | ||
codepen: { | ||
type: "string" | ||
}, | ||
twitter: { | ||
type: "string" | ||
}, | ||
currentStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
longestStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
sendMonthlyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendNotificationEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendQuincyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
isLocked: { | ||
type: "boolean", | ||
description: | ||
"Campers profile does not show challenges/certificates to the public", | ||
default: false | ||
}, | ||
currentChallengeId: { | ||
type: "string", | ||
description: "The challenge last visited by the user", | ||
default: "" | ||
}, | ||
currentChallenge: { | ||
type: {}, | ||
description: "deprecated" | ||
}, | ||
isUniqMigrated: { | ||
type: "boolean", | ||
description: "Campers completedChallenges array is free of duplicates", | ||
default: false | ||
}, | ||
isHonest: { | ||
type: "boolean", | ||
description: "Camper has signed academic honesty policy", | ||
default: false | ||
}, | ||
isFrontEndCert: { | ||
type: "boolean", | ||
description: "Camper is front end certified", | ||
default: false | ||
}, | ||
isDataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified", | ||
default: false | ||
}, | ||
isBackEndCert: { | ||
type: "boolean", | ||
description: "Campers is back end certified", | ||
default: false | ||
}, | ||
isFullStackCert: { | ||
type: "boolean", | ||
description: "Campers is full stack certified", | ||
default: false | ||
}, | ||
isRespWebDesignCert: { | ||
type: "boolean", | ||
description: "Camper is responsive web design certified", | ||
default: false | ||
}, | ||
is2018DataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified (2018)", | ||
default: false | ||
}, | ||
isFrontEndLibsCert: { | ||
type: "boolean", | ||
description: "Camper is front end libraries certified", | ||
default: false | ||
}, | ||
isJsAlgoDataStructCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is javascript algorithms and data structures certified", | ||
default: false | ||
}, | ||
isApisMicroservicesCert: { | ||
type: "boolean", | ||
description: "Camper is apis and microservices certified", | ||
default: false | ||
}, | ||
isInfosecQaCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is information security and quality assurance certified", | ||
default: false | ||
}, | ||
isChallengeMapMigrated: { | ||
type: "boolean", | ||
description: "Migrate completedChallenges array to challenge map", | ||
default: false | ||
}, | ||
challengeMap: { | ||
type: "object", | ||
description: "A map by ID of all the user completed challenges", | ||
default: {} | ||
}, | ||
completedChallenges: { | ||
type: [ | ||
{ | ||
completedDate: "number", | ||
lastUpdated: "number", | ||
numOfAttempts: "number", | ||
id: "string", | ||
name: "string", | ||
completedWith: "string", | ||
solution: "string", | ||
githubLink: "string", | ||
verified: "boolean", | ||
challengeType: { | ||
type: "number", | ||
default: 0 | ||
} | ||
} | ||
], | ||
default: [] | ||
}, | ||
portfolio: { | ||
type: "array", | ||
default: [] | ||
}, | ||
rand: { | ||
type: "number", | ||
index: true | ||
}, | ||
tshirtVote: { | ||
type: "number" | ||
}, | ||
timezone: { | ||
type: "string" | ||
}, | ||
theme: { | ||
type: "string", | ||
default: "default" | ||
}, | ||
languageTag: { | ||
type: "string", | ||
description: "An IETF language tag", | ||
default: "en" | ||
}, | ||
badges: { | ||
type: { | ||
coreTeam: { | ||
type: "array", | ||
default: [] | ||
} | ||
}, | ||
default: {} | ||
} | ||
}); | ||
|
||
module.exports = mongoose.model("User", userSchema, "user"); |
Oops, something went wrong.