├── .editorconfig
├── .gitignore
├── .graphqlconfig.yml
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── config.dev.json
├── config.prod.json
├── config.stage.json
├── docker-compose.yaml
├── examples.graphql
├── package.json
├── scripts
├── copydeps.ts
├── schema.generate.ts
├── schema.import.ts
├── schema.patch.js
└── schema.serve.ts
├── src
├── Config.ts
├── Context.ts
├── embeddedPostgraphile
│ ├── config.ts
│ └── index.ts
├── entity
│ ├── Photo.ts
│ └── User.ts
├── generated
│ ├── postgraphile.graphql
│ ├── postgraphile.ts
│ └── types.ts
├── index.ts
├── migration
│ └── 1534731480120-Migration.ts
├── resolvers
│ ├── Mutation
│ │ ├── auth.ts
│ │ └── post.ts
│ ├── Node.ts
│ ├── Query.ts
│ ├── Subscription.ts
│ └── index.ts
├── schema.graphql
└── utils.ts
├── tsconfig.json
├── tsconfig.prod.json
├── tsconfig.test.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | end_of_line = lf
11 |
12 | [{*.ts,*.tsx,*.js,*.jsx,*.css,*scss,*.graphql}]
13 | indent_size = 4
14 |
15 | [*.md]
16 | max_line_length = off
17 | trim_trailing_whitespace = false
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | package-lock.json
3 | node_modules
4 | .idea
5 | *.log
6 | .env*
7 |
--------------------------------------------------------------------------------
/.graphqlconfig.yml:
--------------------------------------------------------------------------------
1 | projects:
2 | app:
3 | schemaPath: src/schema.graphql
4 | extensions:
5 | endpoints:
6 | default: http://localhost:4000
7 | database:
8 | schemaPath: src/generated/postgraphile.graphql
9 | extensions:
10 | endpoints:
11 | default: http://localhost:5000/graphql
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "tslint.exclude": "**/src/migration/*.ts"
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Andrey Konstantinov & Contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | Starter project for backend development of `Graphql` APIs hooked into `Typeorm` and *embedded* `Postgraphile` infrastructure.
4 |
5 | Alternatives: [Graphql API with typescript via Prisma](https://github.com/graphql-boilerplates/typescript-graphql-server/tree/master/advanced) OR [GraphQL API with typescript via TypeGraphQL](https://19majkel94.github.io/type-graphql/)
6 |
7 | ### Features:
8 |
9 | * Provides great development experience similar to `Prisma`, including code generation of query bindings, `Graphql` API schema, etc..
10 | * Does not require separate running proxy process, like Prisma or Postrgaphile server. All `GraphQL` queries are wired directly to the connection with the database.
11 | * Server is powered by `Apollo` Server 2.0
12 | * Flexible and powerful schema definitions by `Typeorm` decorators
13 | * Fine control for database migrations using `Typeorm` features, including:
14 | * Automated generation of migration queries based on introspection of source code models and state of the database
15 | * Run of the migrations on application's launch or manually via command line
16 | * Revert of the migrations via command line
17 | * Separate configurations and scripts for development, stage and production environment
18 | * `Typescript` and `TSLint` enabled
19 | * Build script creates distributable folder without dependencies to local `node_modules` folder
20 | * GraphQL playground server for experimenting with application's GraphQL API and with Postgraphile's GraphQL API
21 | * Implement resolvers using `Typeorm` queries or wire to embedded `Postgraphile` binding queries.
22 |
23 | ### Getting started
24 |
25 | 1. Run `yarn install`
26 | 2. Edit `config.dev.json` database connection settings (uses `Typeorm` connection configuration options)
27 | 3. Launch everything in development `watch` mode: `yarn start` - it will run the initial database setup (the first database schema migrations script) and will start development environment. Check terminal output for served endpoints and ports.
28 | 4. Play with example queries saved in [examples.graphql](./examples.graphql) file
29 | 5. Build everthing into self contained distributable folder: `yarn build`
30 |
31 | ### Schema migration workflow
32 |
33 | 1. Edit `src/entity/**` files to match your data model. It uses `Typeorm` to define the database model.
34 | 2. Run `yarn migrations:generate`, review and modify if necessary the generated migration script. In most cases you do not need to modify it, `Typeorm` does nice job generating migration scripts.
35 | 3. Run `yarn migrations:run` or just start the application's server to deploy the migrations
36 | 4. Run `yarn postgraphile:generate` to generate postgraphile bindings and internal GraphQL schema for querying the database with the latest schema (code generation steps are explained [here](https://github.com/degroote22/postgraphile-apollo))
37 | 5. Wire Application's GraphQL API calls to Postgraphile API calls as it is required by your business logic: see `src/resolvers/**` scripts for example.
38 |
39 | ### Commands
40 |
41 | * `yarn start` starts GraphQL server in `watch` mode and Playground server
42 | * `yarn watch` starts GraphQL server in `watch` mode
43 | * `yarn build` builds self contained distributable folder with production build
44 | * `yarn playground` opens the GraphQL Playground for the `projects` from [`.graphqlconfig.yml`](./.graphqlconfig.yml)
45 | * `yarn postgraphile:serve` runs `Postgraphile` server targeting development database. It is only for development and usage in playground. `Postgraphile` server is not required in production.
46 | * `yarn migrations:generate` generates database schema migration queries
47 | * `yarn migrations:run`, `yarn migrations:run:stage` and `yarn migrations:run:prod` deployes all pending schema migrations targeting development, stage and production databases accordingly
48 | * `yarn migrations:revert` deployes all pending schema migrations
49 |
50 | Check other commands in the `package.json` file. They are all self explanatory.
51 |
52 | ### Project structure
53 |
54 | | File name | Description
|
55 | | :-- | :-- |
56 | | `config.*.json` | Datebase connection settings for dev, stage and production environments |
57 | | `scripts/**` | Build and code generation scripts invoked by `npm/yarn` commands |
58 | | `src/entity/**` | Definition of the database model |
59 | | `src/migrations/**` | Generated or manually prepared database schema migrations scripts |
60 | | `src/generated/**` | Code automatically generated for quering the database via `Postgraphile` rendered queries |
61 | | `src/resolvers/**` | Implementation of your application's GraphQL API resolvers |
62 | | `└──Mutation/auth.ts` | Example of implementing GraphQL resolvers using `Typeorm` queries |
63 | | `└──Mutation/post.ts` | Example of implementing GraphQL resolvers using transparent proxing to `Postgraphile` queries resolver |
64 | | `docker-compose.yaml` | Config for launching Postgres database locally via `docker-compose up -d` |
65 |
66 | ### Subscription to database changes
67 |
68 | This feature is intentionally not added to the starter project for few reasons:
69 | 1. Subscriptions, based on `Postgraphile` or `Typeorm` subscriptions, do not scale beyong a single process. If you still prefer this there are many examples and blogs how `Typeorm` subscriptions can be wired to GraphQL subscription endpoint. `Postgraphile` also has got subscriptions support via additional plugin. As `Postgraphile` or `Typeorm` notifications are not caused by the database itself, you will need to wire both libraries for notifications or pick only one for invoking database mutations.
70 | 2. Better subscriptions are based on Postgres triggers raising notifications on row updates. This will scale better but require selective choice of what triggers to place. If you prefer this approach: create new database migration script which will add Postgres notification TRIGGER on create/update/delete AND subscribe to these notification using plaing `pg` driver.
71 | 3. Even better use some message queue, like `Kafka` for resilient, scalable, distributed notifications. But this goes beyond of this project boilerplate.
72 |
73 | Enjoy!
74 |
75 | ### License
76 |
77 | MIT License
78 |
79 | Copyright (c) 2018 Andrey Konstantinov & Contributors
80 |
81 |
--------------------------------------------------------------------------------
/config.dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "postgres",
3 | "host": "localhost",
4 | "port": 5432,
5 | "username": "pguser",
6 | "password": "pgpass",
7 | "database": "dbname",
8 | "schema": "public",
9 | "logging": true,
10 | "migrationsRun": true,
11 | "entities": [
12 | "src/entity/**/*"
13 | ],
14 | "migrations": [
15 | "src/migration/**/*"
16 | ],
17 | "subscribers": [
18 | "src/subscriber/**/*"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/config.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "postgres",
3 | "host": "localhost",
4 | "port": 5432,
5 | "username": "pguser",
6 | "password": "pgpass",
7 | "database": "dbname",
8 | "schema": "public",
9 | "logging": false,
10 | "migrationsRun": true,
11 | "entities": [
12 | "src/entity/**/*"
13 | ],
14 | "migrations": [
15 | "src/migration/**/*"
16 | ],
17 | "subscribers": [
18 | "src/subscriber/**/*"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/config.stage.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "postgres",
3 | "host": "localhost",
4 | "port": 5432,
5 | "username": "pguser",
6 | "password": "pgpass",
7 | "database": "dbname",
8 | "schema": "public",
9 | "logging": true,
10 | "migrationsRun": true,
11 | "entities": [
12 | "src/entity/**/*"
13 | ],
14 | "migrations": [
15 | "src/migration/**/*"
16 | ],
17 | "subscribers": [
18 | "src/subscriber/**/*"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | postgres:
4 | container_name: postgres
5 | image: postgres
6 | restart: always
7 | ports:
8 | - "5432:5432"
9 | environment:
10 | POSTGRES_USER: pguser
11 | POSTGRES_PASSWORD: pgpass
12 | volumes:
13 | - postgres:/var/lib/postgresql/data
14 | volumes:
15 | postgres:
16 |
--------------------------------------------------------------------------------
/examples.graphql:
--------------------------------------------------------------------------------
1 | # create user
2 | mutation {
3 | signup(email: "ass@b.c", name: "user", password: "pass") {
4 | id
5 | }
6 | }
7 |
8 | # login and save token
9 | mutation {
10 | login(email: "ass@b.c", password: "pass") {
11 | token
12 | }
13 | }
14 |
15 | # set token to Authorization header as `Bearer TOKEN_VALUE`, and now you can run auth protected queries:
16 |
17 | # create photo
18 | mutation {
19 | createPhoto(input: {
20 | photo: {
21 | name: "photo name",
22 | description: "some descr",
23 | views: 1,
24 | isPublished: false
25 | filename: "filename.tst"
26 | }
27 | }) {
28 | photo {
29 | id
30 | name
31 | nodeId
32 | filename
33 | description
34 | id
35 | }
36 | }
37 | }
38 |
39 | # list photos
40 | query {
41 | allPhotos {
42 | nodes {
43 | name,
44 | filename,
45 | description,
46 | nodeId
47 | }
48 | }
49 | }
50 |
51 | # delete photo
52 | mutation {
53 | deletePhoto(input: {
54 | nodeId: "WyJwaG90b3MiLDFd"
55 | }) {
56 | photo {
57 | id
58 | name
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "www-backend",
3 | "scripts": {
4 | "build": "npm-run-all server:build tslint:build && ts-node scripts/copydeps.ts",
5 | "start": "npm-run-all -p server:watch tslint:watch postgraphile:serve playground",
6 | "watch": "npm-run-all -p server:watch tslint:watch",
7 | "server:build": "rimraf build && tsc && cp src/schema.graphql build/schema.graphql && cp src/generated/postgraphile.graphql build/generated/postgraphile.graphql",
8 | "server:watch": "npm-watch server:runts",
9 | "server:debug": "ts-node --inspect src/index.ts config.dev.json",
10 | "server:runts": "ts-node src/index.ts config.dev.json",
11 | "server:runts:stage": "ts-node src/index.ts config.stage.json",
12 | "server:runts:prod": "ts-node src/index.ts config.prod.json",
13 | "server:runjs": "node build/index.js config.dev.json",
14 | "server:runjs:stage": "node build/index.js config.stage.json",
15 | "server:runjs:prod": "node build/index.js config.prod.json",
16 | "tslint:build": "tslint --exclude src/migration/** --project .",
17 | "tslint:watch": "npm-watch tslint:build",
18 | "migration:run": "ts-node ./node_modules/typeorm/cli.js migration:run -f config.dev",
19 | "migration:run:stage": "ts-node ./node_modules/typeorm/cli.js migration:run -f config.stage",
20 | "migration:run:prod": "ts-node ./node_modules/typeorm/cli.js migration:run -f config.prod",
21 | "migration:revert": "ts-node ./node_modules/typeorm/cli.js migration:revert -f config.dev",
22 | "migration:revert:stage": "ts-node ./node_modules/typeorm/cli.js migration:revert -f config.stage",
23 | "migration:revert:prod": "ts-node ./node_modules/typeorm/cli.js migration:revert -f config.prod",
24 | "migration:generate": "ts-node ./node_modules/typeorm/cli.js migration:generate -n Migration -d src/migration -f config.dev",
25 | "postgraphile:generate": "npm-run-all postgraphile:generate-step1 postgraphile:generate-step2 postgraphile:generate-step3",
26 | "postgraphile:generate-step1": "ts-node scripts/schema.generate.ts config.dev.json",
27 | "postgraphile:generate-step2": "graphql-binding --language typescript --input src/embeddedPostgraphile/index.ts --outputBinding src/generated/postgraphile.ts && node scripts/schema.patch.js",
28 | "postgraphile:generate-step3": "cross-env CODEGEN_ENUMS_AS_TYPES=TRUE gql-gen --require ts-node/register --template typescript --schema scripts/schema.import.ts --out src/generated/",
29 | "postgraphile:serve": "ts-node scripts/schema.serve.ts config.dev.json",
30 | "playground": "graphql playground"
31 | },
32 | "watch": {
33 | "server:runts": {
34 | "patterns": [
35 | "src/**"
36 | ],
37 | "extensions": "ts,js,graphql",
38 | "quiet": false,
39 | "legacyWatch": false,
40 | "delay": 500
41 | },
42 | "tslint:build": {
43 | "patterns": [
44 | "src/**"
45 | ],
46 | "extensions": "ts",
47 | "quiet": false,
48 | "legacyWatch": false,
49 | "delay": 500
50 | }
51 | },
52 | "resolutions": {
53 | "graphql": "0.13.2"
54 | },
55 | "dependencies": {
56 | "apollo-server": "2.0.0",
57 | "bcryptjs": "2.4.3",
58 | "graphql-import": "0.6.0",
59 | "jsonwebtoken": "8.3.0",
60 | "node-fetch": "2.2.0",
61 | "pg": "7.4.3",
62 | "postgraphile": "4.0.0-rc.2",
63 | "reflect-metadata": "0.1.12",
64 | "typeorm": "0.2.7"
65 | },
66 | "devDependencies": {
67 | "@types/bcryptjs": "2.4.1",
68 | "@types/graphql": "^0.13.4",
69 | "@types/jsonwebtoken": "7.2.8",
70 | "@types/pg": "7.4.10",
71 | "@types/ws": "6.0.0",
72 | "cross-env": "^5.2.0",
73 | "dotenv-cli": "1.4.0",
74 | "graphql-binding": "^2.2.3",
75 | "graphql-cli": "2.16.5",
76 | "graphql-code-generator": "^0.10.3",
77 | "graphql-codegen-typescript-template": "^0.10.3",
78 | "graphql-tools": "^3.0.5",
79 | "nodemon": "1.18.3",
80 | "npm-run-all": "4.1.3",
81 | "npm-watch": "0.3.0",
82 | "rimraf": "2.6.2",
83 | "ts-node": "6.2.0",
84 | "tslint": "5.11.0",
85 | "tslint-config-airbnb": "5.9.2",
86 | "tslint-react": "3.6.0",
87 | "typescript": "2.9.2"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/scripts/copydeps.ts:
--------------------------------------------------------------------------------
1 | // the script analyses production dependencies recursively
2 | // and publishes required node modules to build directory
3 |
4 | import * as cp from 'child_process';
5 | import * as fs from 'fs';
6 | const deps = cp.execSync('yarn list --prod').toString();
7 | const packs = deps.split('\n').map(l => {
8 | const words = l.split(' ');
9 | return words[words.length - 1].split('@')[0];
10 | }).filter(i => i.length !== 0);
11 |
12 | if (fs.existsSync('build/node_modules')) {
13 | cp.execSync(`rm -Rf build/node_modules`);
14 | }
15 | fs.mkdirSync('build/node_modules');
16 | const copied: string[] = [];
17 | packs.forEach(p => {
18 | if (!copied.includes(p) && fs.existsSync(`node_modules/${p}`)) {
19 | console.log(`coping: node_modules/${p} => build/node_modules/${p}`);
20 | cp.execSync(`cp -r node_modules/${p} build/node_modules`).toString();
21 | copied.push(p);
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/scripts/schema.generate.ts:
--------------------------------------------------------------------------------
1 | import { Config } from '../src/Config';
2 |
3 | import { Connection } from '../src/embeddedPostgraphile/config';
4 |
5 | const { postgraphile } = require('postgraphile');
6 |
7 | // simple arguments parser
8 | const configPath = process.argv[process.argv.length - 1];
9 | if (configPath === __filename) {
10 | console.log(`Config argument required. Run as: > ${__filename} path/to/config.json`);
11 | process.exit(1);
12 | }
13 | const config = Config.fromFile(configPath);
14 |
15 | Connection.init(config);
16 |
17 | postgraphile(Connection.instance().url, Connection.instance().schema, {
18 | ...Connection.instance().postgraphileSchemaOptions,
19 | exportGqlSchemaPath: './src/generated/postgraphile.graphql'
20 | });
21 |
--------------------------------------------------------------------------------
/scripts/schema.import.ts:
--------------------------------------------------------------------------------
1 | import { importSchema } from 'graphql-import';
2 | const schema = importSchema('./src/schema.graphql');
3 | export default schema;
4 |
--------------------------------------------------------------------------------
/scripts/schema.patch.js:
--------------------------------------------------------------------------------
1 | // temporary workaround for
2 |
3 | 'use strict'
4 |
5 | var lineToSearch = `import schema from '..\\embeddedPostgraphile\\index'`;
6 | var lineToWrite = `import schema from '../embeddedPostgraphile/index'`;
7 |
8 | var lineToSearch2 = `...args)`;
9 | var lineToWrite2 = `...args: any[])`;
10 |
11 | var lineToInsert = '/* tslint:disable */'
12 |
13 | var fileToPatch = 'src/generated/postgraphile.ts';
14 |
15 | var fs = require('fs')
16 | fs.readFile(fileToPatch, 'utf8', function (err, data) {
17 | if (err) {
18 | console.log(err);
19 | process.exit(1);
20 | }
21 |
22 | if (data.includes(lineToWrite) && data.includes(lineToWrite2) && data.startsWith(lineToInsert)) {
23 | // already patched, nothing to do
24 | process.exit(0);
25 | }
26 |
27 | var result = data.replace(lineToSearch, lineToWrite);
28 | if (result === data) {
29 | console.log(`[bindingFix.js] can not find the line '${lineToSearch}' for patching, skipping...`);
30 | }
31 |
32 | data = result;
33 | result = data.replace(lineToSearch2, lineToWrite2);
34 | if (result === data) {
35 | console.log(`[bindingFix.js] can not find the line '${lineToSearch2}' for patching, skipping...`);
36 | }
37 |
38 | data = result;
39 | if (data.startsWith(lineToInsert)) {
40 | console.log(`[bindingFix.js] tslint is already disabled, skipping...`);
41 | } else {
42 | result = `${lineToInsert}\n${data}`;
43 | }
44 |
45 | fs.writeFile(fileToPatch, result, 'utf8', function (err) {
46 | if (err) {
47 | console.log(err);
48 | process.exit(1);
49 | }
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/scripts/schema.serve.ts:
--------------------------------------------------------------------------------
1 | import { Config } from '../src/Config';
2 | import { Connection } from '../src/embeddedPostgraphile/config';
3 |
4 | // simple arguments parser
5 | const configPath = process.argv[process.argv.length - 1];
6 | if (configPath === __filename) {
7 | console.log(`Config argument required. Run as: > ${__filename} path/to/config.json`);
8 | process.exit(1);
9 | }
10 | const config = Config.fromFile(configPath);
11 | Connection.init(config);
12 |
13 | import { exec } from 'child_process';
14 | const child = exec(
15 | `postgraphile --cors -c ${Connection.instance().url} --schema ${Connection.instance().schema}`);
16 |
17 | child.stdout.on('data', (data) => {
18 | console.log(data);
19 | });
20 | child.stderr.on('data', (data) => {
21 | console.log(data);
22 | });
23 |
--------------------------------------------------------------------------------
/src/Config.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 |
3 | export class Config {
4 | static fromFile(path: string): any {
5 | // it does not validate the config
6 | const config = JSON.parse(fs.readFileSync(path).toString());
7 |
8 | // patch paths for production run of the compiled code
9 | if (__filename.endsWith('.js')) {
10 | config.entities = [
11 | __dirname + '/entity/**/*' // accepts both ts and js
12 | ];
13 | config.migrations = [
14 | __dirname + '/migration/**/*' // accepts both ts and js
15 | ];
16 | config.subscribers = [
17 | __dirname + '/subscriber/**/*' // accepts both ts and js
18 | ];
19 | }
20 |
21 | console.log(`Running with configuration: ${path}: ${JSON.stringify(config, undefined, 4)}`);
22 | return config;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Context.ts:
--------------------------------------------------------------------------------
1 | import { Connection } from 'typeorm';
2 | import { Binding } from './generated/postgraphile';
3 | import { IncomingMessage, ServerResponse } from 'http';
4 |
5 | export interface Context {
6 | orm: Connection;
7 | gql: Binding;
8 | req: IncomingMessage;
9 | res: ServerResponse;
10 | }
11 |
--------------------------------------------------------------------------------
/src/embeddedPostgraphile/config.ts:
--------------------------------------------------------------------------------
1 | const { defaultPlugins } = require('graphile-build-pg');
2 | const {
3 | defaultPlugins: defaultGraphileBuildPlugins,
4 | MutationPayloadQueryPlugin
5 | } = require('graphile-build');
6 |
7 | const defaultPluginsToChange = defaultGraphileBuildPlugins.filter(
8 | (p: any) => p !== MutationPayloadQueryPlugin
9 | );
10 |
11 | export class Connection {
12 | private static _instance: Connection | undefined = undefined;
13 |
14 | url: string;
15 | schema: string;
16 | postgraphileSchemaOptions: any;
17 |
18 | static init(config: any) {
19 | this._instance = {
20 | // tslint:disable-next-line:max-line-length
21 | url: `postgres://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`,
22 | schema: config.schema.toString(),
23 | postgraphileSchemaOptions: {
24 | ignoreRBAC: false,
25 | replaceAllPlugins: [...defaultPluginsToChange, ...defaultPlugins],
26 | showErrorStack: false,
27 | extendedErrors: 'hint',
28 |
29 | // set it if you rely on postgresql access control
30 | // pgDefaultRole: "example_role2",
31 | // jwtSecret: "secret",
32 | // jwtPgTypeIdentifier: "example.jwt_token",
33 |
34 | // production may need to catch it and log.
35 | // handleErrors: (err) => {} intercept errors in production
36 |
37 | // A file path string. Reads cached values from local cache file
38 | // to improve startup time (you may want to do this in production)
39 | // readCache: 'path to file'
40 |
41 | // A file path string. Writes computed values to local cache file
42 | // so startup can be faster (do this during the build phase)
43 | // writeCache: 'path to file'
44 | }
45 | };
46 |
47 | if (config.logging) {
48 | this._instance.postgraphileSchemaOptions.showErrorStack = true;
49 | this._instance.postgraphileSchemaOptions.extendedErrors = 'detail';
50 | }
51 | }
52 |
53 | static instance(): Connection {
54 | if (this._instance) {
55 | return this._instance;
56 | }
57 | throw Error(`${__filename} Connection.init() should be invoked before it can be used`);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/embeddedPostgraphile/index.ts:
--------------------------------------------------------------------------------
1 | const {
2 | withPostGraphileContext,
3 | createPostGraphileSchema
4 | } = require('postgraphile');
5 |
6 | import { makeRemoteExecutableSchema } from 'graphql-tools';
7 | import * as fs from 'fs';
8 | import { FetcherOperation } from 'graphql-tools/dist/stitching/makeRemoteExecutableSchema';
9 | import { execute } from 'graphql';
10 | import { Pool } from 'pg';
11 | import { Connection } from './config';
12 |
13 | let _schema: any;
14 | const getSchema = async () => {
15 | if (!_schema) {
16 | _schema = await createPostGraphileSchema(
17 | Connection.instance().url,
18 | Connection.instance().schema,
19 | Connection.instance().postgraphileSchemaOptions
20 | );
21 | return _schema;
22 | } else {
23 | return _schema;
24 | }
25 | };
26 |
27 | let pgPool: Pool | undefined = undefined;
28 | function initPgPool() {
29 | pgPool = new Pool({ connectionString: Connection.instance().url });
30 | return pgPool;
31 | }
32 |
33 | const fetcher = async (operation: FetcherOperation) => {
34 | const graphqlContext = operation.context
35 | ? operation.context.graphqlContext
36 | : {};
37 |
38 | const postGraphileContextOptions = {
39 | ...Connection.instance().postgraphileSchemaOptions,
40 | ...graphqlContext,
41 | pgPool: pgPool || initPgPool()
42 | };
43 | const postgraphileSchema = await getSchema();
44 | const result = withPostGraphileContext(postGraphileContextOptions, (context: any) =>
45 | execute(
46 | postgraphileSchema,
47 | operation.query,
48 | null,
49 | context,
50 | operation.variables,
51 | operation.operationName
52 | )
53 | );
54 | return result;
55 | };
56 |
57 | const typeDefs = fs.readFileSync('./src/generated/postgraphile.graphql', 'utf-8');
58 | const schema = makeRemoteExecutableSchema({ fetcher, schema: typeDefs });
59 |
60 | export default schema;
61 |
--------------------------------------------------------------------------------
/src/entity/Photo.ts:
--------------------------------------------------------------------------------
1 | import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
2 |
3 | @Entity()
4 | export class Photo {
5 |
6 | @PrimaryGeneratedColumn()
7 | id: number;
8 |
9 | @Column({
10 | length: 100
11 | })
12 | name: string;
13 |
14 | @Column('text')
15 | description: string;
16 |
17 | @Column()
18 | filename: string;
19 |
20 | @Column('double precision')
21 | views: number;
22 |
23 | @Column()
24 | isPublished: boolean;
25 | }
26 |
--------------------------------------------------------------------------------
/src/entity/User.ts:
--------------------------------------------------------------------------------
1 | import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
2 |
3 | @Entity()
4 | export class User {
5 | @PrimaryGeneratedColumn()
6 | id: number;
7 |
8 | @Column({
9 | length: 100
10 | })
11 | name: string;
12 |
13 | @Column('text', { unique: true })
14 | email: string;
15 |
16 | @Column('text')
17 | password: string;
18 | }
19 |
--------------------------------------------------------------------------------
/src/generated/postgraphile.graphql:
--------------------------------------------------------------------------------
1 | """
2 | A signed eight-byte integer. The upper big integer values are greater then the
3 | max value for a JavaScript number. Therefore all big integers will be output as
4 | strings and not numbers.
5 | """
6 | scalar BigInt
7 |
8 | """All input for the create `Migration` mutation."""
9 | input CreateMigrationInput {
10 | """
11 | An arbitrary string value with no semantic meaning. Will be included in the
12 | payload verbatim. May be used to track mutations by the client.
13 | """
14 | clientMutationId: String
15 |
16 | """The `Migration` to be created by this mutation."""
17 | migration: MigrationInput!
18 | }
19 |
20 | """The output of our create `Migration` mutation."""
21 | type CreateMigrationPayload {
22 | """
23 | The exact same `clientMutationId` that was provided in the mutation input,
24 | unchanged and unused. May be used by a client to track mutations.
25 | """
26 | clientMutationId: String
27 |
28 | """The `Migration` that was created by this mutation."""
29 | migration: Migration
30 |
31 | """An edge for our `Migration`. May be used by Relay 1."""
32 | migrationEdge(
33 | """The method to use when ordering `Migration`."""
34 | orderBy: [MigrationsOrderBy!] = PRIMARY_KEY_ASC
35 | ): MigrationsEdge
36 | }
37 |
38 | """All input for the create `Photo` mutation."""
39 | input CreatePhotoInput {
40 | """
41 | An arbitrary string value with no semantic meaning. Will be included in the
42 | payload verbatim. May be used to track mutations by the client.
43 | """
44 | clientMutationId: String
45 |
46 | """The `Photo` to be created by this mutation."""
47 | photo: PhotoInput!
48 | }
49 |
50 | """The output of our create `Photo` mutation."""
51 | type CreatePhotoPayload {
52 | """
53 | The exact same `clientMutationId` that was provided in the mutation input,
54 | unchanged and unused. May be used by a client to track mutations.
55 | """
56 | clientMutationId: String
57 |
58 | """The `Photo` that was created by this mutation."""
59 | photo: Photo
60 |
61 | """An edge for our `Photo`. May be used by Relay 1."""
62 | photoEdge(
63 | """The method to use when ordering `Photo`."""
64 | orderBy: [PhotosOrderBy!] = PRIMARY_KEY_ASC
65 | ): PhotosEdge
66 | }
67 |
68 | """All input for the create `User` mutation."""
69 | input CreateUserInput {
70 | """
71 | An arbitrary string value with no semantic meaning. Will be included in the
72 | payload verbatim. May be used to track mutations by the client.
73 | """
74 | clientMutationId: String
75 |
76 | """The `User` to be created by this mutation."""
77 | user: UserInput!
78 | }
79 |
80 | """The output of our create `User` mutation."""
81 | type CreateUserPayload {
82 | """
83 | The exact same `clientMutationId` that was provided in the mutation input,
84 | unchanged and unused. May be used by a client to track mutations.
85 | """
86 | clientMutationId: String
87 |
88 | """The `User` that was created by this mutation."""
89 | user: User
90 |
91 | """An edge for our `User`. May be used by Relay 1."""
92 | userEdge(
93 | """The method to use when ordering `User`."""
94 | orderBy: [UsersOrderBy!] = PRIMARY_KEY_ASC
95 | ): UsersEdge
96 | }
97 |
98 | """A location in a connection that can be used for resuming pagination."""
99 | scalar Cursor
100 |
101 | """All input for the `deleteMigrationById` mutation."""
102 | input DeleteMigrationByIdInput {
103 | """
104 | An arbitrary string value with no semantic meaning. Will be included in the
105 | payload verbatim. May be used to track mutations by the client.
106 | """
107 | clientMutationId: String
108 | id: Int!
109 | }
110 |
111 | """All input for the `deleteMigration` mutation."""
112 | input DeleteMigrationInput {
113 | """
114 | An arbitrary string value with no semantic meaning. Will be included in the
115 | payload verbatim. May be used to track mutations by the client.
116 | """
117 | clientMutationId: String
118 |
119 | """
120 | The globally unique `ID` which will identify a single `Migration` to be deleted.
121 | """
122 | nodeId: ID!
123 | }
124 |
125 | """The output of our delete `Migration` mutation."""
126 | type DeleteMigrationPayload {
127 | """
128 | The exact same `clientMutationId` that was provided in the mutation input,
129 | unchanged and unused. May be used by a client to track mutations.
130 | """
131 | clientMutationId: String
132 |
133 | """The `Migration` that was deleted by this mutation."""
134 | migration: Migration
135 | deletedMigrationId: ID
136 |
137 | """An edge for our `Migration`. May be used by Relay 1."""
138 | migrationEdge(
139 | """The method to use when ordering `Migration`."""
140 | orderBy: [MigrationsOrderBy!] = PRIMARY_KEY_ASC
141 | ): MigrationsEdge
142 | }
143 |
144 | """All input for the `deletePhotoById` mutation."""
145 | input DeletePhotoByIdInput {
146 | """
147 | An arbitrary string value with no semantic meaning. Will be included in the
148 | payload verbatim. May be used to track mutations by the client.
149 | """
150 | clientMutationId: String
151 | id: Int!
152 | }
153 |
154 | """All input for the `deletePhoto` mutation."""
155 | input DeletePhotoInput {
156 | """
157 | An arbitrary string value with no semantic meaning. Will be included in the
158 | payload verbatim. May be used to track mutations by the client.
159 | """
160 | clientMutationId: String
161 |
162 | """
163 | The globally unique `ID` which will identify a single `Photo` to be deleted.
164 | """
165 | nodeId: ID!
166 | }
167 |
168 | """The output of our delete `Photo` mutation."""
169 | type DeletePhotoPayload {
170 | """
171 | The exact same `clientMutationId` that was provided in the mutation input,
172 | unchanged and unused. May be used by a client to track mutations.
173 | """
174 | clientMutationId: String
175 |
176 | """The `Photo` that was deleted by this mutation."""
177 | photo: Photo
178 | deletedPhotoId: ID
179 |
180 | """An edge for our `Photo`. May be used by Relay 1."""
181 | photoEdge(
182 | """The method to use when ordering `Photo`."""
183 | orderBy: [PhotosOrderBy!] = PRIMARY_KEY_ASC
184 | ): PhotosEdge
185 | }
186 |
187 | """All input for the `deleteUserById` mutation."""
188 | input DeleteUserByIdInput {
189 | """
190 | An arbitrary string value with no semantic meaning. Will be included in the
191 | payload verbatim. May be used to track mutations by the client.
192 | """
193 | clientMutationId: String
194 | id: Int!
195 | }
196 |
197 | """All input for the `deleteUser` mutation."""
198 | input DeleteUserInput {
199 | """
200 | An arbitrary string value with no semantic meaning. Will be included in the
201 | payload verbatim. May be used to track mutations by the client.
202 | """
203 | clientMutationId: String
204 |
205 | """
206 | The globally unique `ID` which will identify a single `User` to be deleted.
207 | """
208 | nodeId: ID!
209 | }
210 |
211 | """The output of our delete `User` mutation."""
212 | type DeleteUserPayload {
213 | """
214 | The exact same `clientMutationId` that was provided in the mutation input,
215 | unchanged and unused. May be used by a client to track mutations.
216 | """
217 | clientMutationId: String
218 |
219 | """The `User` that was deleted by this mutation."""
220 | user: User
221 | deletedUserId: ID
222 |
223 | """An edge for our `User`. May be used by Relay 1."""
224 | userEdge(
225 | """The method to use when ordering `User`."""
226 | orderBy: [UsersOrderBy!] = PRIMARY_KEY_ASC
227 | ): UsersEdge
228 | }
229 |
230 | type Migration implements Node {
231 | """
232 | A globally unique identifier. Can be used in various places throughout the system to identify this single value.
233 | """
234 | nodeId: ID!
235 | id: Int!
236 | timestamp: BigInt!
237 | name: String!
238 | }
239 |
240 | """
241 | A condition to be used against `Migration` object types. All fields are tested
242 | for equality and combined with a logical ‘and.’
243 | """
244 | input MigrationCondition {
245 | """Checks for equality with the object’s `id` field."""
246 | id: Int
247 |
248 | """Checks for equality with the object’s `timestamp` field."""
249 | timestamp: BigInt
250 |
251 | """Checks for equality with the object’s `name` field."""
252 | name: String
253 | }
254 |
255 | """An input for mutations affecting `Migration`"""
256 | input MigrationInput {
257 | id: Int
258 | timestamp: BigInt!
259 | name: String!
260 | }
261 |
262 | """
263 | Represents an update to a `Migration`. Fields that are set will be updated.
264 | """
265 | input MigrationPatch {
266 | id: Int
267 | timestamp: BigInt
268 | name: String
269 | }
270 |
271 | """A connection to a list of `Migration` values."""
272 | type MigrationsConnection {
273 | """A list of `Migration` objects."""
274 | nodes: [Migration]!
275 |
276 | """
277 | A list of edges which contains the `Migration` and cursor to aid in pagination.
278 | """
279 | edges: [MigrationsEdge!]!
280 |
281 | """Information to aid in pagination."""
282 | pageInfo: PageInfo!
283 |
284 | """The count of *all* `Migration` you could get from the connection."""
285 | totalCount: Int
286 | }
287 |
288 | """A `Migration` edge in the connection."""
289 | type MigrationsEdge {
290 | """A cursor for use in pagination."""
291 | cursor: Cursor
292 |
293 | """The `Migration` at the end of the edge."""
294 | node: Migration
295 | }
296 |
297 | """Methods to use when ordering `Migration`."""
298 | enum MigrationsOrderBy {
299 | NATURAL
300 | ID_ASC
301 | ID_DESC
302 | TIMESTAMP_ASC
303 | TIMESTAMP_DESC
304 | NAME_ASC
305 | NAME_DESC
306 | PRIMARY_KEY_ASC
307 | PRIMARY_KEY_DESC
308 | }
309 |
310 | """
311 | The root mutation type which contains root level fields which mutate data.
312 | """
313 | type Mutation {
314 | """Creates a single `Migration`."""
315 | createMigration(
316 | """
317 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
318 | """
319 | input: CreateMigrationInput!
320 | ): CreateMigrationPayload
321 |
322 | """Creates a single `Photo`."""
323 | createPhoto(
324 | """
325 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
326 | """
327 | input: CreatePhotoInput!
328 | ): CreatePhotoPayload
329 |
330 | """Creates a single `User`."""
331 | createUser(
332 | """
333 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
334 | """
335 | input: CreateUserInput!
336 | ): CreateUserPayload
337 |
338 | """
339 | Updates a single `Migration` using its globally unique id and a patch.
340 | """
341 | updateMigration(
342 | """
343 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
344 | """
345 | input: UpdateMigrationInput!
346 | ): UpdateMigrationPayload
347 |
348 | """Updates a single `Migration` using a unique key and a patch."""
349 | updateMigrationById(
350 | """
351 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
352 | """
353 | input: UpdateMigrationByIdInput!
354 | ): UpdateMigrationPayload
355 |
356 | """Updates a single `Photo` using its globally unique id and a patch."""
357 | updatePhoto(
358 | """
359 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
360 | """
361 | input: UpdatePhotoInput!
362 | ): UpdatePhotoPayload
363 |
364 | """Updates a single `Photo` using a unique key and a patch."""
365 | updatePhotoById(
366 | """
367 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
368 | """
369 | input: UpdatePhotoByIdInput!
370 | ): UpdatePhotoPayload
371 |
372 | """Updates a single `User` using its globally unique id and a patch."""
373 | updateUser(
374 | """
375 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
376 | """
377 | input: UpdateUserInput!
378 | ): UpdateUserPayload
379 |
380 | """Updates a single `User` using a unique key and a patch."""
381 | updateUserById(
382 | """
383 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
384 | """
385 | input: UpdateUserByIdInput!
386 | ): UpdateUserPayload
387 |
388 | """Deletes a single `Migration` using its globally unique id."""
389 | deleteMigration(
390 | """
391 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
392 | """
393 | input: DeleteMigrationInput!
394 | ): DeleteMigrationPayload
395 |
396 | """Deletes a single `Migration` using a unique key."""
397 | deleteMigrationById(
398 | """
399 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
400 | """
401 | input: DeleteMigrationByIdInput!
402 | ): DeleteMigrationPayload
403 |
404 | """Deletes a single `Photo` using its globally unique id."""
405 | deletePhoto(
406 | """
407 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
408 | """
409 | input: DeletePhotoInput!
410 | ): DeletePhotoPayload
411 |
412 | """Deletes a single `Photo` using a unique key."""
413 | deletePhotoById(
414 | """
415 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
416 | """
417 | input: DeletePhotoByIdInput!
418 | ): DeletePhotoPayload
419 |
420 | """Deletes a single `User` using its globally unique id."""
421 | deleteUser(
422 | """
423 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
424 | """
425 | input: DeleteUserInput!
426 | ): DeleteUserPayload
427 |
428 | """Deletes a single `User` using a unique key."""
429 | deleteUserById(
430 | """
431 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
432 | """
433 | input: DeleteUserByIdInput!
434 | ): DeleteUserPayload
435 | }
436 |
437 | """An object with a globally unique `ID`."""
438 | interface Node {
439 | """
440 | A globally unique identifier. Can be used in various places throughout the system to identify this single value.
441 | """
442 | nodeId: ID!
443 | }
444 |
445 | """Information about pagination in a connection."""
446 | type PageInfo {
447 | """When paginating forwards, are there more items?"""
448 | hasNextPage: Boolean!
449 |
450 | """When paginating backwards, are there more items?"""
451 | hasPreviousPage: Boolean!
452 |
453 | """When paginating backwards, the cursor to continue."""
454 | startCursor: Cursor
455 |
456 | """When paginating forwards, the cursor to continue."""
457 | endCursor: Cursor
458 | }
459 |
460 | type Photo implements Node {
461 | """
462 | A globally unique identifier. Can be used in various places throughout the system to identify this single value.
463 | """
464 | nodeId: ID!
465 | id: Int!
466 | name: String!
467 | description: String!
468 | filename: String!
469 | views: Float!
470 | isPublished: Boolean!
471 | }
472 |
473 | """
474 | A condition to be used against `Photo` object types. All fields are tested for equality and combined with a logical ‘and.’
475 | """
476 | input PhotoCondition {
477 | """Checks for equality with the object’s `id` field."""
478 | id: Int
479 |
480 | """Checks for equality with the object’s `name` field."""
481 | name: String
482 |
483 | """Checks for equality with the object’s `description` field."""
484 | description: String
485 |
486 | """Checks for equality with the object’s `filename` field."""
487 | filename: String
488 |
489 | """Checks for equality with the object’s `views` field."""
490 | views: Float
491 |
492 | """Checks for equality with the object’s `isPublished` field."""
493 | isPublished: Boolean
494 | }
495 |
496 | """An input for mutations affecting `Photo`"""
497 | input PhotoInput {
498 | id: Int
499 | name: String!
500 | description: String!
501 | filename: String!
502 | views: Float!
503 | isPublished: Boolean!
504 | }
505 |
506 | """
507 | Represents an update to a `Photo`. Fields that are set will be updated.
508 | """
509 | input PhotoPatch {
510 | id: Int
511 | name: String
512 | description: String
513 | filename: String
514 | views: Float
515 | isPublished: Boolean
516 | }
517 |
518 | """A connection to a list of `Photo` values."""
519 | type PhotosConnection {
520 | """A list of `Photo` objects."""
521 | nodes: [Photo]!
522 |
523 | """
524 | A list of edges which contains the `Photo` and cursor to aid in pagination.
525 | """
526 | edges: [PhotosEdge!]!
527 |
528 | """Information to aid in pagination."""
529 | pageInfo: PageInfo!
530 |
531 | """The count of *all* `Photo` you could get from the connection."""
532 | totalCount: Int
533 | }
534 |
535 | """A `Photo` edge in the connection."""
536 | type PhotosEdge {
537 | """A cursor for use in pagination."""
538 | cursor: Cursor
539 |
540 | """The `Photo` at the end of the edge."""
541 | node: Photo
542 | }
543 |
544 | """Methods to use when ordering `Photo`."""
545 | enum PhotosOrderBy {
546 | NATURAL
547 | ID_ASC
548 | ID_DESC
549 | NAME_ASC
550 | NAME_DESC
551 | DESCRIPTION_ASC
552 | DESCRIPTION_DESC
553 | FILENAME_ASC
554 | FILENAME_DESC
555 | VIEWS_ASC
556 | VIEWS_DESC
557 | IS_PUBLISHED_ASC
558 | IS_PUBLISHED_DESC
559 | PRIMARY_KEY_ASC
560 | PRIMARY_KEY_DESC
561 | }
562 |
563 | """The root query type which gives access points into the data universe."""
564 | type Query implements Node {
565 | """
566 | Exposes the root query type nested one level down. This is helpful for Relay 1
567 | which can only query top level fields if they are in a particular form.
568 | """
569 | query: Query!
570 |
571 | """
572 | The root query type must be a `Node` to work well with Relay 1 mutations. This just resolves to `query`.
573 | """
574 | nodeId: ID!
575 |
576 | """Fetches an object given its globally unique `ID`."""
577 | node(
578 | """The globally unique `ID`."""
579 | nodeId: ID!
580 | ): Node
581 |
582 | """Reads and enables pagination through a set of `Migration`."""
583 | allMigrations(
584 | """Only read the first `n` values of the set."""
585 | first: Int
586 |
587 | """Only read the last `n` values of the set."""
588 | last: Int
589 |
590 | """
591 | Skip the first `n` values from our `after` cursor, an alternative to cursor
592 | based pagination. May not be used with `last`.
593 | """
594 | offset: Int
595 |
596 | """Read all values in the set before (above) this cursor."""
597 | before: Cursor
598 |
599 | """Read all values in the set after (below) this cursor."""
600 | after: Cursor
601 |
602 | """The method to use when ordering `Migration`."""
603 | orderBy: [MigrationsOrderBy!] = [PRIMARY_KEY_ASC]
604 |
605 | """
606 | A condition to be used in determining which values should be returned by the collection.
607 | """
608 | condition: MigrationCondition
609 | ): MigrationsConnection
610 |
611 | """Reads and enables pagination through a set of `Photo`."""
612 | allPhotos(
613 | """Only read the first `n` values of the set."""
614 | first: Int
615 |
616 | """Only read the last `n` values of the set."""
617 | last: Int
618 |
619 | """
620 | Skip the first `n` values from our `after` cursor, an alternative to cursor
621 | based pagination. May not be used with `last`.
622 | """
623 | offset: Int
624 |
625 | """Read all values in the set before (above) this cursor."""
626 | before: Cursor
627 |
628 | """Read all values in the set after (below) this cursor."""
629 | after: Cursor
630 |
631 | """The method to use when ordering `Photo`."""
632 | orderBy: [PhotosOrderBy!] = [PRIMARY_KEY_ASC]
633 |
634 | """
635 | A condition to be used in determining which values should be returned by the collection.
636 | """
637 | condition: PhotoCondition
638 | ): PhotosConnection
639 |
640 | """Reads and enables pagination through a set of `User`."""
641 | allUsers(
642 | """Only read the first `n` values of the set."""
643 | first: Int
644 |
645 | """Only read the last `n` values of the set."""
646 | last: Int
647 |
648 | """
649 | Skip the first `n` values from our `after` cursor, an alternative to cursor
650 | based pagination. May not be used with `last`.
651 | """
652 | offset: Int
653 |
654 | """Read all values in the set before (above) this cursor."""
655 | before: Cursor
656 |
657 | """Read all values in the set after (below) this cursor."""
658 | after: Cursor
659 |
660 | """The method to use when ordering `User`."""
661 | orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC]
662 |
663 | """
664 | A condition to be used in determining which values should be returned by the collection.
665 | """
666 | condition: UserCondition
667 | ): UsersConnection
668 | migrationById(id: Int!): Migration
669 | photoById(id: Int!): Photo
670 | userById(id: Int!): User
671 |
672 | """Reads a single `Migration` using its globally unique `ID`."""
673 | migration(
674 | """
675 | The globally unique `ID` to be used in selecting a single `Migration`.
676 | """
677 | nodeId: ID!
678 | ): Migration
679 |
680 | """Reads a single `Photo` using its globally unique `ID`."""
681 | photo(
682 | """The globally unique `ID` to be used in selecting a single `Photo`."""
683 | nodeId: ID!
684 | ): Photo
685 |
686 | """Reads a single `User` using its globally unique `ID`."""
687 | user(
688 | """The globally unique `ID` to be used in selecting a single `User`."""
689 | nodeId: ID!
690 | ): User
691 | }
692 |
693 | """All input for the `updateMigrationById` mutation."""
694 | input UpdateMigrationByIdInput {
695 | """
696 | An arbitrary string value with no semantic meaning. Will be included in the
697 | payload verbatim. May be used to track mutations by the client.
698 | """
699 | clientMutationId: String
700 |
701 | """
702 | An object where the defined keys will be set on the `Migration` being updated.
703 | """
704 | migrationPatch: MigrationPatch!
705 | id: Int!
706 | }
707 |
708 | """All input for the `updateMigration` mutation."""
709 | input UpdateMigrationInput {
710 | """
711 | An arbitrary string value with no semantic meaning. Will be included in the
712 | payload verbatim. May be used to track mutations by the client.
713 | """
714 | clientMutationId: String
715 |
716 | """
717 | The globally unique `ID` which will identify a single `Migration` to be updated.
718 | """
719 | nodeId: ID!
720 |
721 | """
722 | An object where the defined keys will be set on the `Migration` being updated.
723 | """
724 | migrationPatch: MigrationPatch!
725 | }
726 |
727 | """The output of our update `Migration` mutation."""
728 | type UpdateMigrationPayload {
729 | """
730 | The exact same `clientMutationId` that was provided in the mutation input,
731 | unchanged and unused. May be used by a client to track mutations.
732 | """
733 | clientMutationId: String
734 |
735 | """The `Migration` that was updated by this mutation."""
736 | migration: Migration
737 |
738 | """An edge for our `Migration`. May be used by Relay 1."""
739 | migrationEdge(
740 | """The method to use when ordering `Migration`."""
741 | orderBy: [MigrationsOrderBy!] = PRIMARY_KEY_ASC
742 | ): MigrationsEdge
743 | }
744 |
745 | """All input for the `updatePhotoById` mutation."""
746 | input UpdatePhotoByIdInput {
747 | """
748 | An arbitrary string value with no semantic meaning. Will be included in the
749 | payload verbatim. May be used to track mutations by the client.
750 | """
751 | clientMutationId: String
752 |
753 | """
754 | An object where the defined keys will be set on the `Photo` being updated.
755 | """
756 | photoPatch: PhotoPatch!
757 | id: Int!
758 | }
759 |
760 | """All input for the `updatePhoto` mutation."""
761 | input UpdatePhotoInput {
762 | """
763 | An arbitrary string value with no semantic meaning. Will be included in the
764 | payload verbatim. May be used to track mutations by the client.
765 | """
766 | clientMutationId: String
767 |
768 | """
769 | The globally unique `ID` which will identify a single `Photo` to be updated.
770 | """
771 | nodeId: ID!
772 |
773 | """
774 | An object where the defined keys will be set on the `Photo` being updated.
775 | """
776 | photoPatch: PhotoPatch!
777 | }
778 |
779 | """The output of our update `Photo` mutation."""
780 | type UpdatePhotoPayload {
781 | """
782 | The exact same `clientMutationId` that was provided in the mutation input,
783 | unchanged and unused. May be used by a client to track mutations.
784 | """
785 | clientMutationId: String
786 |
787 | """The `Photo` that was updated by this mutation."""
788 | photo: Photo
789 |
790 | """An edge for our `Photo`. May be used by Relay 1."""
791 | photoEdge(
792 | """The method to use when ordering `Photo`."""
793 | orderBy: [PhotosOrderBy!] = PRIMARY_KEY_ASC
794 | ): PhotosEdge
795 | }
796 |
797 | """All input for the `updateUserById` mutation."""
798 | input UpdateUserByIdInput {
799 | """
800 | An arbitrary string value with no semantic meaning. Will be included in the
801 | payload verbatim. May be used to track mutations by the client.
802 | """
803 | clientMutationId: String
804 |
805 | """
806 | An object where the defined keys will be set on the `User` being updated.
807 | """
808 | userPatch: UserPatch!
809 | id: Int!
810 | }
811 |
812 | """All input for the `updateUser` mutation."""
813 | input UpdateUserInput {
814 | """
815 | An arbitrary string value with no semantic meaning. Will be included in the
816 | payload verbatim. May be used to track mutations by the client.
817 | """
818 | clientMutationId: String
819 |
820 | """
821 | The globally unique `ID` which will identify a single `User` to be updated.
822 | """
823 | nodeId: ID!
824 |
825 | """
826 | An object where the defined keys will be set on the `User` being updated.
827 | """
828 | userPatch: UserPatch!
829 | }
830 |
831 | """The output of our update `User` mutation."""
832 | type UpdateUserPayload {
833 | """
834 | The exact same `clientMutationId` that was provided in the mutation input,
835 | unchanged and unused. May be used by a client to track mutations.
836 | """
837 | clientMutationId: String
838 |
839 | """The `User` that was updated by this mutation."""
840 | user: User
841 |
842 | """An edge for our `User`. May be used by Relay 1."""
843 | userEdge(
844 | """The method to use when ordering `User`."""
845 | orderBy: [UsersOrderBy!] = PRIMARY_KEY_ASC
846 | ): UsersEdge
847 | }
848 |
849 | type User implements Node {
850 | """
851 | A globally unique identifier. Can be used in various places throughout the system to identify this single value.
852 | """
853 | nodeId: ID!
854 | id: Int!
855 | name: String!
856 | email: String!
857 | password: String!
858 | }
859 |
860 | """
861 | A condition to be used against `User` object types. All fields are tested for equality and combined with a logical ‘and.’
862 | """
863 | input UserCondition {
864 | """Checks for equality with the object’s `id` field."""
865 | id: Int
866 |
867 | """Checks for equality with the object’s `name` field."""
868 | name: String
869 |
870 | """Checks for equality with the object’s `email` field."""
871 | email: String
872 |
873 | """Checks for equality with the object’s `password` field."""
874 | password: String
875 | }
876 |
877 | """An input for mutations affecting `User`"""
878 | input UserInput {
879 | id: Int
880 | name: String!
881 | email: String!
882 | password: String!
883 | }
884 |
885 | """
886 | Represents an update to a `User`. Fields that are set will be updated.
887 | """
888 | input UserPatch {
889 | id: Int
890 | name: String
891 | email: String
892 | password: String
893 | }
894 |
895 | """A connection to a list of `User` values."""
896 | type UsersConnection {
897 | """A list of `User` objects."""
898 | nodes: [User]!
899 |
900 | """
901 | A list of edges which contains the `User` and cursor to aid in pagination.
902 | """
903 | edges: [UsersEdge!]!
904 |
905 | """Information to aid in pagination."""
906 | pageInfo: PageInfo!
907 |
908 | """The count of *all* `User` you could get from the connection."""
909 | totalCount: Int
910 | }
911 |
912 | """A `User` edge in the connection."""
913 | type UsersEdge {
914 | """A cursor for use in pagination."""
915 | cursor: Cursor
916 |
917 | """The `User` at the end of the edge."""
918 | node: User
919 | }
920 |
921 | """Methods to use when ordering `User`."""
922 | enum UsersOrderBy {
923 | NATURAL
924 | ID_ASC
925 | ID_DESC
926 | NAME_ASC
927 | NAME_DESC
928 | EMAIL_ASC
929 | EMAIL_DESC
930 | PASSWORD_ASC
931 | PASSWORD_DESC
932 | PRIMARY_KEY_ASC
933 | PRIMARY_KEY_DESC
934 | }
935 |
--------------------------------------------------------------------------------
/src/generated/postgraphile.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | import { makeBindingClass, Options } from 'graphql-binding'
3 | import { GraphQLResolveInfo, GraphQLSchema } from 'graphql'
4 | import { IResolvers } from 'graphql-tools/dist/Interfaces'
5 | import schema from '../embeddedPostgraphile/index'
6 |
7 | export interface Query {
8 | query: (args?: {}, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
9 | nodeId: (args?: {}, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
10 | node: (args: { nodeId: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
11 | allMigrations: (args: { first?: Int, last?: Int, offset?: Int, before?: Cursor, after?: Cursor, orderBy?: MigrationsOrderBy[], condition?: MigrationCondition }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
12 | allPhotos: (args: { first?: Int, last?: Int, offset?: Int, before?: Cursor, after?: Cursor, orderBy?: PhotosOrderBy[], condition?: PhotoCondition }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
13 | allUsers: (args: { first?: Int, last?: Int, offset?: Int, before?: Cursor, after?: Cursor, orderBy?: UsersOrderBy[], condition?: UserCondition }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
14 | migrationById: (args: { id: Int }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
15 | photoById: (args: { id: Int }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
16 | userById: (args: { id: Int }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
17 | migration: (args: { nodeId: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
18 | photo: (args: { nodeId: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
19 | user: (args: { nodeId: ID_Output }, info?: GraphQLResolveInfo | string, options?: Options) => Promise
20 | }
21 |
22 | export interface Mutation {
23 | createMigration: (args: { input: CreateMigrationInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
24 | createPhoto: (args: { input: CreatePhotoInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
25 | createUser: (args: { input: CreateUserInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
26 | updateMigration: (args: { input: UpdateMigrationInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
27 | updateMigrationById: (args: { input: UpdateMigrationByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
28 | updatePhoto: (args: { input: UpdatePhotoInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
29 | updatePhotoById: (args: { input: UpdatePhotoByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
30 | updateUser: (args: { input: UpdateUserInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
31 | updateUserById: (args: { input: UpdateUserByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
32 | deleteMigration: (args: { input: DeleteMigrationInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
33 | deleteMigrationById: (args: { input: DeleteMigrationByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
34 | deletePhoto: (args: { input: DeletePhotoInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
35 | deletePhotoById: (args: { input: DeletePhotoByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
36 | deleteUser: (args: { input: DeleteUserInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise ,
37 | deleteUserById: (args: { input: DeleteUserByIdInput }, info?: GraphQLResolveInfo | string, options?: Options) => Promise
38 | }
39 |
40 | export interface Subscription {}
41 |
42 | export interface Binding {
43 | query: Query
44 | mutation: Mutation
45 | subscription: Subscription
46 | request: (query: string, variables?: {[key: string]: any}) => Promise
47 | delegate(operation: 'query' | 'mutation', fieldName: string, args: {
48 | [key: string]: any;
49 | }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options): Promise;
50 | delegateSubscription(fieldName: string, args?: {
51 | [key: string]: any;
52 | }, infoOrQuery?: GraphQLResolveInfo | string, options?: Options): Promise>;
53 | getAbstractResolvers(filterSchema?: GraphQLSchema | string): IResolvers;
54 | }
55 |
56 | export interface BindingConstructor {
57 | new(...args: any[]): T
58 | }
59 |
60 | export const Binding = makeBindingClass>({ schema })
61 |
62 | /**
63 | * Types
64 | */
65 |
66 | /*
67 | * Methods to use when ordering `Migration`.
68 |
69 | */
70 | export type MigrationsOrderBy = 'NATURAL' |
71 | 'ID_ASC' |
72 | 'ID_DESC' |
73 | 'TIMESTAMP_ASC' |
74 | 'TIMESTAMP_DESC' |
75 | 'NAME_ASC' |
76 | 'NAME_DESC' |
77 | 'PRIMARY_KEY_ASC' |
78 | 'PRIMARY_KEY_DESC'
79 |
80 | /*
81 | * Methods to use when ordering `Photo`.
82 |
83 | */
84 | export type PhotosOrderBy = 'NATURAL' |
85 | 'ID_ASC' |
86 | 'ID_DESC' |
87 | 'NAME_ASC' |
88 | 'NAME_DESC' |
89 | 'DESCRIPTION_ASC' |
90 | 'DESCRIPTION_DESC' |
91 | 'FILENAME_ASC' |
92 | 'FILENAME_DESC' |
93 | 'VIEWS_ASC' |
94 | 'VIEWS_DESC' |
95 | 'IS_PUBLISHED_ASC' |
96 | 'IS_PUBLISHED_DESC' |
97 | 'PRIMARY_KEY_ASC' |
98 | 'PRIMARY_KEY_DESC'
99 |
100 | /*
101 | * Methods to use when ordering `User`.
102 |
103 | */
104 | export type UsersOrderBy = 'NATURAL' |
105 | 'ID_ASC' |
106 | 'ID_DESC' |
107 | 'NAME_ASC' |
108 | 'NAME_DESC' |
109 | 'EMAIL_ASC' |
110 | 'EMAIL_DESC' |
111 | 'PASSWORD_ASC' |
112 | 'PASSWORD_DESC' |
113 | 'PRIMARY_KEY_ASC' |
114 | 'PRIMARY_KEY_DESC'
115 |
116 | /*
117 | * All input for the create `Photo` mutation.
118 |
119 | */
120 | export interface CreatePhotoInput {
121 | clientMutationId?: String
122 | photo: PhotoInput
123 | }
124 |
125 | /*
126 | * All input for the `deleteUserById` mutation.
127 |
128 | */
129 | export interface DeleteUserByIdInput {
130 | clientMutationId?: String
131 | id: Int
132 | }
133 |
134 | /*
135 | * Represents an update to a `Photo`. Fields that are set will be updated.
136 |
137 | */
138 | export interface PhotoPatch {
139 | id?: Int
140 | name?: String
141 | description?: String
142 | filename?: String
143 | views?: Float
144 | isPublished?: Boolean
145 | }
146 |
147 | /*
148 | * An input for mutations affecting `Photo`
149 |
150 | */
151 | export interface PhotoInput {
152 | id?: Int
153 | name: String
154 | description: String
155 | filename: String
156 | views: Float
157 | isPublished: Boolean
158 | }
159 |
160 | /*
161 | * All input for the `updatePhoto` mutation.
162 |
163 | */
164 | export interface UpdatePhotoInput {
165 | clientMutationId?: String
166 | nodeId: ID_Input
167 | photoPatch: PhotoPatch
168 | }
169 |
170 | /*
171 | * A condition to be used against `Migration` object types. All fields are tested
172 | , * for equality and combined with a logical ‘and.’
173 |
174 | */
175 | export interface MigrationCondition {
176 | id?: Int
177 | timestamp?: BigInt
178 | name?: String
179 | }
180 |
181 | /*
182 | * All input for the `updateMigrationById` mutation.
183 |
184 | */
185 | export interface UpdateMigrationByIdInput {
186 | clientMutationId?: String
187 | migrationPatch: MigrationPatch
188 | id: Int
189 | }
190 |
191 | /*
192 | * All input for the `deletePhotoById` mutation.
193 |
194 | */
195 | export interface DeletePhotoByIdInput {
196 | clientMutationId?: String
197 | id: Int
198 | }
199 |
200 | /*
201 | * A condition to be used against `User` object types. All fields are tested for equality and combined with a logical ‘and.’
202 |
203 | */
204 | export interface UserCondition {
205 | id?: Int
206 | name?: String
207 | email?: String
208 | password?: String
209 | }
210 |
211 | /*
212 | * All input for the `deleteMigrationById` mutation.
213 |
214 | */
215 | export interface DeleteMigrationByIdInput {
216 | clientMutationId?: String
217 | id: Int
218 | }
219 |
220 | /*
221 | * Represents an update to a `Migration`. Fields that are set will be updated.
222 |
223 | */
224 | export interface MigrationPatch {
225 | id?: Int
226 | timestamp?: BigInt
227 | name?: String
228 | }
229 |
230 | /*
231 | * All input for the `updateUserById` mutation.
232 |
233 | */
234 | export interface UpdateUserByIdInput {
235 | clientMutationId?: String
236 | userPatch: UserPatch
237 | id: Int
238 | }
239 |
240 | /*
241 | * All input for the `updateMigration` mutation.
242 |
243 | */
244 | export interface UpdateMigrationInput {
245 | clientMutationId?: String
246 | nodeId: ID_Input
247 | migrationPatch: MigrationPatch
248 | }
249 |
250 | /*
251 | * A condition to be used against `Photo` object types. All fields are tested for equality and combined with a logical ‘and.’
252 |
253 | */
254 | export interface PhotoCondition {
255 | id?: Int
256 | name?: String
257 | description?: String
258 | filename?: String
259 | views?: Float
260 | isPublished?: Boolean
261 | }
262 |
263 | /*
264 | * All input for the `updatePhotoById` mutation.
265 |
266 | */
267 | export interface UpdatePhotoByIdInput {
268 | clientMutationId?: String
269 | photoPatch: PhotoPatch
270 | id: Int
271 | }
272 |
273 | /*
274 | * All input for the create `User` mutation.
275 |
276 | */
277 | export interface CreateUserInput {
278 | clientMutationId?: String
279 | user: UserInput
280 | }
281 |
282 | /*
283 | * An input for mutations affecting `Migration`
284 |
285 | */
286 | export interface MigrationInput {
287 | id?: Int
288 | timestamp: BigInt
289 | name: String
290 | }
291 |
292 | /*
293 | * All input for the create `Migration` mutation.
294 |
295 | */
296 | export interface CreateMigrationInput {
297 | clientMutationId?: String
298 | migration: MigrationInput
299 | }
300 |
301 | /*
302 | * An input for mutations affecting `User`
303 |
304 | */
305 | export interface UserInput {
306 | id?: Int
307 | name: String
308 | email: String
309 | password: String
310 | }
311 |
312 | /*
313 | * All input for the `deleteUser` mutation.
314 |
315 | */
316 | export interface DeleteUserInput {
317 | clientMutationId?: String
318 | nodeId: ID_Input
319 | }
320 |
321 | /*
322 | * All input for the `updateUser` mutation.
323 |
324 | */
325 | export interface UpdateUserInput {
326 | clientMutationId?: String
327 | nodeId: ID_Input
328 | userPatch: UserPatch
329 | }
330 |
331 | /*
332 | * Represents an update to a `User`. Fields that are set will be updated.
333 |
334 | */
335 | export interface UserPatch {
336 | id?: Int
337 | name?: String
338 | email?: String
339 | password?: String
340 | }
341 |
342 | /*
343 | * All input for the `deleteMigration` mutation.
344 |
345 | */
346 | export interface DeleteMigrationInput {
347 | clientMutationId?: String
348 | nodeId: ID_Input
349 | }
350 |
351 | /*
352 | * All input for the `deletePhoto` mutation.
353 |
354 | */
355 | export interface DeletePhotoInput {
356 | clientMutationId?: String
357 | nodeId: ID_Input
358 | }
359 |
360 | /*
361 | * An object with a globally unique `ID`.
362 |
363 | */
364 | export interface Node {
365 | nodeId: ID_Output
366 | }
367 |
368 | export interface Photo extends Node {
369 | nodeId: ID_Output
370 | id: Int
371 | name: String
372 | description: String
373 | filename: String
374 | views: Float
375 | isPublished: Boolean
376 | }
377 |
378 | /*
379 | * A `Migration` edge in the connection.
380 |
381 | */
382 | export interface MigrationsEdge {
383 | cursor?: Cursor
384 | node?: Migration
385 | }
386 |
387 | /*
388 | * The output of our update `Photo` mutation.
389 |
390 | */
391 | export interface UpdatePhotoPayload {
392 | clientMutationId?: String
393 | photo?: Photo
394 | photoEdge?: PhotosEdge
395 | }
396 |
397 | /*
398 | * The output of our create `Photo` mutation.
399 |
400 | */
401 | export interface CreatePhotoPayload {
402 | clientMutationId?: String
403 | photo?: Photo
404 | photoEdge?: PhotosEdge
405 | }
406 |
407 | /*
408 | * A connection to a list of `Photo` values.
409 |
410 | */
411 | export interface PhotosConnection {
412 | nodes: Photo[]
413 | edges: PhotosEdge[]
414 | pageInfo: PageInfo
415 | totalCount?: Int
416 | }
417 |
418 | /*
419 | * A `User` edge in the connection.
420 |
421 | */
422 | export interface UsersEdge {
423 | cursor?: Cursor
424 | node?: User
425 | }
426 |
427 | /*
428 | * A connection to a list of `Migration` values.
429 |
430 | */
431 | export interface MigrationsConnection {
432 | nodes: Migration[]
433 | edges: MigrationsEdge[]
434 | pageInfo: PageInfo
435 | totalCount?: Int
436 | }
437 |
438 | export interface User extends Node {
439 | nodeId: ID_Output
440 | id: Int
441 | name: String
442 | email: String
443 | password: String
444 | }
445 |
446 | /*
447 | * The output of our delete `Photo` mutation.
448 |
449 | */
450 | export interface DeletePhotoPayload {
451 | clientMutationId?: String
452 | photo?: Photo
453 | deletedPhotoId?: ID_Output
454 | photoEdge?: PhotosEdge
455 | }
456 |
457 | /*
458 | * The output of our update `Migration` mutation.
459 |
460 | */
461 | export interface UpdateMigrationPayload {
462 | clientMutationId?: String
463 | migration?: Migration
464 | migrationEdge?: MigrationsEdge
465 | }
466 |
467 | /*
468 | * A `Photo` edge in the connection.
469 |
470 | */
471 | export interface PhotosEdge {
472 | cursor?: Cursor
473 | node?: Photo
474 | }
475 |
476 | /*
477 | * The output of our delete `Migration` mutation.
478 |
479 | */
480 | export interface DeleteMigrationPayload {
481 | clientMutationId?: String
482 | migration?: Migration
483 | deletedMigrationId?: ID_Output
484 | migrationEdge?: MigrationsEdge
485 | }
486 |
487 | /*
488 | * Information about pagination in a connection.
489 |
490 | */
491 | export interface PageInfo {
492 | hasNextPage: Boolean
493 | hasPreviousPage: Boolean
494 | startCursor?: Cursor
495 | endCursor?: Cursor
496 | }
497 |
498 | export interface Migration extends Node {
499 | nodeId: ID_Output
500 | id: Int
501 | timestamp: BigInt
502 | name: String
503 | }
504 |
505 | /*
506 | * The output of our update `User` mutation.
507 |
508 | */
509 | export interface UpdateUserPayload {
510 | clientMutationId?: String
511 | user?: User
512 | userEdge?: UsersEdge
513 | }
514 |
515 | /*
516 | * The output of our create `Migration` mutation.
517 |
518 | */
519 | export interface CreateMigrationPayload {
520 | clientMutationId?: String
521 | migration?: Migration
522 | migrationEdge?: MigrationsEdge
523 | }
524 |
525 | /*
526 | * The output of our delete `User` mutation.
527 |
528 | */
529 | export interface DeleteUserPayload {
530 | clientMutationId?: String
531 | user?: User
532 | deletedUserId?: ID_Output
533 | userEdge?: UsersEdge
534 | }
535 |
536 | /*
537 | * A connection to a list of `User` values.
538 |
539 | */
540 | export interface UsersConnection {
541 | nodes: User[]
542 | edges: UsersEdge[]
543 | pageInfo: PageInfo
544 | totalCount?: Int
545 | }
546 |
547 | /*
548 | * The output of our create `User` mutation.
549 |
550 | */
551 | export interface CreateUserPayload {
552 | clientMutationId?: String
553 | user?: User
554 | userEdge?: UsersEdge
555 | }
556 |
557 | /*
558 | The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).
559 | */
560 | export type Float = number
561 |
562 | /*
563 | The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
564 | */
565 | export type ID_Input = string | number
566 | export type ID_Output = string
567 |
568 | /*
569 | The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
570 | */
571 | export type Int = number
572 |
573 | /*
574 | The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
575 | */
576 | export type String = string
577 |
578 | /*
579 | A signed eight-byte integer. The upper big integer values are greater then the
580 | max value for a JavaScript number. Therefore all big integers will be output as
581 | strings and not numbers.
582 | */
583 | export type BigInt = string
584 |
585 | /*
586 | A location in a connection that can be used for resuming pagination.
587 | */
588 | export type Cursor = string
589 |
590 | /*
591 | The `Boolean` scalar type represents `true` or `false`.
592 | */
593 | export type Boolean = boolean
--------------------------------------------------------------------------------
/src/generated/types.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | import { GraphQLResolveInfo } from "graphql";
3 |
4 | export type Resolver = (
5 | parent?: Parent,
6 | args?: Args,
7 | context?: Context,
8 | info?: GraphQLResolveInfo
9 | ) => Promise | Result;
10 |
11 | export type SubscriptionResolver<
12 | Result,
13 | Parent = any,
14 | Context = any,
15 | Args = any
16 | > = {
17 | subscribe(
18 | parent?: P,
19 | args?: Args,
20 | context?: Context,
21 | info?: GraphQLResolveInfo
22 | ): AsyncIterator;
23 | resolve?(
24 | parent?: P,
25 | args?: Args,
26 | context?: Context,
27 | info?: GraphQLResolveInfo
28 | ): R | Result | Promise;
29 | };
30 |
31 | /** A location in a connection that can be used for resuming pagination. */
32 | export type Cursor = any;
33 |
34 | /** A signed eight-byte integer. The upper big integer values are greater then themax value for a JavaScript number. Therefore all big integers will be output asstrings and not numbers. */
35 | export type BigInt = any;
36 | /** An object with a globally unique `ID`. */
37 | export interface Node {
38 | nodeId: string /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */;
39 | }
40 |
41 | export interface Query {
42 | me?: User | null;
43 | allPhotos?: PhotosConnection | null /** Reads and enables pagination through a set of `Photo`. */;
44 | }
45 |
46 | export interface User {
47 | id: string;
48 | email: string;
49 | name: string;
50 | }
51 | /** A connection to a list of `Photo` values. */
52 | export interface PhotosConnection {
53 | nodes: (Photo | null)[] /** A list of `Photo` objects. */;
54 | edges: PhotosEdge[] /** A list of edges which contains the `Photo` and cursor to aid in pagination. */;
55 | pageInfo: PageInfo /** Information to aid in pagination. */;
56 | totalCount?:
57 | | number
58 | | null /** The count of *all* `Photo` you could get from the connection. */;
59 | }
60 |
61 | export interface Photo extends Node {
62 | nodeId: string /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */;
63 | id: number;
64 | name: string;
65 | description: string;
66 | filename: string;
67 | views: number;
68 | isPublished: boolean;
69 | }
70 | /** A `Photo` edge in the connection. */
71 | export interface PhotosEdge {
72 | cursor?: Cursor | null /** A cursor for use in pagination. */;
73 | node?: Photo | null /** The `Photo` at the end of the edge. */;
74 | }
75 | /** Information about pagination in a connection. */
76 | export interface PageInfo {
77 | hasNextPage: boolean /** When paginating forwards, are there more items? */;
78 | hasPreviousPage: boolean /** When paginating backwards, are there more items? */;
79 | startCursor?: Cursor | null /** When paginating backwards, the cursor to continue. */;
80 | endCursor?: Cursor | null /** When paginating forwards, the cursor to continue. */;
81 | }
82 |
83 | export interface Mutation {
84 | signup: AuthPayload;
85 | login: AuthPayload;
86 | createPhoto?: CreatePhotoPayload | null /** Creates a single `Photo`. */;
87 | deletePhoto?: DeletePhotoPayload | null /** Deletes a single `Photo` using its globally unique id. */;
88 | }
89 |
90 | export interface AuthPayload {
91 | token: string;
92 | user: User;
93 | }
94 | /** The output of our create `Photo` mutation. */
95 | export interface CreatePhotoPayload {
96 | clientMutationId?:
97 | | string
98 | | null /** The exact same `clientMutationId` that was provided in the mutation input,unchanged and unused. May be used by a client to track mutations. */;
99 | photo?: Photo | null /** The `Photo` that was created by this mutation. */;
100 | photoEdge?: PhotosEdge | null /** An edge for our `Photo`. May be used by Relay 1. */;
101 | }
102 | /** The output of our delete `Photo` mutation. */
103 | export interface DeletePhotoPayload {
104 | clientMutationId?:
105 | | string
106 | | null /** The exact same `clientMutationId` that was provided in the mutation input,unchanged and unused. May be used by a client to track mutations. */;
107 | photo?: Photo | null /** The `Photo` that was deleted by this mutation. */;
108 | deletedPhotoId?: string | null;
109 | photoEdge?: PhotosEdge | null /** An edge for our `Photo`. May be used by Relay 1. */;
110 | }
111 |
112 | export interface Migration extends Node {
113 | nodeId: string /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */;
114 | id: number;
115 | timestamp: BigInt;
116 | name: string;
117 | }
118 | /** A condition to be used against `Photo` object types. All fields are tested for equality and combined with a logical ‘and.’ */
119 | export interface PhotoCondition {
120 | id?: number | null /** Checks for equality with the object’s `id` field. */;
121 | name?:
122 | | string
123 | | null /** Checks for equality with the object’s `name` field. */;
124 | description?:
125 | | string
126 | | null /** Checks for equality with the object’s `description` field. */;
127 | filename?:
128 | | string
129 | | null /** Checks for equality with the object’s `filename` field. */;
130 | views?:
131 | | number
132 | | null /** Checks for equality with the object’s `views` field. */;
133 | isPublished?:
134 | | boolean
135 | | null /** Checks for equality with the object’s `isPublished` field. */;
136 | }
137 | /** All input for the create `Photo` mutation. */
138 | export interface CreatePhotoInput {
139 | clientMutationId?:
140 | | string
141 | | null /** An arbitrary string value with no semantic meaning. Will be included in thepayload verbatim. May be used to track mutations by the client. */;
142 | photo: PhotoInput /** The `Photo` to be created by this mutation. */;
143 | }
144 | /** An input for mutations affecting `Photo` */
145 | export interface PhotoInput {
146 | id?: number | null;
147 | name: string;
148 | description: string;
149 | filename: string;
150 | views: number;
151 | isPublished: boolean;
152 | }
153 | /** All input for the `deletePhoto` mutation. */
154 | export interface DeletePhotoInput {
155 | clientMutationId?:
156 | | string
157 | | null /** An arbitrary string value with no semantic meaning. Will be included in thepayload verbatim. May be used to track mutations by the client. */;
158 | nodeId: string /** The globally unique `ID` which will identify a single `Photo` to be deleted. */;
159 | }
160 | export interface AllPhotosQueryArgs {
161 | first?: number | null /** Only read the first `n` values of the set. */;
162 | last?: number | null /** Only read the last `n` values of the set. */;
163 | offset?:
164 | | number
165 | | null /** Skip the first `n` values from our `after` cursor, an alternative to cursorbased pagination. May not be used with `last`. */;
166 | before?: Cursor | null /** Read all values in the set before (above) this cursor. */;
167 | after?: Cursor | null /** Read all values in the set after (below) this cursor. */;
168 | orderBy?:
169 | | PhotosOrderBy[]
170 | | null /** The method to use when ordering `Photo`. */;
171 | condition?: PhotoCondition | null /** A condition to be used in determining which values should be returned by the collection. */;
172 | }
173 | export interface SignupMutationArgs {
174 | email: string;
175 | password: string;
176 | name: string;
177 | }
178 | export interface LoginMutationArgs {
179 | email: string;
180 | password: string;
181 | }
182 | export interface CreatePhotoMutationArgs {
183 | input: CreatePhotoInput /** The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. */;
184 | }
185 | export interface DeletePhotoMutationArgs {
186 | input: DeletePhotoInput /** The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. */;
187 | }
188 | export interface PhotoEdgeCreatePhotoPayloadArgs {
189 | orderBy?:
190 | | PhotosOrderBy[]
191 | | null /** The method to use when ordering `Photo`. */;
192 | }
193 | export interface PhotoEdgeDeletePhotoPayloadArgs {
194 | orderBy?:
195 | | PhotosOrderBy[]
196 | | null /** The method to use when ordering `Photo`. */;
197 | }
198 | /** Methods to use when ordering `Photo`. */
199 | export type PhotosOrderBy =
200 | | "NATURAL"
201 | | "ID_ASC"
202 | | "ID_DESC"
203 | | "NAME_ASC"
204 | | "NAME_DESC"
205 | | "DESCRIPTION_ASC"
206 | | "DESCRIPTION_DESC"
207 | | "FILENAME_ASC"
208 | | "FILENAME_DESC"
209 | | "VIEWS_ASC"
210 | | "VIEWS_DESC"
211 | | "IS_PUBLISHED_ASC"
212 | | "IS_PUBLISHED_DESC"
213 | | "PRIMARY_KEY_ASC"
214 | | "PRIMARY_KEY_DESC";
215 |
216 | export namespace QueryResolvers {
217 | export interface Resolvers {
218 | me?: MeResolver;
219 | allPhotos?: AllPhotosResolver<
220 | PhotosConnection | null,
221 | any,
222 | Context
223 | > /** Reads and enables pagination through a set of `Photo`. */;
224 | }
225 |
226 | export type MeResolver<
227 | R = User | null,
228 | Parent = any,
229 | Context = any
230 | > = Resolver;
231 | export type AllPhotosResolver<
232 | R = PhotosConnection | null,
233 | Parent = any,
234 | Context = any
235 | > = Resolver;
236 | export interface AllPhotosArgs {
237 | first?: number | null /** Only read the first `n` values of the set. */;
238 | last?: number | null /** Only read the last `n` values of the set. */;
239 | offset?:
240 | | number
241 | | null /** Skip the first `n` values from our `after` cursor, an alternative to cursorbased pagination. May not be used with `last`. */;
242 | before?: Cursor | null /** Read all values in the set before (above) this cursor. */;
243 | after?: Cursor | null /** Read all values in the set after (below) this cursor. */;
244 | orderBy?:
245 | | PhotosOrderBy[]
246 | | null /** The method to use when ordering `Photo`. */;
247 | condition?: PhotoCondition | null /** A condition to be used in determining which values should be returned by the collection. */;
248 | }
249 | }
250 |
251 | export namespace UserResolvers {
252 | export interface Resolvers {
253 | id?: IdResolver;
254 | email?: EmailResolver;
255 | name?: NameResolver;
256 | }
257 |
258 | export type IdResolver = Resolver<
259 | R,
260 | Parent,
261 | Context
262 | >;
263 | export type EmailResolver = Resolver<
264 | R,
265 | Parent,
266 | Context
267 | >;
268 | export type NameResolver = Resolver<
269 | R,
270 | Parent,
271 | Context
272 | >;
273 | }
274 | /** A connection to a list of `Photo` values. */
275 | export namespace PhotosConnectionResolvers {
276 | export interface Resolvers {
277 | nodes?: NodesResolver<
278 | (Photo | null)[],
279 | any,
280 | Context
281 | > /** A list of `Photo` objects. */;
282 | edges?: EdgesResolver<
283 | PhotosEdge[],
284 | any,
285 | Context
286 | > /** A list of edges which contains the `Photo` and cursor to aid in pagination. */;
287 | pageInfo?: PageInfoResolver<
288 | PageInfo,
289 | any,
290 | Context
291 | > /** Information to aid in pagination. */;
292 | totalCount?: TotalCountResolver<
293 | number | null,
294 | any,
295 | Context
296 | > /** The count of *all* `Photo` you could get from the connection. */;
297 | }
298 |
299 | export type NodesResolver<
300 | R = (Photo | null)[],
301 | Parent = any,
302 | Context = any
303 | > = Resolver;
304 | export type EdgesResolver<
305 | R = PhotosEdge[],
306 | Parent = any,
307 | Context = any
308 | > = Resolver;
309 | export type PageInfoResolver<
310 | R = PageInfo,
311 | Parent = any,
312 | Context = any
313 | > = Resolver;
314 | export type TotalCountResolver<
315 | R = number | null,
316 | Parent = any,
317 | Context = any
318 | > = Resolver;
319 | }
320 |
321 | export namespace PhotoResolvers {
322 | export interface Resolvers {
323 | nodeId?: NodeIdResolver<
324 | string,
325 | any,
326 | Context
327 | > /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */;
328 | id?: IdResolver;
329 | name?: NameResolver;
330 | description?: DescriptionResolver;
331 | filename?: FilenameResolver;
332 | views?: ViewsResolver;
333 | isPublished?: IsPublishedResolver;
334 | }
335 |
336 | export type NodeIdResolver<
337 | R = string,
338 | Parent = any,
339 | Context = any
340 | > = Resolver;
341 | export type IdResolver = Resolver<
342 | R,
343 | Parent,
344 | Context
345 | >;
346 | export type NameResolver = Resolver<
347 | R,
348 | Parent,
349 | Context
350 | >;
351 | export type DescriptionResolver<
352 | R = string,
353 | Parent = any,
354 | Context = any
355 | > = Resolver;
356 | export type FilenameResolver<
357 | R = string,
358 | Parent = any,
359 | Context = any
360 | > = Resolver;
361 | export type ViewsResolver = Resolver<
362 | R,
363 | Parent,
364 | Context
365 | >;
366 | export type IsPublishedResolver<
367 | R = boolean,
368 | Parent = any,
369 | Context = any
370 | > = Resolver;
371 | }
372 | /** A `Photo` edge in the connection. */
373 | export namespace PhotosEdgeResolvers {
374 | export interface Resolvers {
375 | cursor?: CursorResolver<
376 | Cursor | null,
377 | any,
378 | Context
379 | > /** A cursor for use in pagination. */;
380 | node?: NodeResolver<
381 | Photo | null,
382 | any,
383 | Context
384 | > /** The `Photo` at the end of the edge. */;
385 | }
386 |
387 | export type CursorResolver<
388 | R = Cursor | null,
389 | Parent = any,
390 | Context = any
391 | > = Resolver;
392 | export type NodeResolver<
393 | R = Photo | null,
394 | Parent = any,
395 | Context = any
396 | > = Resolver;
397 | }
398 | /** Information about pagination in a connection. */
399 | export namespace PageInfoResolvers {
400 | export interface Resolvers {
401 | hasNextPage?: HasNextPageResolver<
402 | boolean,
403 | any,
404 | Context
405 | > /** When paginating forwards, are there more items? */;
406 | hasPreviousPage?: HasPreviousPageResolver<
407 | boolean,
408 | any,
409 | Context
410 | > /** When paginating backwards, are there more items? */;
411 | startCursor?: StartCursorResolver<
412 | Cursor | null,
413 | any,
414 | Context
415 | > /** When paginating backwards, the cursor to continue. */;
416 | endCursor?: EndCursorResolver<
417 | Cursor | null,
418 | any,
419 | Context
420 | > /** When paginating forwards, the cursor to continue. */;
421 | }
422 |
423 | export type HasNextPageResolver<
424 | R = boolean,
425 | Parent = any,
426 | Context = any
427 | > = Resolver;
428 | export type HasPreviousPageResolver<
429 | R = boolean,
430 | Parent = any,
431 | Context = any
432 | > = Resolver;
433 | export type StartCursorResolver<
434 | R = Cursor | null,
435 | Parent = any,
436 | Context = any
437 | > = Resolver;
438 | export type EndCursorResolver<
439 | R = Cursor | null,
440 | Parent = any,
441 | Context = any
442 | > = Resolver;
443 | }
444 |
445 | export namespace MutationResolvers {
446 | export interface Resolvers {
447 | signup?: SignupResolver;
448 | login?: LoginResolver;
449 | createPhoto?: CreatePhotoResolver<
450 | CreatePhotoPayload | null,
451 | any,
452 | Context
453 | > /** Creates a single `Photo`. */;
454 | deletePhoto?: DeletePhotoResolver<
455 | DeletePhotoPayload | null,
456 | any,
457 | Context
458 | > /** Deletes a single `Photo` using its globally unique id. */;
459 | }
460 |
461 | export type SignupResolver<
462 | R = AuthPayload,
463 | Parent = any,
464 | Context = any
465 | > = Resolver;
466 | export interface SignupArgs {
467 | email: string;
468 | password: string;
469 | name: string;
470 | }
471 |
472 | export type LoginResolver<
473 | R = AuthPayload,
474 | Parent = any,
475 | Context = any
476 | > = Resolver;
477 | export interface LoginArgs {
478 | email: string;
479 | password: string;
480 | }
481 |
482 | export type CreatePhotoResolver<
483 | R = CreatePhotoPayload | null,
484 | Parent = any,
485 | Context = any
486 | > = Resolver;
487 | export interface CreatePhotoArgs {
488 | input: CreatePhotoInput /** The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. */;
489 | }
490 |
491 | export type DeletePhotoResolver<
492 | R = DeletePhotoPayload | null,
493 | Parent = any,
494 | Context = any
495 | > = Resolver;
496 | export interface DeletePhotoArgs {
497 | input: DeletePhotoInput /** The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. */;
498 | }
499 | }
500 |
501 | export namespace AuthPayloadResolvers {
502 | export interface Resolvers {
503 | token?: TokenResolver;
504 | user?: UserResolver;
505 | }
506 |
507 | export type TokenResolver = Resolver<
508 | R,
509 | Parent,
510 | Context
511 | >;
512 | export type UserResolver = Resolver<
513 | R,
514 | Parent,
515 | Context
516 | >;
517 | }
518 | /** The output of our create `Photo` mutation. */
519 | export namespace CreatePhotoPayloadResolvers {
520 | export interface Resolvers {
521 | clientMutationId?: ClientMutationIdResolver<
522 | string | null,
523 | any,
524 | Context
525 | > /** The exact same `clientMutationId` that was provided in the mutation input,unchanged and unused. May be used by a client to track mutations. */;
526 | photo?: PhotoResolver<
527 | Photo | null,
528 | any,
529 | Context
530 | > /** The `Photo` that was created by this mutation. */;
531 | photoEdge?: PhotoEdgeResolver<
532 | PhotosEdge | null,
533 | any,
534 | Context
535 | > /** An edge for our `Photo`. May be used by Relay 1. */;
536 | }
537 |
538 | export type ClientMutationIdResolver<
539 | R = string | null,
540 | Parent = any,
541 | Context = any
542 | > = Resolver;
543 | export type PhotoResolver<
544 | R = Photo | null,
545 | Parent = any,
546 | Context = any
547 | > = Resolver;
548 | export type PhotoEdgeResolver<
549 | R = PhotosEdge | null,
550 | Parent = any,
551 | Context = any
552 | > = Resolver;
553 | export interface PhotoEdgeArgs {
554 | orderBy?:
555 | | PhotosOrderBy[]
556 | | null /** The method to use when ordering `Photo`. */;
557 | }
558 | }
559 | /** The output of our delete `Photo` mutation. */
560 | export namespace DeletePhotoPayloadResolvers {
561 | export interface Resolvers {
562 | clientMutationId?: ClientMutationIdResolver<
563 | string | null,
564 | any,
565 | Context
566 | > /** The exact same `clientMutationId` that was provided in the mutation input,unchanged and unused. May be used by a client to track mutations. */;
567 | photo?: PhotoResolver<
568 | Photo | null,
569 | any,
570 | Context
571 | > /** The `Photo` that was deleted by this mutation. */;
572 | deletedPhotoId?: DeletedPhotoIdResolver;
573 | photoEdge?: PhotoEdgeResolver<
574 | PhotosEdge | null,
575 | any,
576 | Context
577 | > /** An edge for our `Photo`. May be used by Relay 1. */;
578 | }
579 |
580 | export type ClientMutationIdResolver<
581 | R = string | null,
582 | Parent = any,
583 | Context = any
584 | > = Resolver;
585 | export type PhotoResolver<
586 | R = Photo | null,
587 | Parent = any,
588 | Context = any
589 | > = Resolver;
590 | export type DeletedPhotoIdResolver<
591 | R = string | null,
592 | Parent = any,
593 | Context = any
594 | > = Resolver;
595 | export type PhotoEdgeResolver<
596 | R = PhotosEdge | null,
597 | Parent = any,
598 | Context = any
599 | > = Resolver;
600 | export interface PhotoEdgeArgs {
601 | orderBy?:
602 | | PhotosOrderBy[]
603 | | null /** The method to use when ordering `Photo`. */;
604 | }
605 | }
606 |
607 | export namespace MigrationResolvers {
608 | export interface Resolvers {
609 | nodeId?: NodeIdResolver<
610 | string,
611 | any,
612 | Context
613 | > /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */;
614 | id?: IdResolver;
615 | timestamp?: TimestampResolver;
616 | name?: NameResolver;
617 | }
618 |
619 | export type NodeIdResolver<
620 | R = string,
621 | Parent = any,
622 | Context = any
623 | > = Resolver;
624 | export type IdResolver = Resolver<
625 | R,
626 | Parent,
627 | Context
628 | >;
629 | export type TimestampResolver<
630 | R = BigInt,
631 | Parent = any,
632 | Context = any
633 | > = Resolver;
634 | export type NameResolver = Resolver<
635 | R,
636 | Parent,
637 | Context
638 | >;
639 | }
640 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // patch for missed fetch in production
2 | (global as any).fetch = require('node-fetch');
3 |
4 | import { importSchema } from 'graphql-import';
5 | import { ApolloServer, gql } from 'apollo-server';
6 |
7 | import 'reflect-metadata';
8 | import { createConnection } from 'typeorm';
9 |
10 | import { Config } from './Config';
11 | import { Connection } from './embeddedPostgraphile/config';
12 | import { Binding } from './generated/postgraphile';
13 | import resolvers from './resolvers';
14 |
15 | // simple arguments parser
16 | const configPath = process.argv[process.argv.length - 1];
17 | if (configPath === __filename) {
18 | console.log(`Config argument required. Run as: > ${__filename} path/to/config.json`);
19 | process.exit(1);
20 | }
21 | const config = Config.fromFile(configPath);
22 |
23 | createConnection(config).then(async connection => {
24 | // load end user facing graphql schema
25 | const typeDefs = gql(importSchema(__dirname + '/schema.graphql'));
26 |
27 | // connect postgraphile bindings with the postgres DB using typeorm configuration
28 | Connection.init(config);
29 | const postgraphileConnection = new Binding();
30 |
31 | const server = new ApolloServer({
32 | typeDefs,
33 | resolvers,
34 | mocks: true,
35 | // onHealthCheck: () => fetch('https://fourtonfish.com/hellosalut/?mode=auto'),
36 | context: (c: any) => ({
37 | ...c,
38 | orm: connection,
39 | gql: postgraphileConnection
40 | })
41 | });
42 |
43 | const url = await server.listen();
44 | console.log(`Graphql API server is running on ${url.url}`);
45 | }).catch(error => console.log(error));
46 |
--------------------------------------------------------------------------------
/src/migration/1534731480120-Migration.ts:
--------------------------------------------------------------------------------
1 | import {MigrationInterface, QueryRunner} from "typeorm";
2 | import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions";
3 |
4 | export class Migration1534731480120 implements MigrationInterface {
5 |
6 | public async up(queryRunner: QueryRunner): Promise {
7 | const schema = (queryRunner.connection.options as PostgresConnectionOptions).schema;
8 | await queryRunner.query(`CREATE TABLE "${schema}"."photo" ("id" SERIAL NOT NULL, "name" character varying(100) NOT NULL, "description" text NOT NULL, "filename" character varying NOT NULL, "views" double precision NOT NULL, "isPublished" boolean NOT NULL, CONSTRAINT "PK_3de63c33511727e82cefc06c2ae" PRIMARY KEY ("id"))`);
9 | await queryRunner.query(`CREATE TABLE "${schema}"."user" ("id" SERIAL NOT NULL, "name" character varying(100) NOT NULL, "email" text NOT NULL, "password" text NOT NULL, CONSTRAINT "UQ_89249f6d281c7676eb93d2276c4" UNIQUE ("email"), CONSTRAINT "PK_ba3483e2dc9d4b8478d7cc931ab" PRIMARY KEY ("id"))`);
10 | }
11 |
12 | public async down(queryRunner: QueryRunner): Promise {
13 | const schema = (queryRunner.connection.options as PostgresConnectionOptions).schema;
14 | await queryRunner.query(`DROP TABLE "${schema}"."user"`);
15 | await queryRunner.query(`DROP TABLE "${schema}"."photo"`);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/resolvers/Mutation/auth.ts:
--------------------------------------------------------------------------------
1 | import * as bcrypt from 'bcryptjs';
2 | import * as jwt from 'jsonwebtoken';
3 | import { Context } from '../../Context';
4 | import { ApolloError, AuthenticationError } from 'apollo-server-errors';
5 | import { User } from '../../entity/User';
6 |
7 | export const auth = {
8 | async signup(parent: any, args: any, ctx: Context, info: any) {
9 | const user = new User();
10 | user.name = args.name;
11 | user.email = args.email;
12 | user.password = await bcrypt.hash(args.password, 10);
13 | const result = await ctx.orm.manager.save(user);
14 | return {
15 | result
16 | };
17 | },
18 |
19 | async login(parent: any, { email, password }: any, ctx: Context, info: any) {
20 | const user = await ctx.orm.manager.findOne(User, { email: email });
21 |
22 | if (!user) {
23 | throw new ApolloError(`No such user found for email: ${email}`, 'NOT_FOUND');
24 | }
25 |
26 | const valid = await bcrypt.compare(password, user.password);
27 | if (!valid) {
28 | throw new AuthenticationError('Invalid password');
29 | }
30 |
31 | return {
32 | user,
33 | token: jwt.sign({ userId: user.id }, process.env.APP_SECRET || 'secret'),
34 | };
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/src/resolvers/Mutation/post.ts:
--------------------------------------------------------------------------------
1 | import { Context } from '../../Context';
2 | import { getUserId } from '../../utils';
3 |
4 | export const post = {
5 | async createPhoto(parent: any, args: any, ctx: Context, info: any) {
6 | const unused = getUserId(ctx); // check token
7 | return ctx.gql.mutation.createPhoto(
8 | args,
9 | info
10 | );
11 | },
12 |
13 | async deletePhoto(parent: any, args: any, ctx: Context, info: any) {
14 | const unused = getUserId(ctx); // check token
15 | return ctx.gql.mutation.deletePhoto(
16 | args,
17 | info
18 | );
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/src/resolvers/Node.ts:
--------------------------------------------------------------------------------
1 | // Run-time warning:
2 | // Type "Node" is missing a "resolveType" resolver.
3 | // Pass false into "resolverValidationOptions.requireResolversForResolveType" to disable this warning.
4 | // can be fixed using this implementation of a node resolution:
5 |
6 | // TODO see https://github.com/graphql-cli/graphql-cli/issues/338
7 | // export const Node = {
8 | // // tslint:disable-next-line:function-name
9 | // __resolveType(obj: any, context: Context, info: any) {
10 | // if ((obj as Post).author) {
11 | // return 'Post';
12 | // }
13 | // if ((obj as User).email) {
14 | // return 'User';
15 | // }
16 | // return null;
17 | // },
18 | // };
19 |
--------------------------------------------------------------------------------
/src/resolvers/Query.ts:
--------------------------------------------------------------------------------
1 | import { getUserId } from '../utils';
2 | import { Context } from '../Context';
3 |
4 | export const Query = {
5 | async allPhotos(parent: any, args: any, ctx: Context, info: any) {
6 | const unused = getUserId(ctx); // check token
7 | return ctx.gql.query.allPhotos(
8 | args,
9 | info
10 | );
11 | },
12 |
13 | async me(parent: any, args: any, ctx: Context, info: any) {
14 | const id = getUserId(ctx);
15 | return ctx.gql.query.userById({ id: id }, info);
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/src/resolvers/Subscription.ts:
--------------------------------------------------------------------------------
1 | // import { Context } from '../utils';
2 |
3 | // export const Subscription = {
4 | // feedSubscription: {
5 | // subscribe: (parent: any, args: any, ctx: Context, info: any) => {
6 | // return ctx.db.subscription.post(
7 | // {
8 | // where: {
9 | // node: {
10 | // isPublished: true,
11 | // },
12 | // },
13 | // },
14 | // info,
15 | // );
16 | // },
17 | // },
18 | // };
19 |
--------------------------------------------------------------------------------
/src/resolvers/index.ts:
--------------------------------------------------------------------------------
1 | import { Query } from './Query';
2 | import { auth } from './Mutation/auth';
3 | import { post } from './Mutation/post';
4 | // import { Node } from './Node';
5 |
6 | export default {
7 | Query,
8 | // Node,
9 | Mutation: {
10 | ...auth,
11 | ...post
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/src/schema.graphql:
--------------------------------------------------------------------------------
1 | # import UsersConnection, Photo, CreatePhotoPayload, CreatePhotoInput, DeletePhotoInput, DeletePhotoPayload, PhotosConnection, PhotosOrderBy, Cursor from "./generated/postgraphile.graphql"
2 |
3 | type Query {
4 | # """Retruns the current user from JWT token, which was previously obtained via `login` mutation"""
5 | me: User
6 |
7 | """Reads and enables pagination through a set of `Photo`."""
8 | allPhotos(
9 | """Only read the first `n` values of the set."""
10 | first: Int
11 |
12 | """Only read the last `n` values of the set."""
13 | last: Int
14 |
15 | """
16 | Skip the first `n` values from our `after` cursor, an alternative to cursor
17 | based pagination. May not be used with `last`.
18 | """
19 | offset: Int
20 |
21 | """Read all values in the set before (above) this cursor."""
22 | before: Cursor
23 |
24 | """Read all values in the set after (below) this cursor."""
25 | after: Cursor
26 |
27 | """The method to use when ordering `Photo`."""
28 | orderBy: [PhotosOrderBy!] = [PRIMARY_KEY_ASC]
29 |
30 | """
31 | A condition to be used in determining which values should be returned by the collection.
32 | """
33 | condition: PhotoCondition
34 | ): PhotosConnection
35 | }
36 |
37 | type Mutation {
38 | signup(email: String!, password: String!, name: String!): User!
39 | login(email: String!, password: String!): AuthPayload!
40 |
41 | """Creates a single `Photo`."""
42 | createPhoto(
43 | """
44 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
45 | """
46 | input: CreatePhotoInput!
47 | ): CreatePhotoPayload
48 |
49 | """Deletes a single `Photo` using its globally unique id."""
50 | deletePhoto(
51 | """
52 | The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields.
53 | """
54 | input: DeletePhotoInput!
55 | ): DeletePhotoPayload
56 | }
57 |
58 | type AuthPayload {
59 | token: String!
60 | user: User!
61 | }
62 |
63 | # do not import generated user, but define it here to hide the encrypted password field
64 | type User {
65 | id: Int!
66 | email: String!
67 | name: String!
68 | }
69 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as jwt from 'jsonwebtoken';
2 | import { Context } from './Context';
3 |
4 | export class AuthError extends Error {
5 | constructor() {
6 | super('Not authorized');
7 | }
8 | }
9 |
10 | export function getUserId(ctx: Context) {
11 | const Authorization = ctx.req.headers.authorization;
12 | if (Authorization) {
13 | const token = Authorization.replace('Bearer ', '');
14 | const { userId } = jwt.verify(token, process.env.APP_SECRET || 'secret') as { userId: number };
15 | return userId;
16 | }
17 | throw new AuthError();
18 | }
19 |
20 | export function ensure(value: T | null | undefined): T {
21 | if (value !== undefined && value !== null) {
22 | return value;
23 | }
24 | throw Error(`unexpected '${value}' instance`);
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "moduleResolution": "node",
5 | "module": "commonjs",
6 | "sourceMap": true,
7 | "rootDir": "src",
8 | "outDir": "build",
9 | "lib": [
10 | "esnext",
11 | "dom"
12 | ],
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": true,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": false,
20 | "emitDecoratorMetadata": true,
21 | "experimentalDecorators": true,
22 | "baseUrl": ".",
23 | "newLine": "LF"
24 | },
25 | "exclude": [
26 | "scripts"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/tsconfig.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-config-airbnb", "tslint-react"],
3 | "rules": {
4 | "align": [
5 | true,
6 | "members",
7 | "statements"
8 | ],
9 | "ban": false,
10 | "class-name": true,
11 | "comment-format": false,
12 | "curly": true,
13 | "eofline": false,
14 | "forin": true,
15 | "indent": [ true, "spaces" ],
16 | "interface-name": [true, "never-prefix"],
17 | "jsdoc-format": true,
18 | "jsx-no-lambda": false,
19 | "jsx-no-multiline-js": false,
20 | "label-position": true,
21 | "max-line-length": [ true, 120 ],
22 | "member-ordering": [
23 | true,
24 | "public-before-private",
25 | "static-before-instance",
26 | "variables-before-functions"
27 | ],
28 | "no-any": false,
29 | "no-arg": true,
30 | "no-bitwise": true,
31 | "no-console": [
32 | false,
33 | "log",
34 | "error",
35 | "debug",
36 | "info",
37 | "time",
38 | "timeEnd",
39 | "trace"
40 | ],
41 | "no-consecutive-blank-lines": true,
42 | "no-construct": true,
43 | "no-debugger": true,
44 | "no-duplicate-variable": true,
45 | "no-empty": true,
46 | "no-eval": true,
47 | "no-shadowed-variable": true,
48 | "no-string-literal": true,
49 | "no-switch-case-fall-through": true,
50 | "no-trailing-whitespace": false,
51 | "no-unused-expression": true,
52 | "no-use-before-declare": true,
53 | "one-line": [
54 | true,
55 | "check-catch",
56 | "check-else",
57 | "check-open-brace",
58 | "check-whitespace"
59 | ],
60 | "quotemark": [true, "single", "jsx-double"],
61 | "radix": true,
62 | "semicolon": [true, "always"],
63 | "switch-default": true,
64 |
65 | "trailing-comma": [false],
66 |
67 | "triple-equals": [ true, "allow-null-check" ],
68 | "typedef": [
69 | true,
70 | "parameter",
71 | "property-declaration"
72 | ],
73 | "typedef-whitespace": [
74 | true,
75 | {
76 | "call-signature": "nospace",
77 | "index-signature": "nospace",
78 | "parameter": "nospace",
79 | "property-declaration": "nospace",
80 | "variable-declaration": "nospace"
81 | }
82 | ],
83 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
84 | "whitespace": [
85 | true,
86 | "check-branch",
87 | "check-decl",
88 | "check-module",
89 | "check-operator",
90 | "check-separator",
91 | "check-type",
92 | "check-typecast"
93 | ],
94 | "import-name": false,
95 | "ter-indent": [
96 | true,
97 | 4,
98 | {
99 | "SwitchCase": 1
100 | }
101 | ],
102 | "no-this-assignment": false,
103 | "object-literal-shorthand": false,
104 | "no-parameter-reassignment": false,
105 | "ter-arrow-parens": false,
106 | "no-else-after-return": false
107 | }
108 | }
109 |
--------------------------------------------------------------------------------