Skip to content

Commit e2bcd04

Browse files
committed
First
0 parents  commit e2bcd04

17 files changed

+5283
-0
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DATABASE_URL=postgres://localhost/books

.eslintrc.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
extends:
2+
- airbnb-base
3+
- plugin:prettier/recommended
4+
env:
5+
node: true
6+
parser: typescript-eslint-parser
7+
parserOptions:
8+
ecmaVersion: 7
9+
sourceType: module
10+
ecmaFeatures:
11+
modules: true
12+
plugins:
13+
- import
14+
- typescript
15+
- prettier
16+
- security
17+
rules:
18+
strict: 0
19+
object-curly-spacing:
20+
- error
21+
- always
22+
23+
# Follow up: https://github.com/eslint/typescript-eslint-parser/issues/416
24+
no-undef: 0
25+
no-use-before-define: 0
26+
no-restricted-globals: 0
27+
28+
import/extensions: 0
29+
settings:
30+
import/resolver:
31+
node:
32+
extensions:
33+
- .ts
34+
- .tsx
35+
- .d.ts
36+
- .js
37+
- .json
38+
39+
import/parsers:
40+
typescript-eslint-parser:
41+
- .ts

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.env
2+
node_modules
3+
yarn-error.log
4+
.DS_STORE

.prettierrc.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
printWidth: 120
2+
parser: typescript
3+
semi: true
4+
useTabs: false
5+
singleQuote: true
6+
trailingComma: es5
7+
bracketSpacing: true
8+
jsxBracketSameLine: true
9+
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
exports.up = async knex =>
2+
knex.schema
3+
.createTable('authors', table => {
4+
table.increments('id');
5+
table.string('firstName');
6+
table.string('lastName');
7+
})
8+
.createTable('books', table => {
9+
table.increments('id');
10+
table.string('title');
11+
table
12+
.integer('authorId')
13+
.unsigned()
14+
.references('authors.id')
15+
.onDelete('CASCADE')
16+
.onUpdate('CASCADE');
17+
});
18+
19+
exports.down = async knex => knex.schema.dropTableIfExists('books').dropTableIfExists('authors');

knexfile.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const parse = require('connection-string');
2+
const _ = require('lodash');
3+
4+
require('dotenv').config();
5+
6+
const connection = parse(process.env.DATABASE_URL);
7+
8+
const defaults = {
9+
client: 'pg',
10+
connection: {
11+
user: connection.user || 'root',
12+
password: connection.password || '',
13+
host: connection.host,
14+
port: connection.port || 5432,
15+
database: connection.segments[0],
16+
},
17+
migrations: {
18+
directory: `${__dirname}/db/migrations`,
19+
},
20+
seeds: {
21+
directory: `${__dirname}/db/seeds`,
22+
},
23+
debug: false,
24+
};
25+
26+
const environments = {
27+
production: {
28+
pool: {
29+
min: 2,
30+
max: 10,
31+
},
32+
},
33+
};
34+
35+
const config = _.merge(defaults, environments[process.env.NODE_ENV]);
36+
37+
module.exports = config;

nodemon.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"watch": ["src"],
3+
"ext": "ts json",
4+
"ignore": ["src/**/*.spec.ts"],
5+
"exec": "ts-node ./src/main.ts"
6+
}

package.json

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"scripts": {
3+
"start": "nodemon",
4+
"debug": "nodemon",
5+
"knex": "ts-node ./node_modules/.bin/knex"
6+
},
7+
"dependencies": {
8+
"apollo-server-express": "^1.3.2",
9+
"body-parser": "^1.18.2",
10+
"connection-string": "^0.4.1",
11+
"core-js": "^2.5.3",
12+
"dotenv": "^4.0.0",
13+
"express": "^4.16.2",
14+
"graphql": "^0.12.3",
15+
"graphql-tools": "^2.19.0",
16+
"knex": "^0.14.2",
17+
"lodash": "^4.17.4",
18+
"morgan": "^1.9.0",
19+
"pg": "^7.4.1"
20+
},
21+
"devDependencies": {
22+
"@types/body-parser": "^1.16.8",
23+
"@types/express": "^4.11.0",
24+
"@types/knex": "^0.14.4",
25+
"@types/node": "^9.4.0",
26+
"ava": "^0.25.0",
27+
"eslint": "^4.16.0",
28+
"eslint-config-airbnb-base": "^12.1.0",
29+
"eslint-config-google": "^0.9.1",
30+
"eslint-config-prettier": "^2.9.0",
31+
"eslint-import-resolver-typescript": "^1.0.2",
32+
"eslint-plugin-import": "^2.8.0",
33+
"eslint-plugin-prettier": "^2.5.0",
34+
"eslint-plugin-security": "^1.4.0",
35+
"eslint-plugin-typescript": "^0.8.1",
36+
"nodemon": "^1.14.11",
37+
"prettier": "^1.10.2",
38+
"ts-node": "^4.1.0",
39+
"tslint": "^5.9.1",
40+
"typescript": "^2.6.2",
41+
"typescript-eslint-parser": "^12.0.0"
42+
}
43+
}

