Commit 46d88b0f authored by Max Hartmann's avatar Max Hartmann

init project

parent 317faf4e
{
"README_schema" : "Specifies how to load the GraphQL schema that completion, error highlighting, and documentation is based on in the IDE",
"schema": {
"README_file" : "Remove 'file' to use request url below. A relative or absolute path to the JSON from a schema introspection query, e.g. '{ data: ... }' or a .graphql/.graphqls file describing the schema using GraphQL Schema Language. Changes to the file are watched.",
"file": "graphql.schema.json",
"README_request" : "To request the schema from a url instead, remove the 'file' JSON property above (and optionally delete the default graphql.schema.json file).",
"request": {
"url" : "http://--graphql-server--/path-to-schema-json-or-introspection-endpoint",
"method" : "POST",
"README_postIntrospectionQuery" : "Whether to POST an introspectionQuery to the url. If the url always returns the schema JSON, set to false and consider using GET",
"postIntrospectionQuery" : true,
"README_options" : "See the 'Options' section at https://github.com/then/then-request",
"options" : {
"headers": {
"user-agent" : "JS GraphQL"
}
}
}
},
"README_endpoints": "A list of GraphQL endpoints that can be queried from '.graphql' files in the IDE",
"endpoints" : [
{
"name": "Default (http://localhost:8080/graphql)",
"url": "http://localhost:8080/graphql",
"options" : {
"headers": {
"user-agent" : "JS GraphQL"
}
}
}
]
}
\ No newline at end of file
This diff is collapsed.
{
"name": "graphql-boilerplate",
"scripts": {
"build": "webpack --production",
"dev": "tsc --watch | nodemon --inspect ./dist/app.js --watch dist",
"test": "mocha -r ts-node/register src/**/*.spec.ts"
},
"dependencies": {
"@tsed/common": "^5.1.1",
"@tsed/core": "^5.1.1",
"@tsed/di": "^5.1.1",
"@types/express-graphql": "^0.6.2",
"@types/express": "^4.16.1",
"@types/graphql": "^14.0.5",
"@types/pg": "^7.4.11",
"body-parser": "^1.18.3",
"express": "^4.16.4",
"express-graphql": "^0.7.1",
"graphql": "^14.1.1",
"pg": "^7.8.0"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.5",
"@types/node": "^10.12.23",
"@types/supertest": "^2.0.7",
"awesome-typescript-loader": "^5.2.1",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"nodemon": "^1.18.9",
"supertest": "^3.4.2",
"ts-node": "7.0.1",
"tslint": "^5.12.1",
"typescript": "3.2.2",
"webpack": "^4.29.3",
"webpack-cli": "^3.2.3"
}
}
import {PersonController} from './controllers';
import Server from './server';
import {PersonService} from './services';
const personService = new PersonService();
new Server()
.restController(new PersonController(personService), '/person')
.graphQlController('/graphql')
.start();
import {expect} from 'chai';
import {QueryResult} from 'pg';
import request from 'supertest';
import {GraphQlController, PersonController} from './controllers';
import {Person} from './models';
import {PersonService} from './services';
import express from 'express';
const personServiceMock = {
findPersons(): Promise<QueryResult> {
return new Promise((resolve) => {
resolve({rows: [{id: 1, firstname: 'Max', surname: 'Hartmann'} as Person]} as QueryResult);
});
}
} as PersonService;
describe('Controller Specs', () => {
const app = express();
describe('Graphql Controller', () => {
// noinspection JSUnusedLocalSymbols
const should = require('chai').should();
it('should create Middleware', () => {
const graphQlController = new GraphQlController(personServiceMock);
const middleware = graphQlController.createGraphqlMiddleware();
expect(middleware).not.to.be.null;
});
it('should load Persons', (done) => {
const graphQlController = new GraphQlController(personServiceMock);
const middleware = graphQlController.createGraphqlMiddleware();
app.use('/graphql', middleware);
request(app)
.get('/graphql')
.send({query: '{ persons { surname firstname } }'})
.expect(200)
.end((err, res) => {
res.body.data.persons[0].should.have.property('firstname');
res.body.data.persons[0].should.have.property('surname');
res.body.data.persons[0].firstname.should.equal('Max');
res.body.data.persons[0].surname.should.equal('Hartmann');
done();
});
});
});
describe('Person Controller', () => {
it('should create PersonController', () => {
const personController = new PersonController(personServiceMock);
expect(personController).not.to.be.null;
});
it('should load Persons', (done) => {
const personController = new PersonController(personServiceMock);
app.use('/person', personController.routes());
request(app)
.get('/person')
.expect(200)
.end((err, res) => {
res.body[0].should.have.property('firstname');
res.body[0].should.have.property('surname');
res.body[0].firstname.should.equal('Max');
res.body[0].surname.should.equal('Hartmann');
done();
});
});
});
});
import express = require('express');
import {Request, Response, Router} from 'express';
import {buildSchema} from 'graphql';
import {Person} from './models';
import {Schema} from './schema';
import {PersonService} from './services';
import express_graphql from 'express-graphql';
export class PersonController implements Controller {
private service: PersonService;
constructor(personService: PersonService) {
this.service = personService;
}
public routes(): Router {
const routes = express.Router();
routes.get( '/', this.persons.bind(this));
routes.post( '/', this.savePerson.bind(this));
return routes;
}
private async persons(req: Request, res: Response) {
return this.service.findPersons().then((result) => res.send(result.rows));
}
private async savePerson(req: Request, res: Response) {
const person = req.body.person as Person;
this.service.save(person).then((savedId) => {
person.id = savedId;
res.send(person);
});
}
}
export class GraphQlController {
private service: PersonService;
constructor(personService: PersonService) {
this.service = personService;
}
public createGraphqlMiddleware() {
const schema = buildSchema(Schema);
// Root resolver
const root = {
persons: () => this.service.findPersons()
.then((result) => result.rows)
};
return express_graphql({
graphiql: true,
rootValue: root,
schema
});
}
}
export interface Controller {
routes(): Router;
}
export interface Person {
id: number;
surname: string;
firstname: string;
}
export const Schema = `
type Query {
persons: [Person]
}
type Person {
id: ID,
firstname: String,
surname: String
}
`;
import * as bodyParser from 'body-parser';
import {Express} from 'express';
import express from 'express';
import {Controller, GraphQlController} from './controllers';
import {PersonService} from './services';
/**
* Bootstrap Express
*
* @author max
*/
export default class Server {
private app: Express;
constructor() {
this.app = express();
this.app.use(bodyParser.json());
}
public start() {
this.app.listen(8080, () => {
console.log(`server started at http://localhost:8080`);
});
}
public restController(controller: Controller, requestPath: string) {
this.app.use(requestPath, controller.routes());
return this;
}
public graphQlController(graphqlPath: string) {
const graphQlController = new GraphQlController(new PersonService());
this.app.use(graphqlPath, graphQlController.createGraphqlMiddleware());
return this;
}
}
import {Pool, QueryResult} from 'pg';
import {Person} from './models';
export class PersonService {
private pool: Pool;
constructor() {
this.pool = new Pool({
database: 'postgres',
host: 'localhost',
port: 5432,
user: 'postgres',
password: 'keule',
});
}
public async save(person: Person): Promise<number> {
const client = await this.pool.connect();
try {
await client.query('BEGIN');
const {rows} = await client
.query('INSERT INTO person(surname, firstname) VALUES($1, $2) RETURNING id',
[person.surname, person.firstname]);
await client.query('COMMIT');
return rows[0].id;
} catch (e) {
await client.query('ROLLBACK');
} finally {
client.release();
}
}
public async findPersons(): Promise<QueryResult> {
return await this.pool.query('SELECT * FROM person order BY id ASC');
}
}
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"experimentalDecorators": true,
"target": "es6",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": [
"src/**/*",
"typings/**/*"
],
"exclude": [
"src/**/*.spec.ts"
]
}
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"trailing-comma": [ false ],
"interface-name": [false, "always-prefix"],
"no-console": [true, "trace"],
"max-classes-per-file": [false, 1],
"ordered-imports": false,
"object-literal-sort-keys": false,
"quotemark": [true, "single"],
"no-unused-expression": false
},
"rulesDirectory": []
}
var path = require('path');
module.exports = {
target: 'node',
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 8080
},
entry: [
'./src/app.ts'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist/js')
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: path.resolve(__dirname, "node_modules")
}
]
},
resolve: {
extensions: [
'.ts',
'.js'
]
}
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment