Skip to content

Commit

Permalink
init. setup server with apollo, accounts, typegoose and type-graphql
Browse files Browse the repository at this point in the history
  • Loading branch information
TrillCyborg committed Jun 2, 2019
0 parents commit 0f61c0b
Show file tree
Hide file tree
Showing 21 changed files with 9,314 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 100,
"semi": false,
"singleQuote": true,
"trailingComma": "es5"
}
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules/
3 changes: 3 additions & 0 deletions server/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'],
}
6 changes: 6 additions & 0 deletions server/config/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"PORT": 4000,
"DB_NAME": "oneFraction",
"MONGO_HOST": "",
"ACCOUNTS_SECRET": ""
}
6 changes: 6 additions & 0 deletions server/config/local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"PORT": 4000,
"DB_NAME": "oneFraction",
"MONGO_HOST": "",
"ACCOUNTS_SECRET": "979083ce-a9b6-483d-a899-ea22d5b0a1dd"
}
47 changes: 47 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "server",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "ts-node src/index.ts",
"watch": "nodemon -e ts -w ./src -x npm run watch:serve",
"watch:serve": "ts-node --inspect src/index.ts"
},
"devDependencies": {
"@babel/preset-env": "^7.4.5",
"@babel/preset-typescript": "^7.3.3",
"@types/config": "^0.0.34",
"@types/graphql": "^14.2.0",
"@types/mongoose": "^5.5.3",
"@types/node": "^12.0.4",
"husky": "^2.3.0",
"nodemon": "^1.19.1",
"prettier": "^1.17.1",
"pretty-quick": "^1.11.0",
"ts-node": "^8.2.0",
"typescript": "^3.5.1"
},
"dependencies": {
"@accounts/database-manager": "^0.15.0",
"@accounts/graphql-api": "^0.15.0",
"@accounts/mongo": "^0.15.0",
"@accounts/password": "^0.15.0",
"@accounts/server": "^0.15.0",
"@graphql-modules/core": "^0.7.5",
"apollo-server": "^2.6.1",
"apollo-server-express": "^2.6.1",
"config": "^3.1.0",
"graphql": "^14.3.1",
"graphql-toolkit": "^0.2.14",
"mongoose": "^5.5.12",
"reflect-metadata": "^0.1.13",
"type-graphql": "^0.17.4",
"typegoose": "^5.6.0"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
}
}
34 changes: 34 additions & 0 deletions server/schema.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

"""
The javascript `Date` as string. Type represents date and time as the ISO Date string.
"""
scalar DateTime

type Query {
me: User!
getUser(userId: ID!): User!
}

enum Role {
User
Admin
}

type User {
_id: ID!
sessionId: String!
roles: [Role!]!
phoneNumber: String!
username: String
ethAddress: String
smsToken: String
isBlocked: Boolean
isPhoneVerified: Boolean
isOnboarded: Boolean
createdAt: DateTime!
updatedAt: DateTime!
}
53 changes: 53 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'reflect-metadata'
import * as mongoose from 'mongoose'
import * as express from 'express'
import { ApolloServer, makeExecutableSchema } from 'apollo-server-express'
import { buildSchema } from 'type-graphql'
import { mergeResolvers, mergeTypeDefs, mergeSchemas } from 'graphql-toolkit'
import { PORT, MONGO_HOST, DB_NAME } from './modules/common/consts'
import UserResolver from './modules/user/UserResolver'
import { authChecker } from './modules/user/authChecker'
import { setUpAccounts, userTypeDefs } from './modules/user/accounts'
import { TypegooseMiddleware } from './middleware/typegoose'

;(async () => {
const mongooseConnection = await mongoose.connect(
`mongodb://${MONGO_HOST || 'localhost'}:27017/${DB_NAME}`
)
const { accountsGraphQL, accountsServer } = setUpAccounts(mongooseConnection.connection)

const typeGraphqlSchema = await buildSchema({
resolvers: [UserResolver],
globalMiddlewares: [TypegooseMiddleware],
// scalarsMap: [{ type: ObjectId, scalar: ObjectIdScalar }],
validate: false,
emitSchemaFile: true,
authChecker,
})

const schema = makeExecutableSchema({
typeDefs: mergeTypeDefs([userTypeDefs, accountsGraphQL.typeDefs]),
resolvers: mergeResolvers([accountsGraphQL.resolvers]),
schemaDirectives: {
...accountsGraphQL.schemaDirectives,
},
})

const server = new ApolloServer({
schema: mergeSchemas({
schemas: [schema, typeGraphqlSchema],
}),
context: accountsGraphQL.context,
formatError: error => {
console.error(error)
return error
},
playground: true,
})

const app = express()
server.applyMiddleware({ app })

await app.listen({ port: PORT })
console.log(`🚀 Server ready at localhost:${PORT}`)
})()
24 changes: 24 additions & 0 deletions server/src/middleware/typegoose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { MiddlewareFn } from 'type-graphql'
import { Model, Document } from 'mongoose'
import { getClassForDocument } from 'typegoose'