src/database.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as _ from 'lodash';
2+
import * as Knex from 'knex';
3+
import * as knexfile from '../knexfile';
4+
5+
class Database {
6+
private knexInstance: Knex;
7+
private config: Object;
8+
9+
public connect(options = {}): void {
10+
if (this.knexInstance) {
11+
return;
12+
}
13+
this.config = _.merge({}, knexfile, options);
14+
this.knexInstance = Knex(this.config);
15+
}
16+
17+
public get query(): Knex {
18+
if (!this.knexInstance) {
19+
this.connect();
20+
}
21+
22+
return this.knexInstance;
23+
}
24+
25+
public close(done): void {
26+
if (!this.knexInstance) {
27+
done();
28+
return;
29+
}
30+
31+
this.knexInstance.destroy(done);
32+
}
33+
}
34+
35+
export default new Database();

src/main.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Server from './server';
2+
import Database from './database';
3+
4+
Database.connect();
5+
Server.start();
6+
7+
const shutdown = done => {
8+
Database.close(() => {
9+
Server.stop(done);
10+
});
11+
};
12+
13+
// Nodemon
14+
process.once('SIGUSR2', shutdown.bind(null, process.exit));
15+
process.on('exit', shutdown.bind(null, process.exit));
16+
process.on('SIGINT', shutdown.bind(null, process.exit));
17+
process.on('uncaughtException', shutdown.bind(null, process.exit));

src/schema/author/index.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import db from '../../database';
2+
3+
export const type = `
4+
type Author {
5+
id: ID!,
6+
firstName: String,
7+
lastName: String
8+
9+
books(limit: Int, offset: Int): [Book]
10+
}
11+
`;
12+
13+
export const queries = {
14+
author: {
15+
definition: `author(firstName: String): Author!`,
16+
resolve(root, { firstName = '' }) {
17+
return db
18+
.query('authors')
19+
.where({ firstName })
20+
.first();
21+
},
22+
},
23+
authors: {
24+
definition: 'authors: [Author]!',
25+
resolve: () => db.query('authors'),
26+
},
27+
};
28+
29+
export const mutations = {
30+
createAuthor: {
31+
definition: `createAuthor(firstName: String!, lastName: String!): Author`,
32+
async resolve(root, args) {
33+
const [author] = await db
34+
.query('authors')
35+
.insert(args)
36+
.returning('*');
37+
38+
return author;
39+
},
40+
},
41+
};
42+
43+
export const resolvers = {
44+
Author: {
45+
books(author, { limit = 100, offset = 0 }) {
46+
// TODO: Only query for selected fields (d.fieldNodes[0].selectionSet.selections)
47+
return db
48+
.query('books')
49+
.where({ authorId: author.id })
50+
.limit(limit)
51+
.offset(offset);
52+
},
53+
},
54+
};

src/schema/book/index.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import db from '../../database';
2+
3+
export const type = `
4+
type Book {
5+
id: ID!,
6+
title: String
7+
author: Author
8+
}
9+
`;
10+
11+
export const queries = {
12+
book: {
13+
definition: 'book(title: String): Book',
14+
resolve(root, { title = '' }) {
15+
return db
16+
.query('books')
17+
.where({ title })
18+
.first();
19+
},
20+
},
21+
books: {
22+
definition: 'books(limit: Int): [Book]',
23+
resolve(root, { limit = 100, offset = 0 }) {
24+
return db
25+
.query('books')
26+
.limit(limit)
27+
.offset(offset);
28+
},
29+
},
30+
};
31+
32+
export const mutations = {
33+
createBook: {
34+
definition: `createBook(title: String!, authorId: ID!): Book`,
35+
async resolve(root, args) {
36+
const [book] = await db
37+
.query('books')
38+
.insert(args)
39+
.returning('*');
40+
41+
return book;
42+
},
43+
},
44+
};
45+
46+
export const resolvers = {
47+
Book: {
48+
author(book): Object {
49+
return db
50+
.query('authors')
51+
.where({ id: book.authorId })
52+
.first();
53+
},
54+
},
55+
};

0 commit comments

Comments
 (0)