Init Project

Add tsconfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// tsconfig.json
{
"files": [
"server.ts"
],
"compilerOptions": {
"outDir": "./build",
"target": "es2015",
"module": "commonjs",
"moduleResolution": "Node",
"sourceMap": true,
"pretty": true,
"strictNullChecks": true,
"lib": [
"esnext"
]
},
"exclude": [
"node_modules",
"build"
]
}

Add tslint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// tslint.json
{
"rules": {
"align": [
true,
"parameters",
"arguments",
"statements"
],
"ban": false,
"class-name": true,
"curly": true,
"eofline": true,
"forin": true,
"indent": [
true,
"spaces"
],
"interface-name": false,
"jsdoc-format": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
"public-before-private",
"static-before-instance",
"variables-before-functions"
],
"no-any": false,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-shadowed-variable": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-require-imports": true,
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": false,
"no-var-keyword": true,
"no-var-requires": true,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": [
true,
"single",
"jsx-single",
"avoid-escape"
],
"radix": true,
"semicolon": false,
"switch-default": false,
"triple-equals": [
true,
"allow-null-check"
],
"typedef": false,
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}

Add env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// env.ts
import * as envalid from 'envalid'

const { url, bool, str } = envalid

const env = envalid.cleanEnv(process.env, {
SERVICE_URI: str({ default: 'https://reqres.in/api' }),
NODE_ENV: str({ devDefault: 'development', default: 'production' }),
LOG_LEVEL: str({ default: 'info' }),
CORS: bool({ devDefault: true, default: false }),
GRAPHIQL: bool({ devDefault: true, default: true }),
})

export default env

Add Nodemon

1
2
3
4
5
6
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node ./server.ts"
}

Create Koa APP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/app.ts
import * as Koa from 'koa'
import * as cors from 'koa-cors'
import * as convert from 'koa-convert'
import { apiRouter } from './routers'

const app = new Koa()

if (process.env.CORS) {
app.use(convert(cors()))
}

app.use(apiRouter.routes()).use(apiRouter.allowedMethods())

export default app

Server Startup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// server.ts
import * as chalk from 'chalk'
import env from './env'
process.env = env
import app from './src/app'

const port = process.env.PORT || 3001

app.listen(port, () => {
if (process.env.NODE_ENV === 'development') {
if (process.env.GRAPHIQL) {
console.log('The GraphiQL App is running at: ')
console.log(chalk.cyan(`http://localhost:${port}/api/graphiql`))
} else {
console.log('Koa app is running at: ')
console.log(chalk.cyan(`http://localhost:${port}`))
}
} else {
console.log('Koa App is running')
}
})

Add Router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ./src/routers/graphql.ts
import * as Router from 'koa-router'
import * as GraphQLHttp from 'koa-graphql'
import schema from '../graphql/schema'

const router = new Router()
// console.log(process.env)

console.log(process.env)
router.all('/', GraphQLHttp({
schema,
graphiql: process.env.GRAPHIQL,
pretty: true,
errorFormat: error => {
const { message, locations, path, stack } = error
console.error(`GraphQL Error: `, {
message,
locations,
path,
stack,
})
}
}))


export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ./src/routers/index.ts
import * as Router from 'koa-router'
import graphQLRouter from './graphql'
import testRouter from './test'

const apiRouter = new Router({
prefix: '/api'
})

apiRouter.use('/graphql', graphQLRouter.routes(), graphQLRouter.allowedMethods())
apiRouter.use('/test', testRouter.routes(), testRouter.allowedMethods())

export {
apiRouter
}

Config Graphql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ./src/graphql/model.ts
import {
GraphQLObjectType,
GraphQLString,
GraphQLNonNull,
} from 'graphql'

const User = new GraphQLObjectType({
name: 'User',
description: 'A User',
fields: () => ({
id: {
type: new GraphQLNonNull(GraphQLString),
resolve: (user) => user.id,
},
firstName: {
type: new GraphQLNonNull(GraphQLString),
resolve: user => user.first_name
},
lastName: {
type: new GraphQLNonNull(GraphQLString),
resolve: user => user.last_name
}
})
})

export {
User
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ./src/graphql/query.ts
import { GraphQLObjectType, GraphQLList } from 'graphql'
import axios from 'axios'
import { User } from './model'

export default new GraphQLObjectType({
name: 'Query',
description: 'Query of GraphQL',
fields: () => ({
users: {
type: new GraphQLList(User),
resolve: (root, args) => {
return axios({
url: 'https://reqres.in/api/users',
}).then(res => res.data).then(res => res.data)
}
}
})
})
1
2
3
4
5
6
7
// ./src/graphql/schema.ts
import { GraphQLSchema } from 'graphql'
import query from './query'

export default new GraphQLSchema({
query
})