export const TypegooseMiddleware: MiddlewareFn = async (_, next) => {
const result = await next()

if (Array.isArray(result)) {
return result.map(item => (item instanceof Model ? convertDocument(item) : item))
}

if (result instanceof Model) {
return convertDocument(result)
}

return result
}

function convertDocument(doc: Document) {
const convertedDocument = doc.toObject()
const DocumentClass: Function = getClassForDocument(doc)
Object.setPrototypeOf(convertedDocument, DocumentClass.prototype)
return convertedDocument
}
6 changes: 6 additions & 0 deletions server/src/modules/common/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as config from 'config'

export const PORT: number = config.get('PORT')
export const MONGO_HOST: string = config.get('MONGO_HOST')
export const DB_NAME: string = config.get('DB_NAME')
export const ACCOUNTS_SECRET: string = config.get('ACCOUNTS_SECRET')
6 changes: 6 additions & 0 deletions server/src/modules/common/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import User from '../user/UserEntity'

export interface Context {
userId?: string
user?: User
}
58 changes: 58 additions & 0 deletions server/src/modules/user/UserEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { prop, Typegoose } from 'typegoose'
import { ObjectType, Field, ID } from 'type-graphql'
import { ObjectId } from 'mongodb'
import { Role } from './consts'

@ObjectType()
export class User extends Typegoose {
@Field(type => ID)
readonly _id: ObjectId

@prop()
@Field()
sessionId?: string

@prop({ required: true, enum: Role })
@Field(type => Role)
roles: Role[]

@prop({ required: true })
@Field()
phoneNumber: string

@prop({ lowercase: true })
@Field({ nullable: true })
username?: string

@prop({ lowercase: true })
@Field({ nullable: true })
ethAddress?: string

@prop()
@Field({ nullable: true })
smsToken?: string

@prop()
@Field({ nullable: true })
isBlocked?: boolean

@prop()
@Field({ nullable: true })
isPhoneVerified?: boolean

@prop()
@Field({ nullable: true })
isOnboarded?: boolean

@prop()
@Field(() => Date)
createdAt: Date

@prop()
@Field(() => Date)
updatedAt: Date
}

export default new User().getModelForClass(User, {
schemaOptions: { timestamps: true },
})
41 changes: 41 additions & 0 deletions server/src/modules/user/UserResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
Arg,
Resolver,
Query,
Authorized,
Mutation,
Field,
InputType,
ObjectType,
FieldResolver,
Root,
ID,
Ctx,
} from 'type-graphql'
import { Context } from '../common/context'
import { UserService } from './UserService'
import { User } from './UserEntity'
import './enums'

@Resolver(User)
export default class UserResolver {
private readonly service: UserService

constructor() {
this.service = new UserService()
}

@Query(returns => User)
@Authorized()
async me(@Ctx() ctx: Context) {
if (ctx.userId) {
return await this.service.findOneById(ctx.userId)
}
}

@Query(returns => User)
@Authorized()
async getUser(@Arg('userId', returns => ID) userId: string) {
return await this.service.findOneById(userId)
}
}
27 changes: 27 additions & 0 deletions server/src/modules/user/UserService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ModelType } from 'typegoose'
import UserModal, { User } from './UserEntity'

export class UserService {
private readonly modal: ModelType<User>

constructor() {
this.modal = UserModal
}

async find(selector?: Partial<User>) {
return this.modal.find(selector)
}

async findOneById(_id: string) {
return this.modal.findOne({ _id })
}

async remove(_id: string) {
let entityToRemove = await this.modal.findOne(_id)
await this.modal.remove(entityToRemove)
}

async count(entity: any) {
return this.modal.count(entity)
}
}
48 changes: 48 additions & 0 deletions server/src/modules/user/accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import MongoDBInterface from '@accounts/mongo'
import { AccountsModule } from '@accounts/graphql-api'
import { DatabaseManager } from '@accounts/database-manager'
import { AccountsServer } from '@accounts/server'
import { AccountsPassword } from '@accounts/password'
import { ACCOUNTS_SECRET } from '../common/consts'

export const accountsPassword = new AccountsPassword({
validateNewUser: user => {
console.log('validateNewUser', user)

return user
},
})

export const setUpAccounts = (connection: any) => {
const userStorage = new MongoDBInterface(connection)

const accountsDb = new DatabaseManager({
sessionStorage: userStorage,
userStorage,
})

const accountsServer = new AccountsServer(
{ db: accountsDb, tokenSecret: ACCOUNTS_SECRET },
{
password: accountsPassword,
}
)

const accountsGraphQL = AccountsModule.forRoot({
accountsServer,
})

return { accountsGraphQL, accountsServer }
}

export const userTypeDefs = `
# Our custom fields to add to the user
extend input CreateUserInput {
profile: CreateUserProfileInput!
roles: [String!]
}
input CreateUserProfileInput {
firstName: String!
lastName: String!
}
`
Loading

0 comments on commit 0f61c0b

Please sign in to comment.