├── .npmrc ├── packages ├── cli │ ├── .npmignore │ ├── src │ │ ├── bin.ts │ │ ├── discover.ts │ │ └── index.ts │ ├── .gitignore │ ├── README.md │ ├── CONTRIBUTING.md │ ├── tsconfig.json │ ├── package.json │ └── LICENSE ├── common │ ├── .npmignore │ ├── src │ │ ├── index.ts │ │ ├── types.ts │ │ └── command.ts │ ├── .gitignore │ ├── package.json │ └── tsconfig.json ├── loaders │ ├── .npmignore │ ├── .gitignore │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── package.json └── commands │ ├── init │ ├── .npmignore │ ├── .gitignore │ ├── README.md │ ├── tsconfig.json │ ├── src │ │ ├── sources │ │ │ ├── from-scratch.ts │ │ │ ├── from-open-api.ts │ │ │ └── from-existing.ts │ │ ├── search-codegen-config.ts │ │ ├── features │ │ │ ├── inspector.ts │ │ │ └── codegen.ts │ │ ├── common.ts │ │ └── index.ts │ └── package.json │ ├── serve │ ├── .npmignore │ ├── .gitignore │ ├── README.md │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── package.json │ └── generate │ ├── .gitignore │ ├── README.md │ ├── src │ └── index.ts │ ├── tsconfig.json │ └── package.json ├── website ├── static │ ├── CNAME │ ├── img │ │ ├── logo.ico │ │ └── logo.png │ └── js │ │ └── light-mode-by-default.js ├── babel.config.js ├── src │ ├── pages │ │ ├── index.js │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── theme │ │ └── Root.js ├── .gitignore ├── docs │ ├── discover.md │ ├── introspect.md │ ├── init.md │ ├── diff.md │ ├── similar.md │ ├── generate.md │ ├── coverage.md │ ├── validate.md │ ├── codegen.md │ ├── introduction.md │ ├── custom-commands.md │ ├── serve.md │ └── migration.md ├── sidebars.js ├── scripts │ └── algolia-ci.ts ├── README.md ├── package.json ├── docusaurus.config.js └── algolia-lockfile.json ├── templates └── fullstack │ ├── .dockerignore │ ├── client │ ├── src │ │ ├── react-app-env.d.ts │ │ ├── components │ │ │ ├── comment │ │ │ │ ├── Comment.css │ │ │ │ ├── OneComment.tsx │ │ │ │ └── CreateComment.tsx │ │ │ └── notes │ │ │ │ ├── Note.css │ │ │ │ ├── CreateNote.tsx │ │ │ │ ├── EditNote.tsx │ │ │ │ └── OneNote.tsx │ │ ├── App.test.tsx │ │ ├── App.css │ │ ├── index.css │ │ ├── index.tsx │ │ ├── App.tsx │ │ ├── graphql │ │ │ └── graphback.graphql │ │ ├── serviceWorker.ts │ │ └── logo.svg │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── index.html │ ├── .gitignore │ ├── tsconfig.json │ └── package.json │ ├── .gitignore │ ├── schemats.json │ ├── renovate.json │ ├── Dockerfile │ ├── docker-compose.yml │ ├── model │ └── datamodel.graphql │ ├── tslint.json │ ├── server │ ├── src │ │ ├── db.ts │ │ ├── index.ts │ │ ├── generated-db-types.ts │ │ ├── graphql.ts │ │ ├── schema │ │ │ └── schema.graphql │ │ └── generated-types.ts │ ├── tsconfig.json │ └── package.json │ ├── README.md │ ├── package.json │ └── .graphqlrc.yml ├── integration ├── test-project │ ├── .dockerignore │ ├── .gitignore │ ├── renovate.json │ ├── .graphqlrc.yml │ ├── tslint.json │ ├── tsconfig.json │ ├── package.json │ └── schema │ │ └── schema.graphql ├── package.json ├── tsconfig.json └── tests │ └── workflow.ts ├── lerna.json ├── renovate.json ├── templates.json ├── CONTRIBUTING.md ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── website.yml │ ├── algolia-integrity.yml │ ├── algolia-publish.yml │ └── main.yml ├── scripts ├── introspect-config.js └── release.js ├── LICENSE ├── .gitignore ├── package.json ├── docs ├── CUSTOM_EXTENSION.md └── MIGRATION.md └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true -------------------------------------------------------------------------------- /packages/cli/.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /packages/common/.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /packages/loaders/.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /packages/commands/init/.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /packages/commands/serve/.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /website/static/CNAME: -------------------------------------------------------------------------------- 1 | www.graphql-cli.com 2 | -------------------------------------------------------------------------------- /templates/fullstack/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /integration/test-project/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /packages/common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './command'; 3 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/comment/Comment.css: -------------------------------------------------------------------------------- 1 | .comment{ 2 | color:grey; 3 | } -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.1.0", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/cli/src/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { cli } from './index'; 4 | 5 | cli(); 6 | -------------------------------------------------------------------------------- /templates/fullstack/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | types 4 | yarn.lock 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /integration/test-project/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | types 4 | yarn.lock 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /website/static/img/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Urigo/graphql-cli/HEAD/website/static/img/logo.ico -------------------------------------------------------------------------------- /website/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Urigo/graphql-cli/HEAD/website/static/img/logo.png -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /packages/common/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /templates/fullstack/client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /packages/loaders/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /packages/commands/init/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /packages/commands/serve/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /packages/commands/generate/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | temp 5 | yarn-error.log 6 | tests/coverage/ -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /website/static/js/light-mode-by-default.js: -------------------------------------------------------------------------------- 1 | if (!localStorage.getItem('theme')) { 2 | localStorage.setItem('theme', 'light') 3 | } -------------------------------------------------------------------------------- /templates/fullstack/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Urigo/graphql-cli/HEAD/templates/fullstack/client/public/favicon.ico -------------------------------------------------------------------------------- /templates/fullstack/client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Urigo/graphql-cli/HEAD/templates/fullstack/client/public/logo192.png -------------------------------------------------------------------------------- /templates/fullstack/client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Urigo/graphql-cli/HEAD/templates/fullstack/client/public/logo512.png -------------------------------------------------------------------------------- /packages/common/src/types.ts: -------------------------------------------------------------------------------- 1 | import { loadConfig } from 'graphql-config'; 2 | 3 | export type LoadConfigOptions = Partial[0]>; 4 | -------------------------------------------------------------------------------- /templates/fullstack/schemats.json: -------------------------------------------------------------------------------- 1 | { 2 | "conn": "postgres://postgresql:postgres@localhost:55432/users", 3 | "output": "server/src/generated-db-types.ts" 4 | } -------------------------------------------------------------------------------- /templates/fullstack/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "major": { 7 | "automerge": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /integration/test-project/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "major": { 7 | "automerge": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "major": { 7 | "automerge": false 8 | }, 9 | "rangeStrategy": "replace" 10 | } 11 | -------------------------------------------------------------------------------- /templates/fullstack/Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 1 - the build process 2 | FROM node:10 as compile-server 3 | WORKDIR /usr/src/app 4 | COPY . . 5 | RUN npm install 6 | RUN npm run build 7 | 8 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Redirect} from '@docusaurus/router'; 3 | 4 | function Home() { 5 | return ; 6 | } 7 | 8 | export default Home; 9 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # GraphQL CLI 2 | 3 | ![image](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) 4 | 5 | Repo and documentation at: https://github.com/Urigo/graphql-cli 6 | -------------------------------------------------------------------------------- /packages/cli/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | Please see the project root at https://github.com/Urigo/graphql-cli/CONTRIBUTING.md for more information about contributing to GraphQL CLI and the greater GraphQL ecosystem. 4 | -------------------------------------------------------------------------------- /packages/commands/init/README.md: -------------------------------------------------------------------------------- 1 | ## GraphQL CLI Init command 2 | 3 | This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. 4 | Go to: https://github.com/Urigo/graphql-cli for more information 5 | -------------------------------------------------------------------------------- /packages/commands/serve/README.md: -------------------------------------------------------------------------------- 1 | ## GraphQL CLI Serve command 2 | 3 | This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. 4 | Go to: https://github.com/Urigo/graphql-cli for more information 5 | -------------------------------------------------------------------------------- /packages/commands/generate/README.md: -------------------------------------------------------------------------------- 1 | ## GraphQL CLI Generate command 2 | 3 | This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. 4 | Go to: https://github.com/Urigo/graphql-cli for more information 5 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/notes/Note.css: -------------------------------------------------------------------------------- 1 | .inputForm{ 2 | display:grid; 3 | margin: 15%; 4 | width: 70%; 5 | } 6 | 7 | .inputCard{ 8 | width: 50%; 9 | margin-left: 25%; 10 | } 11 | .OneNote{ 12 | margin:5%; 13 | } -------------------------------------------------------------------------------- /templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "Full Stack Template with React Hooks, Apollo, NodeJS and PostgreSQL": { 3 | "repository": "https://github.com/Urigo/graphql-cli.git", 4 | "path": "templates/fullstack", 5 | "projectType": "Full Stack" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /templates/fullstack/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | postgres: 5 | image: postgres:9.6 6 | ports: 7 | - "55432:5432" 8 | environment: 9 | POSTGRES_PASSWORD: postgres 10 | POSTGRES_USER: postgresql 11 | POSTGRES_DB: users 12 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /templates/fullstack/model/datamodel.graphql: -------------------------------------------------------------------------------- 1 | """ @model """ 2 | type Note { 3 | id: ID! 4 | title: String! 5 | description: String 6 | """ 7 | @oneToMany(field: 'note') 8 | """ 9 | comments: [Comment]! 10 | } 11 | 12 | """ @model """ 13 | type Comment { 14 | id: ID! 15 | text: String 16 | description: String 17 | } -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /packages/commands/generate/src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from '@graphql-cli/common'; 2 | import { command, builder as builderConfig, handler } from 'graphback-cli'; 3 | 4 | export default defineCommand(() => { 5 | return { 6 | command, 7 | builder(builder: any) { 8 | return builder.options(builderConfig); 9 | }, 10 | handler, 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /packages/commands/serve/src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from '@graphql-cli/common'; 2 | import { serve } from 'graphql-serve'; 3 | 4 | export default defineCommand(() => { 5 | return { 6 | builder: (builder: any) => { 7 | serve.builder(builder); 8 | return builder; 9 | }, 10 | command: serve.command, 11 | handler: serve.handler, 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /website/docs/discover.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: discover 3 | title: discover 4 | sidebar_label: discover 5 | --- 6 | 7 | Open a list of available plugins in your browser. 8 | 9 | ### Installation 10 | 11 | This command does not need to be installed. 12 | 13 | ### Usage 14 | 15 | ``` 16 | graphql discover 17 | ``` 18 | 19 | #### Arguments 20 | 21 | *None* 22 | 23 | #### Options 24 | 25 | *None* -------------------------------------------------------------------------------- /packages/cli/src/discover.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from '@graphql-cli/common'; 2 | import open from 'open'; 3 | 4 | export default defineCommand(() => { 5 | return { 6 | command: 'discover', 7 | describe: 'Opens a list of all GraphQL CLI Plugins', 8 | handler() { 9 | return open('https://www.npmjs.com/search?q=keywords:graphql-cli-plugin'); 10 | }, 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | } 8 | 9 | .App-header { 10 | background-color: #282c34; 11 | min-height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: calc(10px + 2vmin); 17 | color: white; 18 | } 19 | 20 | .App-link { 21 | color: #09d3ac; 22 | } 23 | -------------------------------------------------------------------------------- /templates/fullstack/client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | We are using Yarn workspaces, so make sure you have the latest version of Yarn installed. 4 | 5 | ## Building project 6 | 7 | To build the entire monorepo, start by installing the dependencies by running `yarn` in the root directory, and then: 8 | 9 | ```sh 10 | yarn build 11 | ``` 12 | 13 | ## Using command line tool from source 14 | 15 | ```sh 16 | cd packages/cli 17 | npm link . 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /packages/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphql-cli/common", 3 | "version": "5.0.0", 4 | "license": "MIT", 5 | "main": "dist/index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "scripts": { 10 | "build": "tsc" 11 | }, 12 | "peerDependencies": { 13 | "graphql-config": "3.4.1", 14 | "yargs": "16.2.0" 15 | }, 16 | "dependencies": { 17 | "@graphql-tools/load": "^7.1.6", 18 | "tslib": "^2.3.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | docs: { 3 | 'Getting Started': [ 4 | 'introduction', 5 | ], 6 | 'Commands': [ 7 | 'codegen', 8 | 'coverage', 9 | 'diff', 10 | 'discover', 11 | 'generate', 12 | 'init', 13 | 'introspect', 14 | 'serve', 15 | 'similar', 16 | 'validate', 17 | ], 18 | 'Recipes': [ 19 | 'migration', 20 | 'custom-commands', 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /integration/test-project/.graphqlrc.yml: -------------------------------------------------------------------------------- 1 | schema: schema/schema.graphql 2 | extensions: 3 | codegen: 4 | generates: 5 | ./src/generated-types.ts: 6 | config: 7 | mappers: 8 | Comment: './generated-db-types#comment' 9 | Note: './generated-db-types#note' 10 | useIndexSignature: true 11 | skipDocumentsValidation: true 12 | plugins: 13 | - add: '/* tslint:disable */' 14 | - typescript 15 | - typescript-resolvers 16 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ifm-color-primary: #8c0082; 3 | --ifm-color-primary-dark: #8c0082; 4 | --ifm-color-primary-darker: #8c0082; 5 | --ifm-color-primary-darkest: #8c0082; 6 | --ifm-color-primary-light: #8c0082; 7 | --ifm-color-primary-lighter: #8c0082; 8 | --ifm-color-primary-lightest: #8c0082; 9 | 10 | --ifm-color-secondary: #bfc7d5; 11 | --ifm-footer-link-hover-color: #fff; 12 | } 13 | 14 | a { 15 | transition: all 0.2s ease 0s; 16 | } 17 | 18 | button { 19 | --ifm-color-content: #fff; 20 | } 21 | 22 | td > code { 23 | white-space: nowrap; 24 | } -------------------------------------------------------------------------------- /website/src/theme/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import BrowserOnly from '@docusaurus/BrowserOnly'; 3 | import { ThemeProvider, Header } from 'the-guild-components'; 4 | 5 | // Default implementation, that you can customize 6 | function Root({ children }) { 7 | return ( 8 | <> 9 | 10 | {() => ( 11 | 12 |
13 | 14 | )} 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | export default Root; 22 | -------------------------------------------------------------------------------- /templates/fullstack/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /templates/fullstack/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /website/scripts/algolia-ci.ts: -------------------------------------------------------------------------------- 1 | import { indexToAlgolia } from '@guild-docs/algolia'; 2 | import { resolve } from 'path'; 3 | import * as sidebars from '../sidebars'; 4 | 5 | indexToAlgolia({ 6 | docusaurus: { 7 | sidebars, 8 | }, 9 | // needed because GraphQL CLI has a weird routing config with content at `/` 10 | postProcessor: (objects) => 11 | objects.map((o) => ({ 12 | ...o, 13 | url: o.url.replace('docs/', ''), 14 | })), 15 | source: 'CLI', 16 | domain: process.env.SITE_URL!, 17 | lockfilePath: resolve(__dirname, '../algolia-lockfile.json'), 18 | dryMode: process.env.ALGOLIA_DRY_RUN === 'true', 19 | }); 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-cli-integration-test", 3 | "version": "4.1.0", 4 | "license": "MIT", 5 | "private": true, 6 | "main": "dist/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "scripts": { 11 | "test": "ava" 12 | }, 13 | "dependencies": { 14 | "log-symbols": "4.1.0", 15 | "tslib": "2.3.1" 16 | }, 17 | "devDependencies": { 18 | "ava": "3.15.0", 19 | "execa": "5.1.1" 20 | }, 21 | "ava": { 22 | "files": [ 23 | "tests/**/*" 24 | ], 25 | "extensions": [ 26 | "ts" 27 | ], 28 | "require": [ 29 | "ts-node/register" 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /packages/loaders/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Loader } from '@graphql-tools/utils'; 2 | import { ApolloEngineLoader } from '@graphql-tools/apollo-engine-loader'; 3 | import { CodeFileLoader } from '@graphql-tools/code-file-loader'; 4 | import { GitLoader } from '@graphql-tools/git-loader'; 5 | import { GithubLoader } from '@graphql-tools/github-loader'; 6 | import { PrismaLoader } from '@graphql-tools/prisma-loader'; 7 | import { UrlLoader } from '@graphql-tools/url-loader'; 8 | 9 | export const loaders: Loader[] = [ 10 | new ApolloEngineLoader(), 11 | new CodeFileLoader(), 12 | new GitLoader(), 13 | new GithubLoader(), 14 | new PrismaLoader(), 15 | new UrlLoader(), 16 | ]; 17 | -------------------------------------------------------------------------------- /templates/fullstack/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "max-line-length": { 5 | "options": [120] 6 | }, 7 | "semicolon": false, 8 | "quotemark": false, 9 | "new-parens": true, 10 | "no-arg": true, 11 | "no-bitwise": true, 12 | "no-conditional-assignment": false, 13 | "no-consecutive-blank-lines": false, 14 | "object-literal-sort-keys": false, 15 | "trailing-comma": false, 16 | "object-literal-shorthand": false, 17 | "no-console": false, 18 | "interface-name": false 19 | }, 20 | "jsRules": { 21 | "max-line-length": { 22 | "options": [120] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /integration/test-project/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "max-line-length": { 5 | "options": [120] 6 | }, 7 | "semicolon": false, 8 | "quotemark": false, 9 | "new-parens": true, 10 | "no-arg": true, 11 | "no-bitwise": true, 12 | "no-conditional-assignment": false, 13 | "no-consecutive-blank-lines": false, 14 | "object-literal-sort-keys": false, 15 | "trailing-comma": false, 16 | "object-literal-shorthand": false, 17 | "no-console": false, 18 | "interface-name": false 19 | }, 20 | "jsRules": { 21 | "max-line-length": { 22 | "options": [120] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/introspect-config.js: -------------------------------------------------------------------------------- 1 | const { loadSchema } = require('@graphql-tools/load'); 2 | const { GraphQLFileLoader } = require('@graphql-tools/graphql-file-loader'); 3 | const { printSchemaWithDirectives } = require('@graphql-tools/utils'); 4 | const { writeFileSync } = require('fs'); 5 | const { resolve } = require('path'); 6 | const { DIRECTIVES } = require('graphql-to-config-schema'); 7 | 8 | async function main() { 9 | const schema = await loadSchema([DIRECTIVES, './**/yaml-config.graphql'], { 10 | loaders: [new GraphQLFileLoader()], 11 | assumeValidSDL: true, 12 | }); 13 | 14 | writeFileSync(resolve(__dirname, '../schema.graphql'), printSchemaWithDirectives(schema)); 15 | } 16 | 17 | main().catch(console.error); 18 | -------------------------------------------------------------------------------- /packages/common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "importHelpers": true, 5 | "experimentalDecorators": true, 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "lib": ["es6", "esnext", "es2015", "dom"], 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "declaration": true, 13 | "outDir": "./dist", 14 | "rootDir": "./src", 15 | "noImplicitAny": true, 16 | "noImplicitThis": true, 17 | "alwaysStrict": true, 18 | "noImplicitReturns": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": false 21 | }, 22 | "files": ["src/index.ts"], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/loaders/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "importHelpers": true, 5 | "experimentalDecorators": true, 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "lib": ["es6", "esnext", "es2015", "dom"], 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "declaration": true, 13 | "outDir": "./dist", 14 | "rootDir": "./src", 15 | "noImplicitAny": true, 16 | "noImplicitThis": true, 17 | "alwaysStrict": true, 18 | "noImplicitReturns": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": false 21 | }, 22 | "files": ["src/index.ts"], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * CSS files with the .module.css suffix will be treated as CSS modules 4 | * and scoped locally. 5 | */ 6 | 7 | .heroBanner { 8 | padding: 4rem 0; 9 | text-align: center; 10 | position: relative; 11 | overflow: hidden; 12 | } 13 | 14 | @media screen and (max-width: 966px) { 15 | .heroBanner { 16 | padding: 2rem; 17 | } 18 | } 19 | 20 | .buttons { 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | } 25 | 26 | .features { 27 | display: flex; 28 | align-items: center; 29 | padding: 2rem 0; 30 | width: 100%; 31 | } 32 | 33 | .featureImage { 34 | height: 200px; 35 | width: 200px; 36 | } 37 | -------------------------------------------------------------------------------- /packages/loaders/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphql-cli/loaders", 3 | "description": "Internal usage", 4 | "version": "5.0.0", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "scripts": { 11 | "build": "tsc" 12 | }, 13 | "peerDependencies": { 14 | "graphql": "15.7.2" 15 | }, 16 | "dependencies": { 17 | "@graphql-tools/apollo-engine-loader": "^7.0.4", 18 | "@graphql-tools/code-file-loader": "^7.0.4", 19 | "@graphql-tools/git-loader": "^7.0.4", 20 | "@graphql-tools/github-loader": "^7.0.4", 21 | "@graphql-tools/prisma-loader": "^7.0.5", 22 | "@graphql-tools/url-loader": "^7.0.10", 23 | "@graphql-tools/utils": "^8.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "esModuleInterop": true, 5 | "importHelpers": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "target": "es2018", 9 | "lib": ["es6", "esnext", "es2015", "dom"], 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "outDir": "./dist", 15 | "rootDir": "./src", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false 22 | }, 23 | "include": ["src"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/commands/generate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "esModuleInterop": true, 5 | "importHelpers": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "target": "es2018", 9 | "lib": ["es6", "esnext", "es2015", "dom"], 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "outDir": "./dist", 15 | "rootDir": "./src", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false 22 | }, 23 | "files": ["src/index.ts"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/commands/serve/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "esModuleInterop": true, 5 | "importHelpers": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "target": "es2018", 9 | "lib": ["es6", "esnext", "es2015", "dom"], 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "outDir": "./dist", 15 | "rootDir": "./src", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false 22 | }, 23 | "files": ["src/index.ts"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/commands/serve/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphql-cli/serve", 3 | "description": "Serves a GraphQL server using an in memory MongoDB", 4 | "version": "5.0.0", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Urigo/graphql-cli", 13 | "directory": "packages/commands/serve" 14 | }, 15 | "keywords": [ 16 | "graphql-cli", 17 | "graphql-cli-plugin" 18 | ], 19 | "scripts": { 20 | "build": "tsc" 21 | }, 22 | "peerDependencies": { 23 | "graphql": "15.7.2" 24 | }, 25 | "dependencies": { 26 | "@graphql-cli/common": "^5.0.0", 27 | "graphql-serve": "^1.1.2", 28 | "tslib": "~2.3.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/fullstack/server/src/db.ts: -------------------------------------------------------------------------------- 1 | import { loadConfig } from 'graphql-config'; 2 | import Knex from 'knex'; 3 | 4 | export const getConfig = async () => { 5 | const config = await loadConfig({ 6 | extensions: [() => ({ name: 'dbmigrations' })], 7 | }); 8 | if (!config) { 9 | throw new Error('Missing dbmigrations config'); 10 | } 11 | const conf = await config.getDefault().extension('dbmigrations'); 12 | 13 | return conf; 14 | }; 15 | 16 | /** 17 | * Creates knex based database using migration configuration 18 | * For production use please use different source of the configuration 19 | */ 20 | export const createDB = async () => { 21 | const dbmigrations = await getConfig(); 22 | // connect to db 23 | const db = Knex(dbmigrations); 24 | 25 | return db as any; 26 | }; 27 | -------------------------------------------------------------------------------- /integration/test-project/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "declarationDir": "./types", 5 | "lib": [ 6 | "esnext", 7 | "dom" 8 | ], 9 | "resolveJsonModule": true, 10 | "noImplicitAny": false, 11 | "preserveConstEnums": true, 12 | "strict": false, 13 | "strictNullChecks": true, 14 | "esModuleInterop": true, 15 | "target": "esnext", 16 | "module": "commonjs", 17 | "moduleResolution": "node", 18 | "allowSyntheticDefaultImports": true, 19 | "importHelpers": true, 20 | "alwaysStrict": false, 21 | "sourceMap": true, 22 | "declaration": true, 23 | "noImplicitReturns": true, 24 | "noUnusedLocals": false, 25 | "noUnusedParameters": false, 26 | "noImplicitThis": false 27 | }, 28 | } -------------------------------------------------------------------------------- /templates/fullstack/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "declarationDir": "./types", 5 | "lib": [ 6 | "esnext", 7 | "dom" 8 | ], 9 | "resolveJsonModule": true, 10 | "noImplicitAny": false, 11 | "preserveConstEnums": true, 12 | "strict": false, 13 | "strictNullChecks": true, 14 | "esModuleInterop": true, 15 | "target": "esnext", 16 | "module": "commonjs", 17 | "moduleResolution": "node", 18 | "allowSyntheticDefaultImports": true, 19 | "importHelpers": true, 20 | "alwaysStrict": false, 21 | "sourceMap": true, 22 | "declaration": true, 23 | "noImplicitReturns": true, 24 | "noUnusedLocals": false, 25 | "noUnusedParameters": false, 26 | "noImplicitThis": false 27 | }, 28 | } -------------------------------------------------------------------------------- /packages/commands/init/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "esModuleInterop": true, 5 | "importHelpers": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "target": "es2018", 9 | "lib": ["es6", "esnext", "es2015", "dom"], 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "outDir": "./dist", 15 | "rootDir": "./src", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false, 22 | "resolveJsonModule": true 23 | }, 24 | "files": ["src/index.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /integration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "esModuleInterop": true, 5 | "importHelpers": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "target": "es2018", 9 | "lib": ["es6", "esnext", "es2015", "dom"], 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "outDir": "./dist", 15 | "rootDir": "./tests", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": false 22 | }, 23 | "files": ["src/index.ts"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/comment/OneComment.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDeleteCommentMutation, Comment } from '../../generated-types'; 3 | import { Button } from '@material-ui/core'; 4 | import './Comment.css'; 5 | 6 | const OneComment = ({ id, text, description }: Comment) => { 7 | const [deleteComment] = useDeleteCommentMutation(); 8 | 9 | return ( 10 |
11 |
  • 12 | {text}:  13 | {description} 14 | 21 |
  • 22 |
    23 | ); 24 | }; 25 | 26 | export default OneComment; 27 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { ApolloProvider } from '@apollo/react-hooks'; 2 | import ApolloClient from 'apollo-boost'; 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import App from './App'; 6 | import './index.css'; 7 | import * as serviceWorker from './serviceWorker'; 8 | 9 | const apolloClient = new ApolloClient({ 10 | uri: 'http://localhost:4000/graphql', 11 | }); 12 | 13 | ReactDOM.render( 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | 20 | // If you want your app to work offline and load faster, you can change 21 | // unregister() to register() below. Note this comes with some pitfalls. 22 | // Learn more about service workers: https://bit.ly/CRA-PWA 23 | serviceWorker.unregister(); 24 | -------------------------------------------------------------------------------- /packages/commands/generate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphql-cli/generate", 3 | "description": "Generate schema and client-side documents for your GraphQL project by using Graphback.", 4 | "version": "5.0.0", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Urigo/graphql-cli", 10 | "directory": "packages/commands/generate" 11 | }, 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "keywords": [ 16 | "graphql-cli", 17 | "graphql-cli-plugin" 18 | ], 19 | "scripts": { 20 | "build": "tsc" 21 | }, 22 | "peerDependencies": { 23 | "graphql": "15.7.2" 24 | }, 25 | "dependencies": { 26 | "@graphql-cli/common": "^5.0.0", 27 | "graphback-cli": "1.1.2", 28 | "tslib": "2.3.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Versions (please complete the following information):** 24 | - OS: [e.g. `Windows 10`, `OS X High Sierra`, `Ubuntu 16.04`] 25 | - `graphql-cli`: [e.g. `2.16.5`] 26 | - other packages if applicable 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /templates/fullstack/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import cors from 'cors'; 2 | import express from 'express'; 3 | import http from 'http'; 4 | 5 | import { createApolloServer } from './graphql'; 6 | 7 | async function start() { 8 | const app = express(); 9 | 10 | app.use(cors()); 11 | 12 | app.get('/health', (req, res) => res.sendStatus(200)); 13 | 14 | const apolloServer = await createApolloServer(); 15 | apolloServer.applyMiddleware({ app }); 16 | 17 | const httpServer = http.createServer(app); 18 | apolloServer.installSubscriptionHandlers(httpServer); 19 | 20 | const port = process.env.PORT || 4000; 21 | 22 | httpServer.listen({ port }, () => { 23 | console.log(`🚀 Server ready at http://localhost:${port}/graphql`); 24 | }); 25 | } 26 | 27 | start().catch((err) => { 28 | console.error(err); 29 | process.exit(1); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/commands/init/src/sources/from-scratch.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import { prompt } from 'inquirer'; 3 | import { Context } from '../common'; 4 | import { handler } from 'create-graphback'; 5 | 6 | export async function fromScratch({ 7 | context, 8 | templateName, 9 | templateUrl, 10 | }: { 11 | context: Context; 12 | templateName: string; 13 | templateUrl: string; 14 | }) { 15 | if (!context.name) { 16 | const { projectName: enteredName } = await prompt([ 17 | { 18 | type: 'input', 19 | name: 'projectName', 20 | message: 'What is the name of the project?', 21 | default: 'my-graphql-project', 22 | }, 23 | ]); 24 | context.name = enteredName; 25 | context.path = join(process.cwd(), context.name); 26 | } 27 | 28 | await handler({ name: context.name, templateName, templateUrl }); 29 | } 30 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /templates/fullstack/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "full-stack-template-server", 4 | "version": "4.1.0", 5 | "scripts": { 6 | "develop": "ts-node-dev src/index.ts", 7 | "start": "ts-node src/index.ts" 8 | }, 9 | "dependencies": { 10 | "graphback": "1.1.2", 11 | "@graphback/runtime-knex": "1.1.2", 12 | "@graphback/codegen-schema": "1.1.2", 13 | "graphql-migrations": "1.1.2", 14 | "@graphql-tools/load-files": "6.5.2", 15 | "@types/cors": "2.8.12", 16 | "@types/express": "4.17.13", 17 | "@types/node": "13.13.45", 18 | "apollo-server-express": "2.25.3", 19 | "cors": "2.8.5", 20 | "express": "4.17.1", 21 | "graphql": "15.7.2", 22 | "graphql-config": "3.4.1", 23 | "graphql-tag": "2.12.6", 24 | "knex": "0.95.14", 25 | "pg": "8.7.1", 26 | "ts-node": "9.1.1", 27 | "ts-node-dev": "1.1.8", 28 | "typescript": "4.4.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "4.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy", 10 | "algolia-sync": "ts-node scripts/algolia-ci.ts" 11 | }, 12 | "dependencies": { 13 | "@docusaurus/core": "^2.0.0-beta.0", 14 | "@guild-docs/algolia": "0.0.6-alpha-1bcf597.0", 15 | "@docusaurus/preset-classic": "^2.0.0-beta.0", 16 | "clsx": "^1.1.1", 17 | "react": "^17.0.0", 18 | "react-dom": "^17.0.0", 19 | "styled-components": "5.3.3", 20 | "the-guild-components": "1.5.3" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /templates/fullstack/README.md: -------------------------------------------------------------------------------- 1 | # GraphQL CLI Basic Full Stack Template 2 | 3 | Starter Full Stack template using GraphQL CLI. 4 | 5 | ## Usage 6 | 7 | This project has been created using GraphQL CLI. Run the project using the following steps: 8 | 9 | ### Install 10 | 11 | yarn install 12 | 13 | ### Database 14 | Start the database 15 | 16 | docker-compose up -d 17 | 18 | Generate resources(schema and resolvers) and create database 19 | 20 | yarn graphql generate --backend 21 | yarn graphql generate --db 22 | 23 | Generate typings for Database Schema and Resolvers 24 | 25 | yarn schemats generate 26 | yarn graphql codegen 27 | 28 | ### Server 29 | Start the server 30 | 31 | yarn start:server 32 | 33 | ### Client 34 | 35 | Generate queries, mutations and subscriptions for client-side project 36 | 37 | yarn graphql generate --client 38 | 39 | Generate React components 40 | 41 | yarn graphql codegen 42 | 43 | Start React App 44 | 45 | yarn start:client 46 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-cli", 3 | "description": "Command line tool for common GraphQL development workflows", 4 | "keywords": [ 5 | "graphql", 6 | "graphql-cli", 7 | "cli" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Urigo/graphql-cli" 12 | }, 13 | "author": { 14 | "name": "Uri Goldshtein", 15 | "email": "uri.goldshtein@gmail.com", 16 | "url": "https://github.com/Urigo" 17 | }, 18 | "version": "5.0.0", 19 | "license": "MIT", 20 | "main": "dist/index.js", 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "bin": { 25 | "graphql": "dist/bin.js" 26 | }, 27 | "scripts": { 28 | "start": "ts-node src/bin.ts", 29 | "build": "tsc" 30 | }, 31 | "dependencies": { 32 | "@graphql-cli/common": "^5.0.0", 33 | "@graphql-cli/init": "^5.0.0", 34 | "globby": "^11.0.2", 35 | "graphql-config": "^3.2.0", 36 | "open": "^7.4.2", 37 | "yargs": "^16.2.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import { useFindNotesQuery } from './generated-types'; 4 | import CreateNote from './components/notes/CreateNote'; 5 | import OneNote from './components/notes/OneNote'; 6 | 7 | const App: React.FC = () => { 8 | const allNotes = useFindNotesQuery(); 9 | allNotes.startPolling(2000); 10 | console.log(allNotes.data?.findNotes); 11 | 12 | return ( 13 |
    14 | 15 |
      16 | { 17 | // TODO fix typings 18 | allNotes.data && 19 | allNotes.data.findNotes.items.map((note: any) => ( 20 | 27 | )) 28 | } 29 |
    30 |
    31 | ); 32 | }; 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /integration/tests/workflow.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line: match-default-export-name no-implicit-dependencies 2 | import ava, { ExecutionContext } from 'ava'; 3 | import { resolve } from 'path'; 4 | import { execSync } from 'child_process'; 5 | 6 | ava('Test cli workflow', (t: ExecutionContext) => { 7 | const basePath = resolve(`${__dirname}/../test-project`); 8 | process.chdir(basePath); 9 | // Workaround for github actions symlinking issue 10 | const graphQLCmd = 'node ../../packages/cli/dist/index.js'; 11 | console.log(`Running commands in ${basePath}`); 12 | try { 13 | let generate = execSync(`${graphQLCmd} generate --backend`, { encoding: 'utf8', cwd: basePath }); 14 | const codegen = execSync(`${graphQLCmd} codegen`, { encoding: 'utf8', cwd: basePath }); 15 | 16 | console.log(` 17 | Generate: ${generate}\n 18 | Codegen: ${codegen}\n 19 | `); 20 | t.true(codegen.indexOf('error') === -1); 21 | t.true(generate.indexOf('failed') === -1); 22 | } catch (error) { 23 | t.fail(`build failed with ${error}`); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /integration/test-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "test-project", 4 | "version": "4.1.0", 5 | "dependencies": { 6 | "@graphql-tools/load-files": "6.5.2", 7 | "@types/node": "13.13.45", 8 | "graphql": "15.7.2", 9 | "graphql-config": "3.4.1", 10 | "graphql-tag": "2.12.6" 11 | }, 12 | "devDependencies": { 13 | "@graphql-codegen/add": "2.0.2", 14 | "@graphql-codegen/typescript": "1.23.0", 15 | "@graphql-codegen/typescript-operations": "1.18.4", 16 | "@graphql-codegen/typescript-react-apollo": "2.3.1", 17 | "@graphql-codegen/typescript-resolvers": "1.20.0", 18 | "@graphql-cli/codegen": "1.17.27", 19 | "@graphql-cli/coverage": "2.1.0", 20 | "@graphql-cli/diff": "2.1.0", 21 | "@graphql-cli/generate": "4.1.0", 22 | "@graphql-cli/serve": "4.1.0", 23 | "@graphql-cli/similar": "2.1.0", 24 | "@graphql-cli/validate": "2.1.0", 25 | "graphql": "15.7.2", 26 | "graphql-cli": "4.1.0", 27 | "tslint": "6.1.3", 28 | "typescript": "4.4.4" 29 | }, 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /templates/fullstack/server/src/generated-db-types.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | 3 | /** 4 | * AUTO-GENERATED FILE @ 2020-06-03 11:49:44 - DO NOT EDIT! 5 | * 6 | * This file was automatically generated by schemats v.3.0.3 7 | * $ schemats generate -c postgres://username:password@localhost:55432/users -t note -t comment -s public 8 | * 9 | */ 10 | 11 | export namespace noteFields { 12 | export type title = string; 13 | export type description = string | null; 14 | export type id = number; 15 | 16 | } 17 | 18 | export interface note { 19 | title: noteFields.title; 20 | description: noteFields.description; 21 | id: noteFields.id; 22 | 23 | } 24 | 25 | export namespace commentFields { 26 | export type text = string | null; 27 | export type description = string | null; 28 | export type noteId = number | null; 29 | export type id = number; 30 | 31 | } 32 | 33 | export interface comment { 34 | text: commentFields.text; 35 | description: commentFields.description; 36 | noteId: commentFields.noteId; 37 | id: commentFields.id; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dotan Simha 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # Bower dependency directory (https://bower.io/) 24 | bower_components 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (https://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules/ 34 | jspm_packages/ 35 | 36 | # TypeScript v1 declaration files 37 | typings/ 38 | 39 | # Optional npm cache directory 40 | .npm 41 | 42 | # Optional eslint cache 43 | .eslintcache 44 | 45 | # Optional REPL history 46 | .node_repl_history 47 | 48 | # Output of 'npm pack' 49 | *.tgz 50 | 51 | # Yarn Integrity file 52 | .yarn-integrity 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # next.js build output 58 | .next 59 | 60 | package-lock.json 61 | -------------------------------------------------------------------------------- /packages/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dotan Simha 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. -------------------------------------------------------------------------------- /templates/fullstack/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "full-stack-template-client", 3 | "version": "4.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@types/jest": "26.0.24", 7 | "@types/node": "13.13.45", 8 | "@types/react": "17.0.34", 9 | "@types/react-dom": "17.0.11", 10 | "react-scripts": "4.0.3", 11 | "typescript": "4.4.4" 12 | }, 13 | "dependencies": { 14 | "@apollo/react-common": "3.1.4", 15 | "@apollo/react-hooks": "3.1.5", 16 | "@material-ui/core": "4.12.3", 17 | "apollo-boost": "0.4.9", 18 | "graphql": "15.7.2", 19 | "react": "17.0.2", 20 | "react-dom": "17.0.2" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": "react-app" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /templates/fullstack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "full-stack-template", 3 | "version": "4.1.0", 4 | "private": true, 5 | "workspaces": [ 6 | "client", 7 | "server" 8 | ], 9 | "scripts": { 10 | "start:server": "cd server/ && yarn start", 11 | "start:client": "cd client/ && yarn start" 12 | }, 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@graphql-codegen/add": "2.0.2", 16 | "@graphql-codegen/typescript": "1.23.0", 17 | "@graphql-codegen/typescript-operations": "1.18.4", 18 | "@graphql-codegen/typescript-react-apollo": "2.3.1", 19 | "@graphql-codegen/typescript-resolvers": "1.20.0", 20 | "@graphql-cli/codegen": "1.17.27", 21 | "@graphql-cli/coverage": "2.1.0", 22 | "@graphql-cli/diff": "2.1.0", 23 | "@graphql-cli/generate": "4.1.0", 24 | "@graphql-cli/serve": "4.1.0", 25 | "@graphql-cli/similar": "2.1.0", 26 | "@graphql-cli/validate": "2.1.0", 27 | "graphql": "15.7.2", 28 | "graphql-cli": "4.1.0", 29 | "schemats": "3.0.3", 30 | "tslint": "6.1.3", 31 | "typescript": "4.4.4" 32 | }, 33 | "resolutions": { 34 | "graphql": "15.7.2" 35 | }, 36 | "author": { 37 | "name": "Arda TANRIKULU" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/commands/init/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphql-cli/init", 3 | "description": "Creates a GraphQL project using a template or GraphQL Config file for your existing project.", 4 | "version": "5.0.0", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Urigo/graphql-cli", 13 | "directory": "packages/commands/init" 14 | }, 15 | "keywords": [ 16 | "graphql-cli", 17 | "graphql-cli-plugin" 18 | ], 19 | "scripts": { 20 | "build": "tsc" 21 | }, 22 | "dependencies": { 23 | "@graphql-cli/common": "^5.0.0", 24 | "chalk": "^4.1.0", 25 | "cosmiconfig": "^7.0.0", 26 | "create-graphback": "^1.1.2", 27 | "cross-fetch": "^3.0.6", 28 | "fs-extra": "^9.1.0", 29 | "fullname": "^4.0.1", 30 | "graphql": "^15.5.0", 31 | "inquirer": "7.3.3", 32 | "js-yaml": "^3.14.1", 33 | "latest-version": "^5.1.0", 34 | "openapi-to-graphql": "^2.2.6", 35 | "ora": "^5.3.0", 36 | "rimraf": "^3.0.2", 37 | "simple-git": "^2.36.1", 38 | "string-env-interpolation": "^1.0.1", 39 | "tmp": "^0.2.1", 40 | "tslib": "~2.3.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/fullstack/server/src/graphql.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import { loadFiles } from '@graphql-tools/load-files'; 3 | import { ApolloServer } from 'apollo-server-express'; 4 | import { buildGraphbackAPI } from 'graphback'; 5 | import { createKnexDbProvider } from '@graphback/runtime-knex'; 6 | import { migrateDB, removeNonSafeOperationsFilter } from 'graphql-migrations'; 7 | import { createDB, getConfig } from './db'; 8 | import { SchemaCRUDPlugin } from '@graphback/codegen-schema'; 9 | 10 | /** 11 | * Creates Apollo server 12 | */ 13 | export const createApolloServer = async () => { 14 | const db = await createDB(); 15 | const dbConfig = await getConfig(); 16 | 17 | const schema = (await loadFiles(join(__dirname, '../../model/'))).join('\n'); 18 | const { resolvers, contextCreator, typeDefs} = buildGraphbackAPI(schema, { 19 | dataProviderCreator: createKnexDbProvider(db), 20 | plugins: [new SchemaCRUDPlugin({ 21 | outputPath: "./src/schema/schema.graphql" 22 | })] 23 | }); 24 | 25 | migrateDB(dbConfig, typeDefs, { 26 | operationFilter: removeNonSafeOperationsFilter 27 | }).then(() => { 28 | console.log("Migrated database"); 29 | }); 30 | 31 | const apolloServer = new ApolloServer({ 32 | typeDefs, 33 | resolvers, 34 | context: contextCreator, 35 | playground: true, 36 | }); 37 | 38 | return apolloServer; 39 | }; 40 | -------------------------------------------------------------------------------- /website/docs/introspect.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: introspect 3 | title: introspect 4 | sidebar_label: introspect 5 | --- 6 | 7 | Dumps an introspection file based on a schema. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/introspect) for details. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/introspect 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/introspect 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Usage 39 | 40 | ``` 41 | graphql introspect [SCHEMA] 42 | ``` 43 | 44 | #### Arguments 45 | 46 | | argument | description | default | 47 | | --- | --- | --- | 48 | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | 49 | 50 | #### Options 51 | 52 | | option | alias | description | default | 53 | | --- | --- | --- | --- | 54 | | `--write` | `-w` | Overwrite the output | `graphql.schema.json` | 55 | | `--require` | `-r` | Require a module | `[]` | 56 | | `--token` | `-t` | An access token | `undefined` | 57 | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | 58 | -------------------------------------------------------------------------------- /website/docs/init.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: init 3 | title: init 4 | sidebar_label: init 5 | --- 6 | 7 | Create a GraphQL project using a template or GraphQL Config file for your existing project. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/init 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/init 33 | ``` 34 | 35 | 36 | 37 | 38 | Note: Because you probably won't need to run this command again after bootstrapping your project, you can also run it using `npx` instead of installing the package: `npx graphql init`. 39 | 40 | ### Usage 41 | 42 | ``` 43 | graphql init 44 | ``` 45 | 46 | Follow the prompts to set up your project. 47 | 48 | #### Arguments 49 | 50 | *None* 51 | 52 | #### Options 53 | 54 | | option | alias | description | default | 55 | | --- | --- | --- | --- | 56 | | `--projectName` | | Name of a project in GraphQL Config | `undefined` | 57 | | `--templateName` | | Name of one of the predefined templates | `undefined` | 58 | | `--templateUrl` | | GitHub URL of the template. For example http://github.com/example/graphql-cli-example-template | `undefined` | -------------------------------------------------------------------------------- /packages/common/src/command.ts: -------------------------------------------------------------------------------- 1 | import { CommandModule } from 'yargs'; 2 | import { loadConfig, GraphQLConfig } from 'graphql-config'; 3 | import { loadDocuments, loadSchema } from '@graphql-tools/load'; 4 | import { Loader } from '@graphql-tools/utils'; 5 | import { LoadConfigOptions } from './types'; 6 | 7 | export type CommandFactory = (api: { 8 | useConfig: (options?: LoadConfigOptions) => Promise; 9 | useLoaders: typeof useLoaders; 10 | }) => CommandModule; 11 | 12 | export function defineCommand(factory: CommandFactory) { 13 | return factory; 14 | } 15 | 16 | export function useConfig(options: LoadConfigOptions = {}) { 17 | return loadConfig({ 18 | rootDir: process.cwd(), 19 | throwOnEmpty: true, 20 | throwOnMissing: true, 21 | ...options, 22 | }); 23 | } 24 | 25 | type PointerOf any> = Parameters[0]; 26 | type OptionsOf any> = Omit[1], 'loaders'>; 27 | 28 | export function useLoaders({ loaders }: { loaders: Loader[] }) { 29 | return { 30 | loadDocuments(pointer: PointerOf, options: OptionsOf) { 31 | return loadDocuments(pointer, { loaders, ...options }); 32 | }, 33 | loadSchema(pointer: PointerOf, options: OptionsOf) { 34 | return loadSchema(pointer, { loaders, ...options }); 35 | }, 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /website/docs/diff.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: diff 3 | title: diff 4 | sidebar_label: diff 5 | --- 6 | 7 | Detect changes to your GraphQL Schema and prevent breaking your existing applications. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/diff) for details. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/diff 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/diff 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Usage 39 | 40 | ``` 41 | graphql diff [OLD_SCHEMA] [NEW_SCHEMA] 42 | ``` 43 | 44 | #### Arguments 45 | 46 | | argument | description | default | 47 | | --- | --- | --- | 48 | | `OLD_SCHEMA` | A pointer to the old schema | `extensions.diff.baseSchema` property in GraphQL Config file | 49 | | `NEW_SCHEMA` | A pointer to the new schema | `schema` property in GraphQL Config file | 50 | 51 | #### Options 52 | 53 | | option | alias | description | default | 54 | | --- | --- | --- | --- | 55 | | `--require` | `-r` | Require a module | `[]` | 56 | | `--token` | `-t` | An access token | `undefined` | 57 | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | 58 | -------------------------------------------------------------------------------- /website/docs/similar.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: similar 3 | title: similar 4 | sidebar_label: similar 5 | --- 6 | 7 | Get a list of similar types in order to find duplicates. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/similar) for details. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/similar 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/similar 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Usage 39 | 40 | ``` 41 | graphql similar [SCHEMA] 42 | ``` 43 | 44 | #### Arguments 45 | 46 | | argument | description | default | 47 | | --- | --- | --- | 48 | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | 49 | 50 | #### Options 51 | 52 | | option | alias | description | default | 53 | | --- | --- | --- | --- | 54 | | `--type` | `-n` | Check only a single type | all types | 55 | | `--threshold` | `-t` | Threshold of similarity ratio | `0.4` | 56 | | `--write` | `-w` | Write a file with results | disabled | 57 | | `--require` | `-r` | Require a module | `[]` | 58 | | `--token` | `-t` | An access token | `undefined` | 59 | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | 60 | -------------------------------------------------------------------------------- /templates/fullstack/.graphqlrc.yml: -------------------------------------------------------------------------------- 1 | schema: ./server/src/schema/**/*.graphql 2 | documents: ./client/src/graphql/**/*.graphql 3 | extensions: 4 | codegen: 5 | generates: 6 | ./client/src/generated-types.tsx: 7 | config: 8 | withComponent: false 9 | withHOC: false 10 | withHooks: true 11 | skipDocumentsValidation: true 12 | plugins: 13 | - add: '/* tslint:disable */' 14 | - typescript 15 | - typescript-operations 16 | - typescript-react-apollo 17 | ./server/src/generated-types.ts: 18 | config: 19 | contextType: '@graphback/core#GraphbackContext' 20 | mappers: 21 | Comment: './generated-db-types#comment' 22 | Note: './generated-db-types#note' 23 | useIndexSignature: true 24 | skipDocumentsValidation: true 25 | plugins: 26 | - add: '/* tslint:disable */' 27 | - typescript 28 | - typescript-resolvers 29 | # Graphback configuration 30 | graphback: 31 | ## Input schema 32 | model: ./model 33 | ## Global configuration for CRUD generator 34 | crud: 35 | create: true 36 | update: true 37 | find: true 38 | findOne: true 39 | delete: true 40 | subCreate: true 41 | subUpdate: true 42 | subDelete: true 43 | ## Codegen plugins 44 | plugins: 45 | graphback-schema: 46 | outputPath: ./server/src/schema/schema.graphql 47 | graphback-client: 48 | outputFile: ./client/src/graphql/graphback.graphql -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/notes/CreateNote.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useCreateNoteMutation } from '../../generated-types'; 3 | import { Button, TextField, Card } from '@material-ui/core'; 4 | import './Note.css'; 5 | 6 | const CreateNote: React.FC = () => { 7 | const [createNote] = useCreateNoteMutation(); 8 | const [newNoteTitle, setNewNoteTitle] = useState(''); 9 | const [newNoteDescription, setNewNoteDescription] = useState(''); 10 | 11 | return ( 12 |
    13 | 14 |
    15 |

    Create Note

    16 |

    This application works only with sample Node/Comment model

    17 | setNewNoteTitle(e.target.value)} 21 | value={newNoteTitle} 22 | /> 23 | setNewNoteDescription(e.target.value)} 27 | value={newNoteDescription} 28 | /> 29 | 38 | 39 |
    40 |
    41 | ); 42 | }; 43 | 44 | export default CreateNote; 45 | -------------------------------------------------------------------------------- /website/docs/generate.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: generate 3 | title: generate 4 | sidebar_label: generate 5 | --- 6 | 7 | Generate schema and client documents for your GraphQL project by using [Graphback](https://graphback.dev). 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/generate 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/generate 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Example Configuration 39 | 40 | ```yml 41 | schema: './src/schema.graphql' 42 | documents: './client/src/graphql/**/*.graphql' 43 | extensions: 44 | graphback: 45 | model: './model/*.graphql' 46 | plugins: 47 | graphback-schema: 48 | outputPath: './src/schema/schema.graphql' 49 | graphback-client: 50 | outputFile: './client/src/graphql/graphback.graphql' 51 | ``` 52 | 53 | See [the docs](https://graphback.dev/docs/introduction) for more details. 54 | 55 | ### Usage 56 | 57 | ``` 58 | graphql generate 59 | ``` 60 | 61 | #### Arguments 62 | 63 | *None* 64 | 65 | #### Options 66 | 67 | | option | alias | description | default | 68 | | --- | --- | --- | --- | 69 | | `--watch` | `-w` | Watch for changes and execute generation automatically | | 70 | | `--db` | | | | 71 | | `--backend` | | | | 72 | | `--silent` | | | | 73 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: Website Deployment 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | deploy-website: 9 | name: Deploy Website 10 | timeout-minutes: 60 11 | runs-on: ubuntu-latest 12 | if: contains(github.event.head_commit.message, '[deploy_website]') || contains(github.ref, 'refs/tags/') 13 | steps: 14 | - name: Checkout Master 15 | uses: actions/checkout@v2 16 | - name: Configure Git Credientials 17 | run: | 18 | git config --global user.email "${{github.actor}}@users.noreply.github.com" 19 | git config --global user.name "${{github.actor}}" 20 | echo "machine github.com login ${{github.actor}} password ${{secrets.GITHUB_TOKEN}}" > ~/.netrc 21 | - name: Add origin remote and refetch master 22 | run: | 23 | git remote rm origin 24 | git remote add origin "https://github.com/${{github.repository}}" 25 | git fetch 26 | git checkout master 27 | git reset --hard 28 | - name: Use Node 29 | uses: actions/setup-node@v2 30 | with: 31 | node-version: '14.x' 32 | - name: Install Dependencies using Yarn 33 | run: yarn install --ignore-engines --frozen-lockfile 34 | - name: Deploy 🚀 35 | run: yarn deploy:website 36 | env: 37 | GIT_USER: ${{github.actor}} 38 | NEXT_PUBLIC_ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} 39 | NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY: a5522203ca95675199cc21edf09e6d75 40 | NEXT_PUBLIC_ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }} 41 | -------------------------------------------------------------------------------- /packages/commands/init/src/search-codegen-config.ts: -------------------------------------------------------------------------------- 1 | import { cosmiconfig, defaultLoaders } from 'cosmiconfig'; 2 | import { env } from 'string-env-interpolation'; 3 | 4 | function generateSearchPlaces(moduleName: string) { 5 | const extensions = ['json', 'yaml', 'yml', 'js', 'config.js']; 6 | // gives codegen.json... 7 | const regular = extensions.map((ext) => `${moduleName}.${ext}`); 8 | // gives .codegenrc.json... but no .codegenrc.config.js 9 | const dot = extensions.filter((ext) => ext !== 'config.js').map((ext) => `.${moduleName}rc.${ext}`); 10 | 11 | return regular.concat(dot); 12 | } 13 | 14 | function customLoader(ext: 'json' | 'yaml' | 'js') { 15 | function loader(filepath: string, content: string) { 16 | if (typeof process !== 'undefined' && 'env' in process) { 17 | content = env(content); 18 | } 19 | 20 | if (ext === 'json') { 21 | return defaultLoaders['.json'](filepath, content); 22 | } 23 | 24 | if (ext === 'yaml') { 25 | return defaultLoaders['.yaml'](filepath, content); 26 | } 27 | 28 | if (ext === 'js') { 29 | return defaultLoaders['.js'](filepath, content); 30 | } 31 | } 32 | 33 | return loader; 34 | } 35 | 36 | export async function searchCodegenConfig(cwd: string) { 37 | const moduleName = 'codegen'; 38 | const cosmi = cosmiconfig(moduleName, { 39 | searchPlaces: generateSearchPlaces(moduleName), 40 | loaders: { 41 | '.json': customLoader('json'), 42 | '.yaml': customLoader('yaml'), 43 | '.yml': customLoader('yaml'), 44 | '.js': customLoader('js'), 45 | noExt: customLoader('yaml'), 46 | }, 47 | }); 48 | return cosmi.search(cwd); 49 | } 50 | -------------------------------------------------------------------------------- /packages/commands/init/src/sources/from-open-api.ts: -------------------------------------------------------------------------------- 1 | import ora from 'ora'; 2 | import { safeLoad as YAMLParse } from 'js-yaml'; 3 | import { readFileSync, writeFileSync, ensureFile } from 'fs-extra'; 4 | import { prompt } from 'inquirer'; 5 | import { Context } from '../common'; 6 | 7 | export async function fromExistingOpenAPI(context: Context) { 8 | const { openApiPath } = await prompt<{ openApiPath: string }>([ 9 | { 10 | type: 'input', 11 | name: 'openApiPath', 12 | message: 'Enter your OpenAPI schema path', 13 | default: './swagger.json', 14 | }, 15 | ]); 16 | 17 | const processingOpenAPISpinner = ora(`Processing OpenAPI definition: ${openApiPath}`).start(); 18 | const schemaText: string = readFileSync(`${openApiPath}`, 'utf8'); 19 | 20 | const parsedObject = 21 | openApiPath.endsWith('yaml') || openApiPath.endsWith('yml') ? YAMLParse(schemaText) : JSON.parse(schemaText); 22 | 23 | const datamodelPath = `${context.graphqlConfig.extensions.graphback.model}/datamodel.graphql`; 24 | 25 | try { 26 | const { createGraphQLSchema } = await import('openapi-to-graphql'); 27 | const { schema } = await createGraphQLSchema(parsedObject, { 28 | strict: true, 29 | fillEmptyResponses: true, 30 | equivalentToMessages: false, 31 | }); 32 | const { printSchema } = await import('graphql'); 33 | const schemaString = printSchema(schema); 34 | 35 | await ensureFile(datamodelPath); 36 | 37 | writeFileSync(datamodelPath, schemaString); 38 | processingOpenAPISpinner.succeed(); 39 | } catch (err) { 40 | processingOpenAPISpinner.fail(`Failed to process OpenAPI definition: ${datamodelPath}. Error: ${err}`); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/notes/EditNote.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useUpdateNoteMutation } from '../../generated-types'; 3 | import { Button, TextField, Card } from '@material-ui/core'; 4 | import './Note.css'; 5 | 6 | type noteProps = { 7 | id: string; 8 | title: string; 9 | description: string | undefined; 10 | editState: any; 11 | }; 12 | 13 | const EditNote = ({ id, title, description, editState }: noteProps) => { 14 | const [updateNote] = useUpdateNoteMutation(); 15 | const [NoteTitle, setNoteTitle] = useState(title); 16 | const [NoteDescription, setNoteDescription] = useState(description); 17 | 18 | return ( 19 |
    20 | 21 |
    22 |

    Edit Note

    23 | setNoteTitle(e.target.value)} 27 | value={NoteTitle} 28 | /> 29 | setNoteDescription(e.target.value)} 33 | value={NoteDescription} 34 | /> 35 | 45 | 46 |
    47 |
    48 | ); 49 | }; 50 | 51 | export default EditNote; 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-cli-monorepo", 3 | "version": "4.1.0", 4 | "private": true, 5 | "author": "dotansimha ", 6 | "license": "MIT", 7 | "workspaces": { 8 | "packages": [ 9 | "packages/*", 10 | "packages/commands/*", 11 | "website", 12 | "integration" 13 | ] 14 | }, 15 | "scripts": { 16 | "release": "node scripts/release.js", 17 | "release:next": "node scripts/release.js --tag=next", 18 | "release:canary": "node scripts/release.js --canary", 19 | "test": "lerna run test", 20 | "build": "lerna run build", 21 | "format": "prettier --write \"**/*.{ts,tsx}\"", 22 | "deploy:website": "cd website && yarn deploy" 23 | }, 24 | "devDependencies": { 25 | "@graphql-tools/load": "6.2.8", 26 | "@types/express": "4.17.13", 27 | "@types/fs-extra": "9.0.13", 28 | "@types/fullname": "2.1.29", 29 | "@types/inquirer": "7.3.3", 30 | "@types/js-yaml": "3.12.7", 31 | "@types/node": "14.17.33", 32 | "@types/rimraf": "3.0.2", 33 | "@types/tmp": "0.2.2", 34 | "graphql": "15.7.2", 35 | "husky": "4.3.8", 36 | "lerna": "3.22.1", 37 | "lint-staged": "10.5.4", 38 | "prettier": "2.4.1", 39 | "rimraf": "3.0.2", 40 | "ts-node": "9.1.1", 41 | "typescript": "4.4.4" 42 | }, 43 | "publishConfig": { 44 | "access": "public" 45 | }, 46 | "lint-staged": { 47 | "*.{ts,tsx}": [ 48 | "tslint --fix", 49 | "prettier --write", 50 | "git add" 51 | ] 52 | }, 53 | "prettier": { 54 | "printWidth": 120, 55 | "singleQuote": true 56 | }, 57 | "resolutions": { 58 | "graphql": "15.7.2" 59 | }, 60 | "dependencies": { 61 | "@types/yargs": "15.0.14" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs'; 2 | import globby from 'globby'; 3 | import { join } from 'path'; 4 | import { CommandFactory, useConfig, useLoaders } from '@graphql-cli/common'; 5 | import discover from './discover'; 6 | 7 | export async function cli(): Promise { 8 | const program = yargs 9 | .scriptName('graphql') 10 | .detectLocale(false) 11 | .epilog('Visit https://github.com/Urigo/graphql-cli for more information') 12 | .version(); 13 | 14 | const commandPackageNames = await discoverCommands(); 15 | const commandFactories = await Promise.all(commandPackageNames.map(loadCommand)); 16 | 17 | [discover, ...commandFactories].forEach((cmd) => { 18 | program.command( 19 | cmd({ 20 | useConfig, 21 | useLoaders, 22 | }) 23 | ); 24 | }); 25 | 26 | program.demandCommand().recommendCommands().help().showHelpOnFail(false).argv; 27 | } 28 | 29 | async function discoverCommands() { 30 | const commandNames: string[] = []; 31 | const paths = require.resolve.paths('graphql-cli'); 32 | 33 | await Promise.all(paths.map(findInDirectory)); 34 | 35 | async function findInDirectory(directory: string) { 36 | const results = await globby('*', { 37 | cwd: join(directory, '@graphql-cli'), 38 | onlyDirectories: true, 39 | deep: 1, 40 | ignore: ['common', 'loaders'], 41 | }); 42 | 43 | if (results.length) { 44 | commandNames.push(...results); 45 | } 46 | } 47 | 48 | // unique names 49 | return commandNames.filter((val, i, list) => list.indexOf(val) === i); 50 | } 51 | 52 | function loadCommand(name: string): CommandFactory { 53 | const mod = require(`@graphql-cli/${name}`); 54 | 55 | return mod.default || mod; 56 | } 57 | -------------------------------------------------------------------------------- /website/docs/coverage.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: coverage 3 | title: coverage 4 | sidebar_label: coverage 5 | --- 6 | 7 | Schema coverage based on documents. Find out how many times types and fields are used in your application. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/coverage) for details. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/coverage 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/coverage 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Usage 39 | 40 | ``` 41 | graphql coverage [DOCUMENTS] [SCHEMA] 42 | ``` 43 | 44 | #### Arguments 45 | 46 | | argument | description | default | 47 | | --- | --- | --- | 48 | | `DOCUMENTS` | A glob pattern that points to GraphQL Documents / Operations | `documents` property in GraphQL Config file | 49 | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | 50 | 51 | #### Options 52 | 53 | | option | alias | description | default | 54 | | --- | --- | --- | --- | 55 | | `--silent` | `-s` | Do not render any stats in the terminal | `false` | 56 | | `--write` | `-w` | Write a file with coverage stats | disabled | 57 | | `--deprecated` | `-d` | Fail on deprecated usage | `false` | 58 | | `--require` | `-r` | Require a module | `[]` | 59 | | `--token` | `-t` | An access token | `undefined` | 60 | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | 61 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/comment/CreateComment.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useCreateCommentMutation } from '../../generated-types'; 3 | import { Card, TextField, Button } from '@material-ui/core'; 4 | import './../notes/Note.css'; 5 | 6 | type createCommentProps = { 7 | noteId: string; 8 | addCommentState: any; 9 | }; 10 | 11 | const CreateComment = ({ noteId, addCommentState }: createCommentProps) => { 12 | const [createComment] = useCreateCommentMutation(); 13 | const [newCommentTitle, setNewCommentTitle] = useState(''); 14 | const [newCommentDescription, setNewCommentDescription] = useState(''); 15 | 16 | return ( 17 |
    18 | 19 |
    20 |

    Create Comment

    21 | setNewCommentTitle(e.target.value)} 25 | value={newCommentTitle} 26 | /> 27 | setNewCommentDescription(e.target.value)} 31 | value={newCommentDescription} 32 | /> 33 | 45 | 46 |
    47 |
    48 | ); 49 | }; 50 | 51 | export default CreateComment; 52 | -------------------------------------------------------------------------------- /packages/commands/init/src/sources/from-existing.ts: -------------------------------------------------------------------------------- 1 | import rimraf from 'rimraf'; 2 | import { join } from 'path'; 3 | import { existsSync } from 'fs-extra'; 4 | import { prompt } from 'inquirer'; 5 | import { Context, PackageManifest } from '../common'; 6 | import { searchCodegenConfig } from '../search-codegen-config'; 7 | 8 | export async function fromExisting({ context, project }: { context: Context; project: PackageManifest }) { 9 | const manifestPath = join(context.path, 'package.json'); 10 | 11 | if (existsSync(manifestPath)) { 12 | const { name: projectName } = require(manifestPath); 13 | context.name = projectName; 14 | } 15 | 16 | const result = await searchCodegenConfig(context.path); 17 | 18 | if (result && !result.isEmpty) { 19 | const codegenFilePath = result.filepath; 20 | const { willBeMerged } = await prompt([ 21 | { 22 | type: 'confirm', 23 | name: 'willBeMerged', 24 | message: `GraphQL Code Generator configuration has been detected in ${codegenFilePath}.\n Do you want to use the same configuration with GraphQL CLI?`, 25 | default: true, 26 | }, 27 | ]); 28 | 29 | if (willBeMerged) { 30 | project.addDependency('@graphql-cli/codegen'); 31 | const codegenConfig = result.config; 32 | 33 | context.graphqlConfig.extensions.codegen = { 34 | generates: {}, 35 | }; 36 | 37 | for (const key in codegenConfig) { 38 | if (key === 'schema') { 39 | context.graphqlConfig.schema = codegenConfig.schema; 40 | } else if (key === 'documents') { 41 | context.graphqlConfig.documents = codegenConfig.documents; 42 | } else { 43 | context.graphqlConfig.extensions.codegen[key] = codegenConfig[key]; 44 | } 45 | } 46 | 47 | rimraf.sync(result.filepath); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/commands/init/src/features/inspector.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from 'inquirer'; 2 | import { Context, PackageManifest, ProjectType } from '../common'; 3 | 4 | export async function askForInspector({ context, project }: { context: Context; project: PackageManifest }) { 5 | if (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) { 6 | const { isFrontendInspectorAsked } = await prompt([ 7 | { 8 | type: 'confirm', 9 | name: 'isFrontendInspectorAsked', 10 | message: 'Do you want to have GraphQL Inspector tools for your frontend?', 11 | default: true, 12 | }, 13 | ]); 14 | 15 | if (isFrontendInspectorAsked) { 16 | project.addDependency('@graphql-cli/coverage'); 17 | project.addDependency('@graphql-cli/validate'); 18 | 19 | project.addScript('graphql:coverage', 'graphql coverage'); 20 | project.addScript('graphql:validate', 'graphql validate'); 21 | } 22 | } 23 | 24 | if (context.type === ProjectType.FullStack || context.type === ProjectType.BackendOnly) { 25 | const { isBackendInspectorAsked } = await prompt([ 26 | { 27 | type: 'confirm', 28 | name: 'isBackendInspectorAsked', 29 | message: 'Do you want to have GraphQL Inspector tools for your backend?', 30 | default: true, 31 | }, 32 | ]); 33 | 34 | if (isBackendInspectorAsked) { 35 | project.addDependency('@graphql-cli/diff'); 36 | project.addDependency('@graphql-cli/similar'); 37 | 38 | project.addScript('graphql:diff', 'graphql diff'); 39 | project.addScript('graphql:similar', 'graphql similar'); 40 | 41 | if (!context.graphqlConfig.extensions.diff) { 42 | context.graphqlConfig.extensions.diff = { 43 | baseSchema: 'your-base-schema-here', 44 | }; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/components/notes/OneNote.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import EditNote from './EditNote'; 3 | import { Note } from '../../generated-types'; 4 | import CreateComment from '../comment/CreateComment'; 5 | import OneComment from '../comment/OneComment'; 6 | import { Button, Card } from '@material-ui/core'; 7 | 8 | const OneNote = ({ id, title, description, comments }: Note) => { 9 | const [noteEdit, setNoteEdit] = useState(false); 10 | const [addComment, setAddComment] = useState(false); 11 | 12 | return ( 13 |
    14 | 15 |
  • 16 | {title}:  17 | {description} 18 | 21 | 24 | {noteEdit ? ( 25 | 26 | ) : ( 27 |
    28 | )} 29 | {addComment ? :
    } 30 |
      31 | {comments && comments.length > 0 ? ( 32 | comments.map((com) => { 33 | if (!com) { 34 | return; 35 | } 36 | return ; 37 | }) 38 | ) : ( 39 |
      40 | )} 41 |
    42 |
  • 43 |
    44 |
    45 | ); 46 | }; 47 | 48 | export default OneNote; 49 | -------------------------------------------------------------------------------- /templates/fullstack/client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
    32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.github/workflows/algolia-integrity.yml: -------------------------------------------------------------------------------- 1 | name: Algolia Integrity 2 | on: 3 | pull_request: 4 | paths: 5 | - 'website/**' 6 | jobs: 7 | algolia-records-check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - name: Use Node 18 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | cache: 'yarn' 19 | - name: Install Dependencies 20 | run: yarn --ignore-engines 21 | working-directory: ./website 22 | 23 | - name: Build Packages 24 | run: yarn build 25 | working-directory: ./website 26 | 27 | - name: Algolia generate 28 | run: yarn algolia-sync 29 | working-directory: ./website 30 | env: 31 | ALGOLIA_DRY_RUN: true 32 | SITE_URL: https://www.graphql-cli.com/ 33 | 34 | - name: Yarn build at root 35 | run: yarn 36 | 37 | - name: Prettier 38 | run: yarn prettier -w website/algolia-lockfile.json 39 | 40 | - name: Compare 41 | run: git diff origin/${{ github.base_ref }}.. -- website/algolia-lockfile.json 42 | 43 | - name: Diff to file 44 | if: always() 45 | id: diff_result 46 | run: | 47 | OUTPUT=$(git diff origin/${{ github.base_ref }}.. -- website/algolia-lockfile.json) 48 | OUTPUT="${OUTPUT//'%'/'%25'}" 49 | OUTPUT="${OUTPUT//$'\n'/'%0A'}" 50 | OUTPUT="${OUTPUT//$'\r'/'%0D'}" 51 | echo "::set-output name=result::$OUTPUT" 52 | 53 | - name: Publish a message 54 | if: always() && contains(steps.diff_result.outputs.result, 'diff') 55 | uses: marocchino/sticky-pull-request-comment@v2 56 | with: 57 | message: | 58 | ```diff 59 | ${{ steps.diff_result.outputs.result }} 60 | ``` 61 | -------------------------------------------------------------------------------- /website/docs/validate.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: validate 3 | title: validate 4 | sidebar_label: validate 5 | --- 6 | 7 | Validates documents against a schema and looks for deprecated usage.. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/validate) for details. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/validate 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/validate 33 | ``` 34 | 35 | 36 | 37 | 38 | ### Usage 39 | 40 | ``` 41 | graphql validate [DOCUMENTS] [SCHEMA] 42 | ``` 43 | 44 | #### Arguments 45 | 46 | | argument | description | default | 47 | | --- | --- | --- | 48 | | `DOCUMENTS` | A glob pattern that points to GraphQL Documents / Operations | `documents` property in GraphQL Config file | 49 | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | 50 | 51 | #### Options 52 | 53 | | option | alias | description | default | 54 | | --- | --- | --- | --- | 55 | | `--deprecated` | `-d` | Fail on deprecated usage | `false` | 56 | | `--noStrictFragments` | | Do not fail on duplicated fragment names | `false` | 57 | | `--apollo` | | Support Apollo directives (@client and @connection) | `false` | 58 | | `--keepClientFields` | | Keeps the fields with @client, but removes @client directive from them - works only with combination of `--apollo` | `false` | 59 | | `--maxDepth` | | Fail when operation depth exceeds maximum depth | `undefined` | 60 | | `--require` | `-r` | Require a module | `[]` | 61 | | `--token` | `-t` | An access token | `undefined` | 62 | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | 63 | -------------------------------------------------------------------------------- /.github/workflows/algolia-publish.yml: -------------------------------------------------------------------------------- 1 | name: Algolia Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | algolia-push-records: 10 | name: Push new records if changes 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Repo 14 | uses: actions/checkout@v3 15 | 16 | - name: Use Node 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 16 20 | cache: 'yarn' 21 | 22 | - name: Install Dependencies 23 | run: yarn 24 | working-directory: ./website 25 | 26 | - name: Build Packages 27 | run: yarn build 28 | working-directory: ./website 29 | 30 | - name: Algolia push 31 | run: yarn algolia-sync 32 | working-directory: ./website 33 | env: 34 | ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} 35 | ALGOLIA_ADMIN_API_KEY: ${{ secrets.ALGOLIA_ADMIN_API_KEY }} 36 | ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }} 37 | SITE_URL: https://www.graphql-cli.com/ 38 | 39 | - name: Yarn build at root 40 | run: yarn 41 | 42 | - name: Prettier 43 | run: yarn prettier -w website/algolia-lockfile.json 44 | 45 | - name: Compare 46 | run: git diff website/algolia-lockfile.json 47 | 48 | - name: Diff to file 49 | if: always() 50 | id: diff_result 51 | run: | 52 | OUTPUT=$(git diff website/algolia-lockfile.json) 53 | OUTPUT="${OUTPUT//'%'/'%25'}" 54 | OUTPUT="${OUTPUT//$'\n'/'%0A'}" 55 | OUTPUT="${OUTPUT//$'\r'/'%0D'}" 56 | echo "::set-output name=result::$OUTPUT" 57 | 58 | - name: Commit algolia-lockfile.json 59 | if: always() && contains(steps.diff_result.outputs.result, 'diff') 60 | uses: EndBug/add-and-commit@v9 61 | with: 62 | commit: website/algolia-lockfile.json 63 | message: Update algolia-lockfile.json 64 | default_author: github_actions 65 | -------------------------------------------------------------------------------- /website/docs/codegen.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: codegen 3 | title: codegen 4 | sidebar_label: codegen 5 | --- 6 | 7 | Generate code from your GraphQL schema and operations. See the official [GraphQL Code Generator](https://graphql-code-generator.com/) site for complete documentation, guides and more. 8 | 9 | ### Installation 10 | 11 | import Tabs from '@theme/Tabs'; 12 | import TabItem from '@theme/TabItem'; 13 | 14 | 21 | 22 | 23 | ``` 24 | yarn global add @graphql-cli/codegen 25 | ``` 26 | 27 | 28 | 29 | 30 | 31 | ``` 32 | npm i -g @graphql-cli/codegen 33 | ``` 34 | 35 | 36 | 37 | 38 | Note: GraphQL Code Generator also utilizes a plugin system, so make sure you also install any plugins you include inside your configuration. See [here](https://graphql-code-generator.com/docs/plugins/index) for a list of plugins. 39 | 40 | ### Example Configuration 41 | 42 | ```yml 43 | schema: 44 | - http://localhost:4000/graphql 45 | extensions: 46 | codegen: 47 | generates: 48 | ./graphql.schema.json: 49 | plugins: 50 | - "introspection" 51 | ``` 52 | 53 | See [the docs](https://graphql-code-generator.com/docs/getting-started/codegen-config) for more details. 54 | 55 | ### Usage 56 | 57 | ``` 58 | graphql codegen 59 | ``` 60 | 61 | #### Arguments 62 | 63 | *None* 64 | 65 | #### Options 66 | 67 | | option | alias | description | default | 68 | | --- | --- | --- | --- | 69 | | `--config` | `-c` | Path to GraphQL codegen YAML config file | `codegen.yml` or GraphQL configuration file in cwd | 70 | | `--watch` | `-w` | Watch for changes and execute generation automatically. You can also specify a glob expreession for custom watch list. | | 71 | | `--require` | `-r` | Loads specific require.extensions before running the codegen and reading the configuration | `[]` | 72 | | `--overwrite` | `-o` | Overwrites existing files | `true` | 73 | | `--silent` | `-s` | Suppresses printing errors | `false` | 74 | | `--project` | `-p` | Name of a project in GraphQL Config | `undefined` | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | release: 11 | types: [released, prereleased] 12 | 13 | jobs: 14 | test: 15 | name: Testing on ${{matrix.os}} and Node ${{matrix.node_version}} 16 | runs-on: ${{matrix.os}} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest] 20 | node_version: [12, 14, 16] 21 | steps: 22 | - name: Checkout Master 23 | uses: actions/checkout@v2 24 | - name: Use Node ${{matrix.node_version}} 25 | uses: actions/setup-node@master 26 | with: 27 | version: ${{ matrix.node_version }} 28 | - name: Install Dependencies using Yarn 29 | run: yarn --ignore-engines 30 | - name: Build 31 | run: yarn build 32 | - name: Test 33 | run: yarn test 34 | 35 | publish-canary: 36 | name: Publish Canary 37 | runs-on: ubuntu-latest 38 | if: ${{ github.event_name != 'release' }} 39 | steps: 40 | - name: Checkout Master 41 | uses: actions/checkout@v2 42 | - name: Use Node 43 | uses: actions/setup-node@v2 44 | with: 45 | node-version: 14 46 | - name: Install Dependencies using Yarn 47 | run: yarn --ignore-engines 48 | - name: Build 49 | run: yarn build 50 | - name: Release Canary 51 | run: | 52 | echo "Fork PR: ${{github.repository}}" 53 | if [ "${{github.repository}}" == "Urigo/graphql-cli" ] && [ "${{ secrets.NODE_AUTH_TOKEN }}" != "" ] 54 | then 55 | echo "//registry.npmjs.org/:_authToken=${{ secrets.NODE_AUTH_TOKEN }}" > .npmrc 56 | npm run release:canary 57 | else 58 | echo "Skipping canary publish due to a fork/PR..." 59 | fi 60 | 61 | publish: 62 | # publish to npm only when doing the release 63 | if: ${{ github.event_name == 'release' }} 64 | name: Publish Release 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: Checkout Master 68 | uses: actions/checkout@v2 69 | - name: Use Node 70 | uses: actions/setup-node@v2 71 | with: 72 | node-version: 14 73 | - name: Install Dependencies using Yarn 74 | run: yarn --ignore-engines 75 | - name: Build 76 | run: yarn build 77 | - name: Release 78 | run: echo "//registry.npmjs.org/:_authToken=${{secrets.NODE_AUTH_TOKEN}}" > ~/.npmrc && TAG=${GITHUB_REF#"refs/tags/"} npm run release 79 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/graphql/graphback.graphql: -------------------------------------------------------------------------------- 1 | fragment NoteFields on Note { 2 | id 3 | title 4 | description 5 | 6 | } 7 | 8 | fragment NoteExpandedFields on Note { 9 | id 10 | title 11 | description 12 | comments { 13 | id 14 | text 15 | description 16 | } 17 | } 18 | 19 | fragment CommentFields on Comment { 20 | id 21 | text 22 | description 23 | 24 | } 25 | 26 | fragment CommentExpandedFields on Comment { 27 | id 28 | text 29 | description 30 | note { 31 | id 32 | title 33 | description 34 | } 35 | } 36 | 37 | query findNotes($filter: NoteFilter, $page: PageRequest, $orderBy: OrderByInput) { 38 | findNotes(filter: $filter, page: $page, orderBy: $orderBy) { 39 | items { 40 | ...NoteExpandedFields 41 | } 42 | offset 43 | limit 44 | count 45 | } 46 | } 47 | 48 | query getNote($id: ID!) { 49 | getNote(id: $id) { 50 | ...NoteExpandedFields 51 | } 52 | } 53 | 54 | query findComments($filter: CommentFilter, $page: PageRequest, $orderBy: OrderByInput) { 55 | findComments(filter: $filter, page: $page, orderBy: $orderBy) { 56 | items { 57 | ...CommentExpandedFields 58 | } 59 | offset 60 | limit 61 | count 62 | } 63 | } 64 | 65 | query getComment($id: ID!) { 66 | getComment(id: $id) { 67 | ...CommentExpandedFields 68 | } 69 | } 70 | 71 | mutation createNote($input: CreateNoteInput!) { 72 | createNote(input: $input) { 73 | ...NoteFields 74 | } 75 | } 76 | 77 | 78 | mutation updateNote($input: MutateNoteInput!) { 79 | updateNote(input: $input) { 80 | ...NoteFields 81 | } 82 | } 83 | 84 | 85 | mutation deleteNote($input: MutateNoteInput!) { 86 | deleteNote(input: $input) { 87 | ...NoteFields 88 | } 89 | } 90 | 91 | 92 | mutation createComment($input: CreateCommentInput!) { 93 | createComment(input: $input) { 94 | ...CommentFields 95 | } 96 | } 97 | 98 | 99 | mutation updateComment($input: MutateCommentInput!) { 100 | updateComment(input: $input) { 101 | ...CommentFields 102 | } 103 | } 104 | 105 | 106 | mutation deleteComment($input: MutateCommentInput!) { 107 | deleteComment(input: $input) { 108 | ...CommentFields 109 | } 110 | } 111 | 112 | 113 | subscription newNote($filter: NoteSubscriptionFilter) { 114 | newNote(filter: $filter) { 115 | ...NoteFields 116 | } 117 | } 118 | 119 | subscription updatedNote($filter: NoteSubscriptionFilter) { 120 | updatedNote(filter: $filter) { 121 | ...NoteFields 122 | } 123 | } 124 | 125 | subscription deletedNote($filter: NoteSubscriptionFilter) { 126 | deletedNote(filter: $filter) { 127 | ...NoteFields 128 | } 129 | } 130 | 131 | subscription newComment($filter: CommentSubscriptionFilter) { 132 | newComment(filter: $filter) { 133 | ...CommentFields 134 | } 135 | } 136 | 137 | subscription updatedComment($filter: CommentSubscriptionFilter) { 138 | updatedComment(filter: $filter) { 139 | ...CommentFields 140 | } 141 | } 142 | 143 | subscription deletedComment($filter: CommentSubscriptionFilter) { 144 | deletedComment(filter: $filter) { 145 | ...CommentFields 146 | } 147 | } -------------------------------------------------------------------------------- /integration/test-project/schema/schema.graphql: -------------------------------------------------------------------------------- 1 | ## NOTE: This schema was generated by Graphback and should not be changed manually 2 | 3 | """Exposes a URL that specifies the behaviour of this scalar.""" 4 | directive @specifiedBy( 5 | """The URL that specifies the behaviour of this scalar.""" 6 | url: String! 7 | ) on SCALAR 8 | 9 | """ @model """ 10 | type Comment { 11 | id: ID! 12 | text: String 13 | description: String 14 | 15 | """@manyToOne(field: 'comments', key: 'noteId')""" 16 | note: Note 17 | } 18 | 19 | input CommentFilter { 20 | id: IDInput 21 | text: StringInput 22 | description: StringInput 23 | noteId: IDInput 24 | and: [CommentFilter!] 25 | or: [CommentFilter!] 26 | not: CommentFilter 27 | } 28 | 29 | type CommentResultList { 30 | items: [Comment]! 31 | offset: Int 32 | limit: Int 33 | count: Int 34 | } 35 | 36 | input CommentSubscriptionFilter { 37 | id: ID 38 | text: String 39 | description: String 40 | } 41 | 42 | input CreateCommentInput { 43 | id: ID 44 | text: String 45 | description: String 46 | noteId: ID 47 | } 48 | 49 | input CreateNoteInput { 50 | id: ID 51 | title: String! 52 | description: String 53 | } 54 | 55 | input IDInput { 56 | ne: ID 57 | eq: ID 58 | le: ID 59 | lt: ID 60 | ge: ID 61 | gt: ID 62 | in: [ID!] 63 | } 64 | 65 | input MutateCommentInput { 66 | id: ID! 67 | text: String 68 | description: String 69 | noteId: ID 70 | } 71 | 72 | input MutateNoteInput { 73 | id: ID! 74 | title: String 75 | description: String 76 | } 77 | 78 | type Mutation { 79 | createNote(input: CreateNoteInput!): Note 80 | updateNote(input: MutateNoteInput!): Note 81 | deleteNote(input: MutateNoteInput!): Note 82 | createComment(input: CreateCommentInput!): Comment 83 | updateComment(input: MutateCommentInput!): Comment 84 | deleteComment(input: MutateCommentInput!): Comment 85 | } 86 | 87 | """ @model """ 88 | type Note { 89 | id: ID! 90 | title: String! 91 | description: String 92 | 93 | """@oneToMany(field: 'note', key: 'noteId')""" 94 | comments(filter: CommentFilter): [Comment]! 95 | } 96 | 97 | input NoteFilter { 98 | id: IDInput 99 | title: StringInput 100 | description: StringInput 101 | and: [NoteFilter!] 102 | or: [NoteFilter!] 103 | not: NoteFilter 104 | } 105 | 106 | type NoteResultList { 107 | items: [Note]! 108 | offset: Int 109 | limit: Int 110 | count: Int 111 | } 112 | 113 | input NoteSubscriptionFilter { 114 | id: ID 115 | title: String 116 | description: String 117 | } 118 | 119 | input OrderByInput { 120 | field: String! 121 | order: SortDirectionEnum = ASC 122 | } 123 | 124 | input PageRequest { 125 | limit: Int 126 | offset: Int 127 | } 128 | 129 | type Query { 130 | getNote(id: ID!): Note 131 | findNotes(filter: NoteFilter, page: PageRequest, orderBy: OrderByInput): NoteResultList! 132 | getComment(id: ID!): Comment 133 | findComments(filter: CommentFilter, page: PageRequest, orderBy: OrderByInput): CommentResultList! 134 | } 135 | 136 | enum SortDirectionEnum { 137 | DESC 138 | ASC 139 | } 140 | 141 | input StringInput { 142 | ne: String 143 | eq: String 144 | le: String 145 | lt: String 146 | ge: String 147 | gt: String 148 | in: [String!] 149 | contains: String 150 | startsWith: String 151 | endsWith: String 152 | } 153 | 154 | type Subscription { 155 | newNote(filter: NoteSubscriptionFilter): Note! 156 | updatedNote(filter: NoteSubscriptionFilter): Note! 157 | deletedNote(filter: NoteSubscriptionFilter): Note! 158 | newComment(filter: CommentSubscriptionFilter): Comment! 159 | updatedComment(filter: CommentSubscriptionFilter): Comment! 160 | deletedComment(filter: CommentSubscriptionFilter): Comment! 161 | } -------------------------------------------------------------------------------- /templates/fullstack/server/src/schema/schema.graphql: -------------------------------------------------------------------------------- 1 | ## NOTE: This schema was generated by Graphback and should not be changed manually 2 | 3 | """Exposes a URL that specifies the behaviour of this scalar.""" 4 | directive @specifiedBy( 5 | """The URL that specifies the behaviour of this scalar.""" 6 | url: String! 7 | ) on SCALAR 8 | 9 | """ @model """ 10 | type Comment { 11 | id: ID! 12 | text: String 13 | description: String 14 | 15 | """@manyToOne(field: 'comments', key: 'noteId')""" 16 | note: Note 17 | } 18 | 19 | input CommentFilter { 20 | id: IDInput 21 | text: StringInput 22 | description: StringInput 23 | noteId: IDInput 24 | and: [CommentFilter!] 25 | or: [CommentFilter!] 26 | not: CommentFilter 27 | } 28 | 29 | type CommentResultList { 30 | items: [Comment]! 31 | offset: Int 32 | limit: Int 33 | count: Int 34 | } 35 | 36 | input CommentSubscriptionFilter { 37 | id: ID 38 | text: String 39 | description: String 40 | } 41 | 42 | input CreateCommentInput { 43 | id: ID 44 | text: String 45 | description: String 46 | noteId: ID 47 | } 48 | 49 | input CreateNoteInput { 50 | id: ID 51 | title: String! 52 | description: String 53 | } 54 | 55 | input IDInput { 56 | ne: ID 57 | eq: ID 58 | le: ID 59 | lt: ID 60 | ge: ID 61 | gt: ID 62 | in: [ID!] 63 | } 64 | 65 | input MutateCommentInput { 66 | id: ID! 67 | text: String 68 | description: String 69 | noteId: ID 70 | } 71 | 72 | input MutateNoteInput { 73 | id: ID! 74 | title: String 75 | description: String 76 | } 77 | 78 | type Mutation { 79 | createNote(input: CreateNoteInput!): Note 80 | updateNote(input: MutateNoteInput!): Note 81 | deleteNote(input: MutateNoteInput!): Note 82 | createComment(input: CreateCommentInput!): Comment 83 | updateComment(input: MutateCommentInput!): Comment 84 | deleteComment(input: MutateCommentInput!): Comment 85 | } 86 | 87 | """ @model """ 88 | type Note { 89 | id: ID! 90 | title: String! 91 | description: String 92 | 93 | """@oneToMany(field: 'note', key: 'noteId')""" 94 | comments(filter: CommentFilter): [Comment]! 95 | } 96 | 97 | input NoteFilter { 98 | id: IDInput 99 | title: StringInput 100 | description: StringInput 101 | and: [NoteFilter!] 102 | or: [NoteFilter!] 103 | not: NoteFilter 104 | } 105 | 106 | type NoteResultList { 107 | items: [Note]! 108 | offset: Int 109 | limit: Int 110 | count: Int 111 | } 112 | 113 | input NoteSubscriptionFilter { 114 | id: ID 115 | title: String 116 | description: String 117 | } 118 | 119 | input OrderByInput { 120 | field: String! 121 | order: SortDirectionEnum = ASC 122 | } 123 | 124 | input PageRequest { 125 | limit: Int 126 | offset: Int 127 | } 128 | 129 | type Query { 130 | getNote(id: ID!): Note 131 | findNotes(filter: NoteFilter, page: PageRequest, orderBy: OrderByInput): NoteResultList! 132 | getComment(id: ID!): Comment 133 | findComments(filter: CommentFilter, page: PageRequest, orderBy: OrderByInput): CommentResultList! 134 | } 135 | 136 | enum SortDirectionEnum { 137 | DESC 138 | ASC 139 | } 140 | 141 | input StringInput { 142 | ne: String 143 | eq: String 144 | le: String 145 | lt: String 146 | ge: String 147 | gt: String 148 | in: [String!] 149 | contains: String 150 | startsWith: String 151 | endsWith: String 152 | } 153 | 154 | type Subscription { 155 | newNote(filter: NoteSubscriptionFilter): Note! 156 | updatedNote(filter: NoteSubscriptionFilter): Note! 157 | deletedNote(filter: NoteSubscriptionFilter): Note! 158 | newComment(filter: CommentSubscriptionFilter): Comment! 159 | updatedComment(filter: CommentSubscriptionFilter): Comment! 160 | deletedComment(filter: CommentSubscriptionFilter): Comment! 161 | } -------------------------------------------------------------------------------- /website/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'GraphQL CLI', 3 | tagline: '📟 Command line tool for common GraphQL development workflows', 4 | 5 | url: 'https://graphql-cli.com', 6 | baseUrl: '/', 7 | favicon: 'img/logo.ico', 8 | 9 | organizationName: 'urigo', 10 | projectName: 'graphql-cli', 11 | 12 | themeConfig: { 13 | colorMode: { 14 | disableSwitch: true, 15 | }, 16 | image: 'img/logo.png', 17 | navbar: { 18 | title: 'GraphQL CLI', 19 | logo: { 20 | alt: 'GraphQL CLI Logo', 21 | src: 'img/logo.png', 22 | }, 23 | items: [ 24 | { 25 | to: '/introduction', 26 | label: 'Documentation', 27 | position: 'right', 28 | }, 29 | { 30 | href: 'https://github.com/urigo/graphql-cli', 31 | label: 'GitHub', 32 | position: 'right', 33 | }, 34 | ], 35 | }, 36 | footer: { 37 | style: 'dark', 38 | copyright: `Copyright © ${new Date().getFullYear()} The Guild. All rights reserved.`, 39 | links: [ 40 | { 41 | title: 'Docs', 42 | items: [ 43 | { 44 | label: 'Introduction', 45 | to: 'introduction', 46 | }, 47 | ], 48 | }, 49 | { 50 | title: 'Community', 51 | items: [ 52 | { 53 | label: 'Discord', 54 | href: 'https://discord.gg/xud7bH9', 55 | }, 56 | { 57 | label: 'Other projects', 58 | href: 'https://github.com/the-guild-org/Stack', 59 | }, 60 | { 61 | label: 'Mailing List', 62 | href: 'https://upscri.be/19qjhi', 63 | }, 64 | { 65 | label: 'Community Meetings', 66 | href: 'https://github.com/the-guild-org/community-meetings', 67 | }, 68 | ], 69 | }, 70 | { 71 | title: 'Social', 72 | items: [ 73 | { 74 | label: 'Blog', 75 | href: 'https://the-guild.dev/blog', 76 | }, 77 | { 78 | label: 'GitHub', 79 | href: 'https://github.com/urigo/graphql-cli', 80 | }, 81 | { 82 | label: 'Twitter', 83 | href: 'https://twitter.com/TheGuildDev', 84 | }, 85 | { 86 | label: 'LinkedIn', 87 | href: 'https://www.linkedin.com/company/the-guild-software', 88 | }, 89 | ], 90 | }, 91 | ], 92 | }, 93 | prism: { 94 | theme: require('prism-react-renderer/themes/dracula'), 95 | }, 96 | }, 97 | scripts: ['/js/light-mode-by-default.js'], 98 | customFields: { 99 | algolia: { 100 | appId: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, 101 | searchApiKey: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY, 102 | indexName: process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME, 103 | }, 104 | }, 105 | presets: [ 106 | [ 107 | require.resolve('@docusaurus/preset-classic'), 108 | { 109 | docs: { 110 | path: 'docs', 111 | routeBasePath: '/', 112 | include: ['**/*.md', '**/*.mdx'], 113 | sidebarPath: require.resolve('./sidebars.js'), 114 | editUrl: 'https://github.com/urigo/graphql-cli/edit/master/website/', 115 | }, 116 | theme: { 117 | customCss: require.resolve('./src/css/custom.css'), 118 | }, 119 | sitemap: { 120 | // cacheTime: 600 * 1001, // 600 sec - cache purge period 121 | changefreq: 'weekly', 122 | priority: 0.5, 123 | }, 124 | }, 125 | ], 126 | ], 127 | }; 128 | -------------------------------------------------------------------------------- /website/docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: introduction 3 | title: Introduction 4 | sidebar_label: Introduction 5 | --- 6 | 7 | ![GraphQL CLI](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) 8 | 9 | ### Features 10 | 11 | * Helpful commands to improve your workflows 12 | * Compatible with editors and IDEs based on [graphql-config](https://www.graphql-config.com) 13 | * Powerful plugin system to extend graphql-cli with custom commands 14 | 15 | ### Installation 16 | 17 | Run the following command to install GraphQL CLI globally: 18 | 19 | import Tabs from '@theme/Tabs'; 20 | import TabItem from '@theme/TabItem'; 21 | 22 | 29 | 30 | 31 | ``` 32 | yarn global add graphql-cli graphql 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | ``` 40 | npm i -g graphql-cli graphql 41 | ``` 42 | 43 | 44 | 45 | 46 | ### Configuration 47 | 48 | GraphQL CLI utilizes GraphQL Config for its configuration. You can learn more about GraphQL Config [here](https://www.graphql-config.com). The easiest way to get started is to run `init` command from your desired workspace: 49 | 50 | ``` 51 | npx graphql-cli init 52 | ``` 53 | 54 | After a series of questions from the command-prompt, the command will use the inputs and selected project templates to generate your configuration file for you. You can also write your own configuration file using an editor of your choice. For example, you could create a `.graphqlrc.yml` file with the following content: 55 | 56 | ``` 57 | schema: "server/src/schema/**/*.graphql" 58 | documents: "client/src/documents/**/*.graphql" 59 | ``` 60 | 61 | If you can run the `init` command with an existing file like the one above, its contents will be included with inputs you provide. 62 | 63 | ### Commands 64 | 65 | Each command in GraphQL CLI is treated as a plugin. In order to use the command, you have to install it first. Each command's package name follows this pattern: `@graphql-cli/[COMMAND-NAME]`. So to install the `init` command we used above, we would run 66 | 67 | 74 | 75 | 76 | ``` 77 | yarn global add @graphql-cli/init 78 | ``` 79 | 80 | 81 | 82 | 83 | 84 | ``` 85 | npm i -g @graphql-cli/init 86 | ``` 87 | 88 | 89 | 90 | 91 | After installing the command, we can then run it like this: 92 | 93 | ``` 94 | graphql init 95 | ``` 96 | 97 | Each command can be configured by updating the `extensions` field in your configuration file (`.graphqlrc.yml`). For example, if we install the `codegen` command, we can provide additional options to it like this: 98 | 99 | ```yml 100 | schema: 101 | ./server/src/schema/**/*.ts: 102 | require: ts-node/register 103 | documents: ./client/src/graphql/**/*.ts 104 | extensions: 105 | codegen: 106 | generates: 107 | ./server/src/generated-types.d.ts: 108 | plugins: 109 | - typescript 110 | - typescript-resolvers 111 | ./client/src/generated-types.tsx: 112 | plugins: 113 | - typescript 114 | - typescript-operations 115 | - typescript-react-apollo 116 | ``` 117 | 118 | You can learn more about each command by navigating to its page from the menu. You can also write your own commands; see [this guide](custom-commands) for a detailed explanation. 119 | 120 | :::tip 121 | Note: You can execute the command `graphql discover` to open a list of GraphQL CLI plugins you can still. This is the only command that is available without installing additional packages. 122 | ::: 123 | -------------------------------------------------------------------------------- /scripts/release.js: -------------------------------------------------------------------------------- 1 | const { argv } = require('yargs'); 2 | const { sync: glob } = require('globby'); 3 | const { writeFile } = require('fs-extra'); 4 | const { resolve, dirname, join } = require('path'); 5 | const semver = require('semver'); 6 | const cp = require('child_process'); 7 | const rootPackageJson = require('../package.json'); 8 | 9 | async function release() { 10 | let version = process.env.RELEASE_VERSION || rootPackageJson.version; 11 | 12 | if (version.startsWith('v')) { 13 | version = version.replace('v', ''); 14 | } 15 | 16 | let tag = argv.tag || 'latest'; 17 | 18 | if (argv.canary) { 19 | const gitHash = cp.spawnSync('git', ['rev-parse', '--short', 'HEAD']).stdout.toString().trim(); 20 | version = semver.inc(version, 'prerelease', true, 'alpha-' + gitHash); 21 | tag = 'canary'; 22 | } 23 | 24 | const workspaceGlobs = (rootPackageJson.workspaces.packages || rootPackageJson.workspaces).map( 25 | (workspace) => workspace + '/package.json' 26 | ); 27 | const packageJsonPaths = glob(workspaceGlobs).map((packageJsonPath) => resolve(process.cwd(), packageJsonPath)); 28 | const packageNames = packageJsonPaths.map((packageJsonPath) => require(packageJsonPath).name); 29 | 30 | rootPackageJson.version = version; 31 | 32 | await writeFile(resolve(__dirname, '../package.json'), JSON.stringify(rootPackageJson, null, 2)); 33 | 34 | await Promise.all( 35 | packageJsonPaths.map(async (packageJsonPath) => { 36 | const packageJson = require(packageJsonPath); 37 | 38 | packageJson.version = version; 39 | 40 | for (const dependency in packageJson.dependencies) { 41 | if (packageNames.includes(dependency)) { 42 | packageJson.dependencies[dependency] = version; 43 | } 44 | } 45 | 46 | for (const dependency in packageJson.devDependencies) { 47 | if (packageNames.includes(dependency)) { 48 | packageJson.devDependencies[dependency] = version; 49 | } 50 | } 51 | 52 | await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); 53 | 54 | if (!packageJson.private) { 55 | const distDirName = (packageJson.publishConfig && packageJson.publishConfig.directory) || ''; 56 | const distPath = join(dirname(packageJsonPath), distDirName); 57 | 58 | //Fix package.json in dist directory 59 | const distPackageJsonPath = join(distPath, 'package.json'); 60 | const distPackageJson = require(distPackageJsonPath); 61 | 62 | distPackageJson.name = packageJson.name; 63 | distPackageJson.version = packageJson.version; 64 | distPackageJson.dependencies = packageJson.dependencies; 65 | distPackageJson.devDependencies = packageJson.devDependencies; 66 | distPackageJson.publishConfig = { 67 | access: (packageJson.publishConfig && packageJson.publishConfig.access) || 'public', 68 | }; 69 | 70 | await writeFile(distPackageJsonPath, JSON.stringify(distPackageJson, null, 2)); 71 | 72 | return new Promise((resolve, reject) => { 73 | const publishSpawn = cp.spawn('npm', [ 74 | 'publish', 75 | distPath, 76 | '--tag', 77 | tag, 78 | '--access', 79 | distPackageJson.publishConfig.access, 80 | ]); 81 | 82 | publishSpawn.stdout.on('data', (data) => { 83 | console.info(data.toString('utf8')); 84 | }); 85 | 86 | publishSpawn.stderr.on('data', function (data) { 87 | console.error(data.toString('utf8')); 88 | }); 89 | 90 | publishSpawn.on('exit', function (code, signal) { 91 | if (code !== 0) { 92 | reject(new Error(`npm publish exited with code: ${code} and signal: ${signal}`)); 93 | } else { 94 | resolve(); 95 | } 96 | }); 97 | }); 98 | } 99 | }) 100 | ); 101 | 102 | console.info(`${tag} => ${version}`); 103 | } 104 | 105 | release().catch((err) => { 106 | console.error(err); 107 | process.exit(1); 108 | }); 109 | -------------------------------------------------------------------------------- /docs/CUSTOM_EXTENSION.md: -------------------------------------------------------------------------------- 1 | ## Writing your own GraphQL-CLI Extension 2 | 3 | `graphql-cli` allow you to write your own plugin/extenion, and intergrate external tools and configuration, and run it from a single CLI. 4 | 5 | The current implementation of `graphql-cli` is using [Yargs](https://yargs.js.org/) to manage it's CLI commands. 6 | 7 | Plugins and extension are treated as NodeJS module by the `graphql-cli`, so it means you can use JavaScript/TypeScript/Any other super-set of JavaScript to write your extension. It means that you plugin will be loaded by it's name under `node_modules` - for example `graphql-cli my-custom-plugin ...`. 8 | 9 | `graphql-cli` also supports `graphql-config`, so it can help you easily load your GraphQL schema, operations and configuration from a unified config file. 10 | 11 | > If you are wrapping an existing tool that has it's own CLI already, consider to expose a programatic API so it will be easier to consume. 12 | 13 | ### TL;DR 14 | 15 | We have a ready-to-use boilerplate for that purpose, [you can find it here](https://github.com/dotansimha/graphql-cli-plugin-example). 16 | 17 | Also, inside this repo, under `packages/commands` you can find a set of plugins implementation you can use as reference. 18 | 19 | ### Getting Started 20 | 21 | Start by creating a simple JavaScript/TypeScript project, according to your preference. Install `@graphql-cli/common` package and use `defineCommand` utility in your entry point (usually `index` file): 22 | 23 | ```ts 24 | import { defineCommand } from '@graphql-cli/common'; 25 | 26 | export default defineCommand((api) => { 27 | return {}; 28 | }); 29 | ``` 30 | 31 | To register your CLI command, give it a name first. Use the `command` property: 32 | 33 | ```ts 34 | export default defineCommand((api) => { 35 | return { 36 | command: 'my-plugin', 37 | async handler() { 38 | // code here 39 | }, 40 | }; 41 | }); 42 | ``` 43 | 44 | Now, your plugin will be avaiable to use with the following command: `graphql my-plugin`. 45 | 46 | You can also add custom validations, flags, default values and much more with Yargs. [You can read the documentation here](https://yargs.js.org/docs/#api-commandcmd-desc-module). 47 | 48 | ## Testing your plugin locally 49 | 50 | To test your plugin locally, install `graphql-cli` in your project as a `devDependency`, and run the following command: 51 | 52 | ``` 53 | graphql ./src/index.js 54 | ``` 55 | 56 | If you registerd sub-commands, you should be able to run those this way: 57 | 58 | ``` 59 | graphql ./src/index.js do-something 60 | ``` 61 | 62 | > The path should point to the entry point of your script, and if you are using TypeScript - point to the compile file. 63 | 64 | ## Loading GraphQL Schema 65 | 66 | To easily load GraphQL schema, you can use `graphql-config`: 67 | 68 | ```ts 69 | import { defineCommand } from '@graphql-cli/common'; 70 | 71 | export default defineCommand((api) => { 72 | return { 73 | command: 'my-plugin', 74 | builder(build) { 75 | return build.options({ 76 | project: { 77 | type: 'string', 78 | describe: 'Name of your project', 79 | }, 80 | }); 81 | }, 82 | async handler(args) { 83 | // use graphql-config and find configuration 84 | const config = await api.useConfig(); 85 | // pick project 86 | const project = args.project ? config.getProject(args.project) : config.getDefault(); 87 | // get schema 88 | const schema = await config.getSchema(); 89 | }, 90 | }; 91 | }); 92 | ``` 93 | 94 | If you are using `graphql-config` to define your configuration, and you wish to load your extenion config from it, do: 95 | 96 | ```ts 97 | type MyConfig = { ... }; 98 | 99 | const extensionConfig = await config.extension('my-plugin'); 100 | ``` 101 | 102 | ## Error Handling 103 | 104 | If you wish to fail the execution of your plugin and report it back to GraphQL CLI host, simply throw an error: 105 | 106 | ```ts 107 | import { defineCommand } from '@graphql-cli/common'; 108 | 109 | export default defineCommand(() => { 110 | return { 111 | command: 'check-if-missing', 112 | handler() { 113 | if (somethingIsMissing) { 114 | throw new Error(`Ooops, something is missing`); 115 | } 116 | }, 117 | }; 118 | }); 119 | ``` 120 | -------------------------------------------------------------------------------- /packages/commands/init/src/features/codegen.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from 'inquirer'; 2 | import { Context, PackageManifest, ProjectType, FrontendType, BackendType, askForEnum } from '../common'; 3 | 4 | export async function askForCodegen({ context, project }: { context: Context; project: PackageManifest }) { 5 | if (!context.graphqlConfig.extensions.codegen) { 6 | const { isCodegenAsked } = await prompt([ 7 | { 8 | type: 'confirm', 9 | name: 'isCodegenAsked', 10 | message: 'Do you want to use GraphQL Code Generator?', 11 | default: true, 12 | }, 13 | ]); 14 | if (isCodegenAsked) { 15 | project.addDependency('@graphql-cli/codegen'); 16 | project.addScript('graphql:codegen', 'graphql codegen'); 17 | 18 | context.graphqlConfig.extensions.codegen = { 19 | generates: {}, 20 | }; 21 | let codegenPlugins = new Set(); 22 | if (context.type === ProjectType.FullStack || context.type === ProjectType.BackendOnly) { 23 | const backendType = await askForEnum({ 24 | enum: BackendType, 25 | message: 'What type of backend do you use?', 26 | defaultValue: BackendType.TS, 27 | }); 28 | 29 | switch (backendType) { 30 | case BackendType.TS: 31 | codegenPlugins.add('typescript'); 32 | codegenPlugins.add('typescript-resolvers'); 33 | break; 34 | case BackendType.Java: 35 | codegenPlugins.add('java'); 36 | codegenPlugins.add('java-resolvers'); 37 | break; 38 | case BackendType.Kotlin: 39 | codegenPlugins.add('java'); 40 | codegenPlugins.add('java-kotlin'); 41 | break; 42 | } 43 | 44 | const { backendGeneratedFile } = await prompt([ 45 | { 46 | type: 'input', 47 | name: 'backendGeneratedFile', 48 | message: 'Where do you want to have generated backend code?', 49 | default: './generated-backend.ts', 50 | }, 51 | ]); 52 | 53 | context.graphqlConfig.extensions.codegen.generates[backendGeneratedFile] = { 54 | plugins: [...codegenPlugins], 55 | }; 56 | } 57 | 58 | if (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) { 59 | const frontendType = await askForEnum({ 60 | enum: FrontendType, 61 | message: 'What type of frontend do you use?', 62 | defaultValue: FrontendType.TSReactApollo, 63 | }); 64 | 65 | switch (frontendType) { 66 | case FrontendType.TSReactApollo: 67 | codegenPlugins.add('typescript'); 68 | codegenPlugins.add('typescript-react-apollo'); 69 | break; 70 | case FrontendType.ApolloAngular: 71 | codegenPlugins.add('typescript'); 72 | codegenPlugins.add('typescript-apollo-angular'); 73 | break; 74 | case FrontendType.StencilApollo: 75 | codegenPlugins.add('typescript'); 76 | codegenPlugins.add('typescript-stencil-apollo'); 77 | break; 78 | case FrontendType.TSUrql: 79 | codegenPlugins.add('typescript'); 80 | codegenPlugins.add('typescript-urql'); 81 | break; 82 | case FrontendType.GraphQLRequest: 83 | codegenPlugins.add('typescript'); 84 | codegenPlugins.add('typescript-graphql-request'); 85 | break; 86 | case FrontendType.ApolloAndroid: 87 | codegenPlugins.add('java-apollo-android'); 88 | break; 89 | } 90 | 91 | const { frontendGeneratedFile } = await prompt([ 92 | { 93 | type: 'input', 94 | name: 'frontendGeneratedFile', 95 | message: 'Where do you want to have generated frontend code?', 96 | default: './generated-frontend.ts', 97 | }, 98 | ]); 99 | 100 | context.graphqlConfig.extensions.codegen.generates[frontendGeneratedFile] = { 101 | plugins: [...codegenPlugins], 102 | }; 103 | } 104 | for (const codegenPlugin of codegenPlugins) { 105 | project.addDependency('@graphql-codegen/' + codegenPlugin); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /packages/commands/init/src/common.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import { ensureFile, writeFileSync } from 'fs-extra'; 3 | import { prompt } from 'inquirer'; 4 | import fullName from 'fullname'; 5 | import latestVersion from 'latest-version'; 6 | 7 | type StandardEnum = { 8 | [id: string]: T | string; 9 | [nu: number]: string; 10 | }; 11 | 12 | export async function askForEnum>(options: { 13 | enum: Enum; 14 | message: string; 15 | defaultValue?: T; 16 | ignoreList?: (T | string)[]; 17 | }): Promise { 18 | let choices: (T | string)[]; 19 | const enumValues = Object.values(options.enum); 20 | if (options.ignoreList) { 21 | choices = enumValues.filter((enumValue) => !options.ignoreList.includes(enumValue)); 22 | } else { 23 | choices = enumValues; 24 | } 25 | 26 | const { answer } = await prompt<{ answer: T }>([ 27 | { 28 | type: 'list', 29 | name: 'answer', 30 | message: options.message, 31 | choices, 32 | default: options.defaultValue, 33 | }, 34 | ]); 35 | return answer; 36 | } 37 | 38 | export interface Context { 39 | name: string; 40 | path: string; 41 | type?: ProjectType; 42 | graphqlConfig: any; 43 | } 44 | 45 | export enum InitializationType { 46 | FromScratch = 'I want to create a new project from a GraphQL CLI Project Template.', 47 | ExistingOpenAPI = 'I have an existing project using OpenAPI/Swagger Schema Definition.', 48 | ExistingGraphQL = 'I have an existing project using GraphQL and want to add GraphQL CLI (run from project root).', 49 | } 50 | 51 | export enum ProjectType { 52 | FullStack = 'Full Stack', 53 | FrontendOnly = 'Frontend only', 54 | BackendOnly = 'Backend only', 55 | } 56 | 57 | export enum FrontendType { 58 | TSReactApollo = 'TypeScript React Apollo', 59 | ApolloAngular = 'Apollo Angular', 60 | StencilApollo = 'Stencil Apollo', 61 | TSUrql = 'TypeScript Urql', 62 | GraphQLRequest = 'GraphQL Request', 63 | ApolloAndroid = 'Apollo Android', 64 | Other = 'Other', 65 | } 66 | 67 | export enum BackendType { 68 | TS = 'TypeScript', 69 | Java = 'Java', 70 | Kotlin = 'Kotlin', 71 | Other = 'Other', 72 | } 73 | 74 | export type PackageManifest = ReturnType; 75 | export function managePackageManifest() { 76 | const packages = new Set(); 77 | const scripts = new Map(); 78 | 79 | return { 80 | addDependency(name: string) { 81 | packages.add(name); 82 | }, 83 | addScript(name: string, script: string) { 84 | scripts.set(name, script); 85 | }, 86 | async writePackage({ 87 | path, 88 | name, 89 | initializationType, 90 | }: { 91 | path: string; 92 | name: string; 93 | initializationType: InitializationType; 94 | }) { 95 | let packageJson: any = {}; 96 | const packageJsonPath = join(path, 'package.json'); 97 | 98 | // Try to load existing package.json 99 | try { 100 | const importedPackageJson = require(packageJsonPath); 101 | 102 | packageJson = importedPackageJson || {}; 103 | } catch (err) {} 104 | 105 | if (initializationType === InitializationType.FromScratch) { 106 | packageJson.private = true; 107 | packageJson.name = name; 108 | 109 | const author = await fullName(); 110 | if (author) { 111 | packageJson.author = { 112 | name: author, 113 | }; 114 | } 115 | } 116 | 117 | for (const [scriptName, scriptValue] of scripts) { 118 | if (!packageJson.scripts) { 119 | packageJson.scripts = {}; 120 | } 121 | 122 | if (!packageJson.scripts[scriptName]) { 123 | packageJson.scripts[scriptName] = scriptValue; 124 | } 125 | } 126 | 127 | // Add dev dependencies 128 | packageJson.devDependencies = packageJson.devDependencies || {}; 129 | 130 | for await (const npmDependency of packages) { 131 | if (!(npmDependency in packageJson.devDependencies)) { 132 | packageJson.devDependencies[npmDependency] = await latestVersion(npmDependency); 133 | } 134 | } 135 | 136 | await ensureFile(packageJsonPath); 137 | writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); 138 | }, 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /website/docs/custom-commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: custom-commands 3 | title: Custom commands 4 | sidebar_label: Custom commands 5 | --- 6 | 7 | ## Writing your own commands 8 | 9 | `graphql-cli` allow you to write your own plugin/extenion, and intergrate external tools and configuration, and run it from a single CLI. 10 | 11 | The current implementation of `graphql-cli` is using [Yargs](https://yargs.js.org/) to manage it's CLI commands. 12 | 13 | Plugins and extension are treated as NodeJS module by the `graphql-cli`, so it means you can use JavaScript/TypeScript/Any other super-set of JavaScript to write your extension. It means that you plugin will be loaded by it's name under `node_modules` - for example `graphql-cli my-custom-plugin ...`. 14 | 15 | `graphql-cli` also supports `graphql-config`, so it can help you easily load your GraphQL schema, operations and configuration from a unified config file. 16 | 17 | > If you are wrapping an existing tool that has it's own CLI already, consider to expose a programatic API so it will be easier to consume. 18 | 19 | ### TL;DR 20 | 21 | We have a ready-to-use boilerplate for that purpose, [you can find it here](https://github.com/dotansimha/graphql-cli-plugin-example). 22 | 23 | Also, inside this repo, under `packages/commands` you can find a set of plugins implementation you can use as reference. 24 | 25 | ### Getting Started 26 | 27 | Start by creating a simple JavaScript/TypeScript project, according to your preference. Install `@graphql-cli/common` package and use `defineCommand` utility in your entry point (usually `index` file): 28 | 29 | ```ts 30 | import { defineCommand } from '@graphql-cli/common'; 31 | 32 | export default defineCommand((api) => { 33 | return {}; 34 | }); 35 | ``` 36 | 37 | To register your CLI command, give it a name first. Use the `command` property: 38 | 39 | ```ts 40 | export default defineCommand((api) => { 41 | return { 42 | command: 'my-plugin', 43 | async handler() { 44 | // code here 45 | }, 46 | }; 47 | }); 48 | ``` 49 | 50 | Now, your plugin will be avaiable to use with the following command: `graphql my-plugin`. 51 | 52 | You can also add custom validations, flags, default values and much more with Yargs. [You can read the documentation here](https://yargs.js.org/docs/#api-commandcmd-desc-module). 53 | 54 | ## Testing your plugin locally 55 | 56 | To test your plugin locally, install `graphql-cli` in your project as a `devDependency`, and run the following command: 57 | 58 | ``` 59 | graphql ./src/index.js 60 | ``` 61 | 62 | If you registerd sub-commands, you should be able to run those this way: 63 | 64 | ``` 65 | graphql ./src/index.js do-something 66 | ``` 67 | 68 | > The path should point to the entry point of your script, and if you are using TypeScript - point to the compile file. 69 | 70 | ## Loading GraphQL Schema 71 | 72 | To easily load GraphQL schema, you can use `graphql-config`: 73 | 74 | ```ts 75 | import { defineCommand } from '@graphql-cli/common'; 76 | 77 | export default defineCommand((api) => { 78 | return { 79 | command: 'my-plugin', 80 | builder(build) { 81 | return build.options({ 82 | project: { 83 | type: 'string', 84 | describe: 'Name of your project', 85 | }, 86 | }); 87 | }, 88 | async handler(args) { 89 | // use graphql-config and find configuration 90 | const config = await api.useConfig(); 91 | // pick project 92 | const project = args.project ? config.getProject(args.project) : config.getDefault(); 93 | // get schema 94 | const schema = await config.getSchema(); 95 | }, 96 | }; 97 | }); 98 | ``` 99 | 100 | If you are using `graphql-config` to define your configuration, and you wish to load your extenion config from it, do: 101 | 102 | ```ts 103 | type MyConfig = { ... }; 104 | 105 | const extensionConfig = await config.extension('my-plugin'); 106 | ``` 107 | 108 | ## Error Handling 109 | 110 | If you wish to fail the execution of your plugin and report it back to GraphQL CLI host, simply throw an error: 111 | 112 | ```ts 113 | import { defineCommand } from '@graphql-cli/common'; 114 | 115 | export default defineCommand(() => { 116 | return { 117 | command: 'check-if-missing', 118 | handler() { 119 | if (somethingIsMissing) { 120 | throw new Error(`Ooops, something is missing`); 121 | } 122 | }, 123 | }; 124 | }); 125 | ``` 126 | -------------------------------------------------------------------------------- /website/docs/serve.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: serve 3 | title: serve 4 | sidebar_label: serve 5 | --- 6 | 7 | Serves a full featured [GraphQL CRUD](https://graphqlcrud.org/) API with subscriptions and data synchronization running in just a few seconds without writing a single line of code - all you need is a data model `.graphql` file. 8 | 9 | GraphQL Serve is a CLI tool that leverages the power of Graphback to generate a codeless Node.js GraphQL API complete with schema and CRUD resolvers and an in-memory MongoDB database. 10 | 11 | ### Installation 12 | 13 | import Tabs from '@theme/Tabs'; 14 | import TabItem from '@theme/TabItem'; 15 | 16 | 23 | 24 | 25 | ``` 26 | yarn global add @graphql-cli/serve 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | ``` 34 | npm i -g @graphql-cli/serve 35 | ``` 36 | 37 | 38 | 39 | 40 | ### Usage 41 | 42 | The bare minimum you need is a GraphQL file with your data models. Create a file called `Note.graphql` and add the following: 43 | 44 | ```graphql 45 | """ @model """ 46 | type Note { 47 | _id: GraphbackObjectID! 48 | title: String! 49 | description: String 50 | likes: Int 51 | } 52 | 53 | scalar GraphbackObjectID 54 | ``` 55 | 56 | The `@model` annotation indicates that `Note` is a data model and Graphback will generate resolvers, a CRUD service and data source for it. You can learn how to build more complex data models in [Data Model](https://graphback.dev/docs/model/datamodel#model). 57 | 58 | #### Running your codeless GraphQL server 59 | 60 | To start your server, run the following command from the same directory as `Note.graphql`: 61 | 62 | ```bash 63 | graphql serve Note.graphql 64 | ``` 65 | 66 | This will start a GraphQL server on a random port using the `Note.graphql` data models we just added. 67 | 68 | You can customise the directory of the data models: 69 | 70 | ```bash 71 | graphql serve ./path/to/models 72 | ``` 73 | 74 | You can also specify where to load the data models from with a Glob pattern: 75 | 76 | ```bash 77 | graphql serve ./schema/**/*.graphql 78 | ``` 79 | 80 | You can specify which port to start the server on: 81 | 82 | ```bash 83 | $ graphql serve ./path/to/models --port 8080 84 | 85 | Starting server... 86 | 87 | Listening at: http://localhost:8080/graphql 88 | ``` 89 | 90 | ### Enable Data Synchronization 91 | 92 | GraphQL Serve can also operate on data sync models. Under the hood this uses the [Data Sync](https://graphback.dev/docs/datasync/intro) package. 93 | To enable data synchronization, all we need to do is enable datasync capabilities on our models via the `@datasync` annotation. 94 | 95 | For the `Note` model defined above, this would look like: 96 | 97 | ```graphql 98 | """ 99 | @model 100 | @datasync 101 | """ 102 | type Note { 103 | _id: GraphbackObjectID! 104 | title: String! 105 | description: String 106 | likes: Int 107 | } 108 | 109 | scalar GraphbackObjectID 110 | ``` 111 | 112 | Once we have a model with datasync capabilities, we can run our GraphQL server by enabling data synchronization as shown below: 113 | 114 | ```bash 115 | graphql serve Note.graphql --datasync 116 | ``` 117 | 118 | Conflict resolution strategies for datasync enabled models can be specified via the --conflict option: 119 | 120 | ```bash 121 | graphql serve Note.graphql --datasync --conflict=clientSideWins 122 | ``` 123 | 124 | This defaults to ClientSideWins, if unset. 125 | 126 | The TTL for delta tables, can also be set using the --deltaTTL option: 127 | 128 | ```bash 129 | graphql serve Note.graphql --datasync --deltaTTL=172800 130 | ``` 131 | 132 | This value defaults to `172800` when unused 133 | 134 | 135 | #### Arguments 136 | 137 | | argument | description | default | 138 | | --- | --- | --- | 139 | | `Model` | Directory to search for data models | `undefined` | 140 | 141 | #### Options 142 | 143 | | option | alias | description | default | 144 | | --- | --- | --- | --- | 145 | | `--port` | `-p` | Port on which to run the HTTP server | `Random port` | 146 | | `--datasync` | `--ds` | Enable datasynchronization features | `false` | 147 | | `--deltaTTL` | N/A | Specify a conflict resolution strategy with --datasync. Choices: `clientSideWins`, `serverSideWins`, `throwOnConflict` | `clientSideWins` | 148 | | `--conflict` | N/A | Specify a TTL for delta tables with --datasync | `172800` | 149 | 150 | 151 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) 19 | ); 20 | 21 | type Config = { 22 | onSuccess?: (registration: ServiceWorkerRegistration) => void; 23 | onUpdate?: (registration: ServiceWorkerRegistration) => void; 24 | }; 25 | 26 | export function register(config?: Config) { 27 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 28 | // The URL constructor is available in all browsers that support SW. 29 | const publicUrl = new URL((process as { env: { [key: string]: string } }).env.PUBLIC_URL, window.location.href); 30 | if (publicUrl.origin !== window.location.origin) { 31 | // Our service worker won't work if PUBLIC_URL is on a different origin 32 | // from what our page is served on. This might happen if a CDN is used to 33 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 34 | return; 35 | } 36 | 37 | window.addEventListener('load', () => { 38 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 39 | 40 | if (isLocalhost) { 41 | // This is running on localhost. Let's check if a service worker still exists or not. 42 | checkValidServiceWorker(swUrl, config); 43 | 44 | // Add some additional logging to localhost, pointing developers to the 45 | // service worker/PWA documentation. 46 | navigator.serviceWorker.ready.then(() => { 47 | console.log( 48 | 'This web app is being served cache-first by a service ' + 49 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 50 | ); 51 | }); 52 | } else { 53 | // Is not localhost. Just register service worker 54 | registerValidSW(swUrl, config); 55 | } 56 | }); 57 | } 58 | } 59 | 60 | function registerValidSW(swUrl: string, config?: Config) { 61 | navigator.serviceWorker 62 | .register(swUrl) 63 | .then((registration) => { 64 | registration.onupdatefound = () => { 65 | const installingWorker = registration.installing; 66 | if (installingWorker == null) { 67 | return; 68 | } 69 | installingWorker.onstatechange = () => { 70 | if (installingWorker.state === 'installed') { 71 | if (navigator.serviceWorker.controller) { 72 | // At this point, the updated precached content has been fetched, 73 | // but the previous service worker will still serve the older 74 | // content until all client tabs are closed. 75 | console.log( 76 | 'New content is available and will be used when all ' + 77 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 78 | ); 79 | 80 | // Execute callback 81 | if (config && config.onUpdate) { 82 | config.onUpdate(registration); 83 | } 84 | } else { 85 | // At this point, everything has been precached. 86 | // It's the perfect time to display a 87 | // "Content is cached for offline use." message. 88 | console.log('Content is cached for offline use.'); 89 | 90 | // Execute callback 91 | if (config && config.onSuccess) { 92 | config.onSuccess(registration); 93 | } 94 | } 95 | } 96 | }; 97 | }; 98 | }) 99 | .catch((error) => { 100 | console.error('Error during service worker registration:', error); 101 | }); 102 | } 103 | 104 | function checkValidServiceWorker(swUrl: string, config?: Config) { 105 | // Check if the service worker can be found. If it can't reload the page. 106 | fetch(swUrl) 107 | .then((response) => { 108 | // Ensure service worker exists, and that we really are getting a JS file. 109 | const contentType = response.headers.get('content-type'); 110 | if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then((registration) => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log('No internet connection found. App is running in offline mode.'); 124 | }); 125 | } 126 | 127 | export function unregister() { 128 | if ('serviceWorker' in navigator) { 129 | navigator.serviceWorker.ready.then((registration) => { 130 | registration.unregister(); 131 | }); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /packages/commands/init/src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineCommand } from '@graphql-cli/common'; 2 | import { prompt } from 'inquirer'; 3 | import { join } from 'path'; 4 | import chalk from 'chalk'; 5 | import { ensureFile, writeFileSync, readFileSync, existsSync } from 'fs-extra'; 6 | import { safeLoad as YAMLParse, safeDump as YAMLStringify } from 'js-yaml'; 7 | import { Context, InitializationType, ProjectType, askForEnum, managePackageManifest } from './common'; 8 | import { askForInspector } from './features/inspector'; 9 | import { askForCodegen } from './features/codegen'; 10 | import { fromExisting } from './sources/from-existing'; 11 | import { fromScratch } from './sources/from-scratch'; 12 | import { fromExistingOpenAPI } from './sources/from-open-api'; 13 | 14 | export default defineCommand< 15 | {}, 16 | { 17 | projectName?: string; 18 | templateName?: string; 19 | templateUrl?: string; 20 | } 21 | >(() => { 22 | return { 23 | command: 'init', 24 | builder(yargs) { 25 | return yargs.options({ 26 | projectName: { 27 | describe: 'Project name', 28 | type: 'string', 29 | }, 30 | templateName: { 31 | describe: 'Name of the predefined template', 32 | type: 'string', 33 | }, 34 | templateUrl: { 35 | describe: 'GitHub URL of the template. For example (http://github.com/example/graphql-cli-example-template)', 36 | type: 'string', 37 | }, 38 | }); 39 | }, 40 | async handler(args) { 41 | let { projectName, templateName, templateUrl } = args; 42 | const initializationType = await askForInitializationType(); 43 | 44 | const context: Context = { 45 | name: projectName, 46 | path: process.cwd(), 47 | graphqlConfig: { 48 | extensions: {}, 49 | }, 50 | }; 51 | 52 | const project = managePackageManifest(); 53 | 54 | project.addDependency('graphql-cli'); 55 | 56 | if (initializationType === InitializationType.ExistingGraphQL) { 57 | await fromExisting({ 58 | context, 59 | project, 60 | }); 61 | } else if (initializationType === InitializationType.FromScratch) { 62 | await fromScratch({ 63 | context, 64 | templateName, 65 | templateUrl, 66 | }); 67 | } 68 | 69 | if (initializationType !== InitializationType.FromScratch) { 70 | context.type = await askForProject(); 71 | } 72 | 73 | loadGraphQLConfig(context); 74 | 75 | if (initializationType === InitializationType.ExistingOpenAPI) { 76 | await fromExistingOpenAPI(context); 77 | } 78 | 79 | await askForSchema(context); 80 | await askForDocuments(context); 81 | await askForCodegen({ context, project }); 82 | await askForInspector({ context, project }); 83 | 84 | await Promise.all([ 85 | writeGraphQLConfig(context), 86 | project.writePackage({ 87 | path: context.path, 88 | name: projectName, 89 | initializationType, 90 | })]); 91 | 92 | const successMessages = [ 93 | `🚀 GraphQL CLI project successfully initialized:`, 94 | context.name, 95 | 'Next Steps:', 96 | `- Change directory to the project folder - ${chalk.cyan(`cd ${context.path}`)}`, 97 | `- Run ${chalk.cyan(`yarn install`)} to install dependencies`, 98 | ]; 99 | 100 | if (initializationType !== InitializationType.ExistingGraphQL) { 101 | successMessages.push( 102 | `- ${chalk.cyan(`(Optional)`)} Initialize your git repo. ${chalk.cyan(`git init`)}.`, 103 | `- Follow the instructions in README.md to continue...` 104 | ); 105 | } 106 | 107 | console.info(successMessages.join('\n')); 108 | process.exit(0); 109 | }, 110 | }; 111 | }); 112 | 113 | function askForProject() { 114 | return askForEnum({ 115 | enum: ProjectType, 116 | message: 'What is the type of the project?', 117 | defaultValue: ProjectType.FullStack, 118 | }); 119 | } 120 | 121 | function askForInitializationType() { 122 | return askForEnum({ 123 | enum: InitializationType, 124 | message: 'Select the best option for you', 125 | defaultValue: InitializationType.FromScratch, 126 | ignoreList: [], 127 | }); 128 | } 129 | 130 | function loadGraphQLConfig(context: Context) { 131 | const graphqlConfigPath = join(context.path, '.graphqlrc.yml'); 132 | 133 | try { 134 | if (existsSync(graphqlConfigPath)) { 135 | context.graphqlConfig = YAMLParse(readFileSync(graphqlConfigPath, 'utf8')); 136 | } 137 | } catch (e) { 138 | console.warn(`Existing GraphQL Config file looks broken! Skipping...`); 139 | } 140 | } 141 | 142 | async function askForSchema(context: Context) { 143 | if (!context.graphqlConfig.schema) { 144 | const { schema } = await prompt([ 145 | { 146 | type: 'input', 147 | name: 'schema', 148 | message: 'Where is your schema?', 149 | default: './schema.graphql', 150 | }, 151 | ]); 152 | 153 | context.graphqlConfig.schema = schema.endsWith('.ts') 154 | ? { 155 | [schema]: { 156 | require: 'ts-node/register', 157 | }, 158 | } 159 | : schema; 160 | } 161 | } 162 | 163 | async function askForDocuments(context: Context) { 164 | if ( 165 | !context.graphqlConfig.documents && 166 | (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) 167 | ) { 168 | const { documents } = await prompt([ 169 | { 170 | type: 'input', 171 | name: 'documents', 172 | message: 'Where are your operation documents?', 173 | }, 174 | ]); 175 | context.graphqlConfig.documents = documents; 176 | } 177 | } 178 | 179 | async function writeGraphQLConfig(context: Context) { 180 | const configPath = join(context.path, '.graphqlrc.yml'); 181 | await ensureFile(configPath); 182 | 183 | const keys = ['schema', 'documents', 'extensions']; 184 | 185 | function sortKeys(a: string, b: string) { 186 | const ai = keys.indexOf(a); 187 | const bi = keys.indexOf(b); 188 | 189 | if (ai === -1 && bi === -1) { 190 | return a.localeCompare(b); 191 | } 192 | 193 | return ai <= bi ? -1 : 1; 194 | } 195 | 196 | writeFileSync( 197 | configPath, 198 | YAMLStringify(context.graphqlConfig, { 199 | sortKeys, 200 | }) 201 | ); 202 | } 203 | -------------------------------------------------------------------------------- /website/algolia-lockfile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "objectID": "cli-codegen", 4 | "headings": [], 5 | "toc": [], 6 | "content": "-", 7 | "url": "https://www.graphql-cli.com/codegen", 8 | "domain": "https://www.graphql-cli.com/", 9 | "hierarchy": ["CLI", "codegen"], 10 | "source": "CLI", 11 | "title": "codegen", 12 | "type": "Documentation" 13 | }, 14 | { 15 | "objectID": "cli-coverage", 16 | "headings": [], 17 | "toc": [], 18 | "content": "-", 19 | "url": "https://www.graphql-cli.com/coverage", 20 | "domain": "https://www.graphql-cli.com/", 21 | "hierarchy": ["CLI", "coverage"], 22 | "source": "CLI", 23 | "title": "coverage", 24 | "type": "Documentation" 25 | }, 26 | { 27 | "objectID": "cli-custom-commands", 28 | "headings": [ 29 | "Writing your own commands", 30 | "Testing your plugin locally", 31 | "Loading GraphQL Schema", 32 | "Error Handling" 33 | ], 34 | "toc": [ 35 | { 36 | "children": [ 37 | { 38 | "children": [ 39 | { 40 | "children": [], 41 | "title": "Getting Started", 42 | "anchor": "getting-started" 43 | } 44 | ], 45 | "title": "TL;DR", 46 | "anchor": "tldr" 47 | } 48 | ], 49 | "title": "Writing your own commands", 50 | "anchor": "writing-your-own-commands" 51 | }, 52 | { 53 | "children": [], 54 | "title": "Testing your plugin locally", 55 | "anchor": "testing-your-plugin-locally" 56 | }, 57 | { 58 | "children": [], 59 | "title": "Loading GraphQL Schema", 60 | "anchor": "loading-graphql-schema" 61 | }, 62 | { 63 | "children": [], 64 | "title": "Error Handling", 65 | "anchor": "error-handling" 66 | } 67 | ], 68 | "content": "-", 69 | "url": "https://www.graphql-cli.com/custom-commands", 70 | "domain": "https://www.graphql-cli.com/", 71 | "hierarchy": ["CLI", "Custom commands"], 72 | "source": "CLI", 73 | "title": "Custom commands", 74 | "type": "Documentation" 75 | }, 76 | { 77 | "objectID": "cli-diff", 78 | "headings": [], 79 | "toc": [], 80 | "content": "-", 81 | "url": "https://www.graphql-cli.com/diff", 82 | "domain": "https://www.graphql-cli.com/", 83 | "hierarchy": ["CLI", "diff"], 84 | "source": "CLI", 85 | "title": "diff", 86 | "type": "Documentation" 87 | }, 88 | { 89 | "objectID": "cli-discover", 90 | "headings": [], 91 | "toc": [], 92 | "content": "-", 93 | "url": "https://www.graphql-cli.com/discover", 94 | "domain": "https://www.graphql-cli.com/", 95 | "hierarchy": ["CLI", "discover"], 96 | "source": "CLI", 97 | "title": "discover", 98 | "type": "Documentation" 99 | }, 100 | { 101 | "objectID": "cli-generate", 102 | "headings": [], 103 | "toc": [], 104 | "content": "-", 105 | "url": "https://www.graphql-cli.com/generate", 106 | "domain": "https://www.graphql-cli.com/", 107 | "hierarchy": ["CLI", "generate"], 108 | "source": "CLI", 109 | "title": "generate", 110 | "type": "Documentation" 111 | }, 112 | { 113 | "objectID": "cli-init", 114 | "headings": [], 115 | "toc": [], 116 | "content": "-", 117 | "url": "https://www.graphql-cli.com/init", 118 | "domain": "https://www.graphql-cli.com/", 119 | "hierarchy": ["CLI", "init"], 120 | "source": "CLI", 121 | "title": "init", 122 | "type": "Documentation" 123 | }, 124 | { 125 | "objectID": "cli-introduction", 126 | "headings": [], 127 | "toc": [], 128 | "content": "-", 129 | "url": "https://www.graphql-cli.com/introduction", 130 | "domain": "https://www.graphql-cli.com/", 131 | "hierarchy": ["CLI", "Introduction"], 132 | "source": "CLI", 133 | "title": "Introduction", 134 | "type": "Documentation" 135 | }, 136 | { 137 | "objectID": "cli-introspect", 138 | "headings": [], 139 | "toc": [], 140 | "content": "-", 141 | "url": "https://www.graphql-cli.com/introspect", 142 | "domain": "https://www.graphql-cli.com/", 143 | "hierarchy": ["CLI", "introspect"], 144 | "source": "CLI", 145 | "title": "introspect", 146 | "type": "Documentation" 147 | }, 148 | { 149 | "objectID": "cli-migration", 150 | "headings": ["Migration from GraphQL CLI 3.x or older"], 151 | "toc": [ 152 | { 153 | "children": [ 154 | { 155 | "children": [ 156 | { 157 | "children": [], 158 | "title": "Update your configuration file", 159 | "anchor": "update-your-configuration-file" 160 | }, 161 | { 162 | "children": [], 163 | "title": "Comparison of old commands", 164 | "anchor": "comparison-of-old-commands" 165 | }, 166 | { 167 | "children": [], 168 | "title": "Special Notes for Prisma users", 169 | "anchor": "special-notes-for-prisma-users" 170 | } 171 | ], 172 | "title": "Install the new version", 173 | "anchor": "install-the-new-version" 174 | } 175 | ], 176 | "title": "Migration from GraphQL CLI 3.x or older", 177 | "anchor": "migration-from-graphql-cli-3x-or-older" 178 | } 179 | ], 180 | "content": "-", 181 | "url": "https://www.graphql-cli.com/migration", 182 | "domain": "https://www.graphql-cli.com/", 183 | "hierarchy": ["CLI", "Migration"], 184 | "source": "CLI", 185 | "title": "Migration", 186 | "type": "Documentation" 187 | }, 188 | { 189 | "objectID": "cli-serve", 190 | "headings": [], 191 | "toc": [], 192 | "content": "-", 193 | "url": "https://www.graphql-cli.com/serve", 194 | "domain": "https://www.graphql-cli.com/", 195 | "hierarchy": ["CLI", "serve"], 196 | "source": "CLI", 197 | "title": "serve", 198 | "type": "Documentation" 199 | }, 200 | { 201 | "objectID": "cli-similar", 202 | "headings": [], 203 | "toc": [], 204 | "content": "-", 205 | "url": "https://www.graphql-cli.com/similar", 206 | "domain": "https://www.graphql-cli.com/", 207 | "hierarchy": ["CLI", "similar"], 208 | "source": "CLI", 209 | "title": "similar", 210 | "type": "Documentation" 211 | }, 212 | { 213 | "objectID": "cli-validate", 214 | "headings": [], 215 | "toc": [], 216 | "content": "-", 217 | "url": "https://www.graphql-cli.com/validate", 218 | "domain": "https://www.graphql-cli.com/", 219 | "hierarchy": ["CLI", "validate"], 220 | "source": "CLI", 221 | "title": "validate", 222 | "type": "Documentation" 223 | } 224 | ] 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL CLI 2 | 3 | ![image](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) 4 | 5 | ![CI](https://github.com/Urigo/graphql-cli/workflows/CI/badge.svg) 6 | [![npm version](http://img.shields.io/npm/v/graphql-cli.svg?style=flat)](https://npmjs.org/package/graphql-cli "View this project on npm") [![Discord Chat](https://img.shields.io/discord/625400653321076807)](https://the-guild.dev/discord) 7 | 8 | Help us to improve new GraphQL CLI. Check out the new structure and commands below! 9 | Feel free to contact us in Discord channel. We would love to hear your feedback. 10 | 11 | ## Features 12 | 13 | - Helpful commands to improve your workflows 14 | - Compatible with editors and IDEs based on [`graphql-config`](https://github.com/kamilkisiela/graphql-config) 15 | - Powerful plugin system to extend `graphql-cli` with custom commands 16 | 17 | ## Install 18 | 19 | You can install the CLI using `yarn` by running the following command. This will add the `graphql` binary to your path. 20 | 21 | ```sh 22 | yarn global add graphql-cli 23 | ``` 24 | 25 | The equivalent npm global install will also work. 26 | 27 | ## Migration from 3.x.x to 4.x.x 28 | 29 | **Important: many aspects of GraphQL CLI syntax and structure have changed in 4.x.x.** Please check out the [Migration Guide](./docs/MIGRATION.md) to learn more. 30 | 31 | ## Usage / Initialization 32 | 33 | At the heart of a project created using GraphQL CLI is the GraphQL Config configuration file. For starters, this configuration lets the cd CLI tools know where all of the GraphQL documents and operations are. For more information about GraphQL Config, [you can click here to learn more](https://graphql-config.com/docs/introduction). 34 | 35 | The most straightforward way to launch a GraphQL CLI-capable project with a working GraphQL Config setup is to use the `init` command from your desired workspace: 36 | 37 | ```sh 38 | npx graphql-cli init 39 | ``` 40 | 41 | After a series of questions from the command-prompt, the system will use the inputs and selected project templates to generate a working project complete with a GraphQL Config setup. The GraphQL Config file is generated referencing the necessary files and ecosystem plugins. 42 | 43 | You can also get started with GraphQL CLI by creating your own GraphQL Config file using an editor of your choice. Starting with a filename `.graphqlrc.yml`, for instance, we could add: 44 | 45 | ```yml 46 | schema: "server/src/schema/**/*.graphql" 47 | documents: "client/src/documents/**/*.graphql" 48 | ``` 49 | 50 | This is now a valid YAML-syntax GraphQL Config file. Using `init` from the GraphQL CLI will generate a project based on the instructions in your YAML. 51 | 52 | Finally, one of the options with `graphql init` is to access schema using an OpenAPI or Swagger endpoint. Choose this option at the start of the Init question tree, and then follow the instructions to navigate to the URL of your choice. 53 | 54 | ## Plugin System 55 | 56 | Each command in GraphQL CLI is a seperate package, so you can have your own plugins or use the ones we maintain. You can have those commands by installing them like `@graphql-cli/[COMMAND-NAME]`. 57 | 58 | To configure a command/plugin, you need to update the `extensions` field in your GraphQL Config file (`.graphqlrc.yml`). See `extensions:` in the example below. 59 | 60 | ```yml 61 | schema: 62 | ./server/src/schema/**/*.ts: 63 | require: ts-node/register 64 | documents: ./client/src/graphql/**/*.ts 65 | extensions: 66 | codegen: 67 | generates: 68 | ./server/src/generated-types.d.ts: 69 | plugins: 70 | - typescript 71 | - typescript-resolvers 72 | ./client/src/generated-types.tsx: 73 | plugins: 74 | - typescript 75 | - typescript-operations 76 | - typescript-react-apollo 77 | config: 78 | withHooks: true 79 | graphback: 80 | model: './model/*.graphql' 81 | plugins: 82 | graphback-schema: 83 | outputPath: './src/schema/schema.graphql' 84 | ... 85 | ``` 86 | 87 | [For a detailed example check out a template file here.](https://github.com/Urigo/graphql-cli/blob/master/templates/fullstack/.graphqlrc.yml) 88 | 89 | Some of the available Plugins are: 90 | 91 | - [`init`](https://github.com/Urigo/graphql-cli/tree/focs/packages/commands/init) - Creates a GraphQL project using a template or GraphQL Config file for your existing project. 92 | - [`codegen`](https://github.com/dotansimha/graphql-code-generator/tree/master/packages/graphql-cli-codegen-plugin) - GraphQL Code Generator's GraphQL CLI plugin. GraphQL Code Generator is a tool that generates code from your GraphQL schema and documents for your backend or frontend with flexible support for custom plugins and templates. [Learn More](https://graphql-code-generator.com) 93 | - [`generate`](https://github.com/Urigo/graphql-cli/tree/master/packages/commands/generate) - Generate schema and client-side documents for your GraphQL project by using [Graphback](https://graphback.dev). 94 | - [`coverage`](https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/coverage) - Schema coverage based on documents. Find out how many times types and fields are used in your application using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/coverage). 95 | - [`diff`](https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/diff) - Compares schemas and finds breaking or dangerous changes using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/diff). 96 | - You can also compare your current schema against a base schema using URL, Git link and local file. You can give this pointer in the command line after `graphql diff` or in GraphQL Config file: 97 | 98 | ```yml 99 | # ... 100 | extensions: 101 | diff: 102 | baseSchema: git:origin/master:schema.graphql 103 | ``` 104 | 105 | - [`similar`]((https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/similar)) - Get a list of similar types in order to find duplicates using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/similar). 106 | - [`validate`]((https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/validate)) - Validates documents against a schema and looks for deprecated usage using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/validate). 107 | - [`serve`](https://github.com/Urigo/graphql-cli/tree/master/packages/commands/serve) - Serves a GraphQL server, using an in memory database and a defined GraphQL schema. Please read through [serve documentation](./website/docs/command-serve.md) to learn more about this command. 108 | 109 | More plugins are definitely welcome! Please check the existing ones to see how to use GraphQL Config and GraphQL CLI API. 110 | 111 | ## Contributing 112 | 113 | Please read through the [contributing guidelines](./CONTRIBUTING.md) 114 | 115 | ## Writing your own plugin 116 | 117 | GraphQL CLI supports custom plugins, [you can find a tutorial and example here](./docs/CUSTOM_EXTENSION.md) 118 | 119 | ## Help & Community [![Discord Chat](https://img.shields.io/discord/625400653321076807)](https://the-guild.dev/discord) 120 | 121 | Join our [Discord chat](https://the-guild.dev/discord) if you run into issues or have questions. We're excited to welcome you to the community! 122 | -------------------------------------------------------------------------------- /docs/MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migration from GraphQL CLI 3.x or older 2 | 3 | Starting with GraphQL CLI 4.0 and higher, the way projects are set up is significantly restructured. 4 | 5 | ## Install the new version 6 | To get started, install the new version: 7 | ```sh 8 | yarn global add graphql-cli 9 | ``` 10 | You can also globally install using npm. 11 | 12 | > NOTE: If you have previous version of the GraphQL-CLI installed make sure to uninstall it first 13 | 14 | ```bash 15 | npm uninstall graphql-cli 16 | ``` 17 | 18 | ## Update your configuration file 19 | If you are working from an existing project, the GraphQL Config file that is used by GraphQL CLI is now called `.graphqlrc.yml` (by default) instead of `.graphqlconfig`. [Other options exist](https://graphql-config.com/usage) for naming the config files supported by GraphQL CLI, but this guide will assume you're using YAML syntax. 20 | 21 | To migrate, you will first need to update your GraphQL Configuration file to match GraphQL Config's updated syntax and structure. 22 | 23 | You can [check here](https://graphql-config.com/usage) for more information about the new structure. 24 | 25 | ###Specifying schema(s): 26 | 27 | ```yml 28 | schema: ./src/schema/**/*.graphql #You can have URL endpoint, Git URL and local files using globs here. 29 | ``` 30 | 31 | `schemaPath` is replaced by `schema`, which is now more flexible then the previous approach. This field is used by all commands and plugins of GraphQL CLI. 32 | 33 | ## Comparison of old commands 34 | 35 | ### `get-schema` is no longer available 36 | In previous versions, you were able to download the schema to the given path in `schemaPath` from the URL given inside `endpoint`. In the new version, `schema` refers to the endpoint of the schema. 37 | 38 | If you use Prisma or any other tool that provides your schema under URL endpoint, you must specify it using the following syntax in your configuration YAML: 39 | 40 | ```yaml 41 | schema: http://localhost:4000/graphql #This is the schema path 42 | ``` 43 | 44 | If you want to download the schema from this URL to your local file system, you will also need to install `codegen` plugin and its `schema-ast` plugin using the following command or its npm equivalent: 45 | 46 | ```bash 47 | yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev 48 | ``` 49 | 50 | After that, you can specify the output path of the local schema file: 51 | 52 | ```yaml 53 | schema: http://localhost:4000/graphql 54 | extensions: 55 | codegen: 56 | generates: 57 | ./schema.graphql: 58 | plugins: 59 | - schema-ast 60 | ``` 61 | 62 | By running `graphql codegen`, the `schema.graphql` file is generated in the root path of your project. 63 | 64 | #### For JSON Output 65 | If you want to download the schema as a `json` introspection file, you will need to install `@graphql-codegen/introspection` instead, and add `introspection` instead of `schema-ast`. 66 | 67 | ```yaml 68 | schema: http://localhost:4000/graphql 69 | extensions: 70 | codegen: 71 | generates: 72 | ./schema.json: 73 | plugins: 74 | - introspection 75 | ``` 76 | 77 | ### `create` is no longer available: it is replaced by the `init` command. 78 | If you want to create a GraphQL Config file on an existing project or create a project using a template from scratch, you can use `graphql init` command. 79 | This command will ask some questions about your new or existing project to update your dependencies and create a new configuration file for GraphQL CLI. 80 | 81 | ### `diff` has been changed 82 | If you want to see the differences between your schema and another schema, use the `diff` command as follows: 83 | ```yaml 84 | graphql diff git:origin/master:schema.graphql 85 | ``` 86 | For example, `diff` will show the differences between the version in `schema` field of GraphQL Configuration file and the `schema.graphql` file in the remote master branch. 87 | 88 | Alternatively, you can compare `schema` with a URL endpoint: 89 | ```yaml 90 | graphql diff http://my-dev-instance.com/graphql 91 | ``` 92 | 93 | ### `add-endpoint`, `add-project`, `schema-status`, `ping`, `query`, `prepare`, `lint` and `playground` commands are no longer available. 94 | GraphQL CLI (as well as GraphQL Config) no longer separates `endpoints` and `schemaPath`. The new `schema` field refers to the single endpoint of your GraphQL schema, so it can be a URL endpoint or a local file. If your project uses a remote schema, you can directly define this URL in `schema` path without downloading it or defining it as an extra `endpoint` etc. 95 | 96 | Instead of using these legacy commands, you can create a faked server to test your schema using the `yarn serve` command. 97 | 98 | ### `codegen` now uses GraphQL Code Generator 99 | GraphQL CLI now uses GraphQL Code Generator which has a lot of plugins and templates for various environments, platforms and use cases. You can generate resolver signatures, TypeScript representations of your GraphQL Schema and more. [Check it out](https://graphql-code-generator.com/) 100 | 101 | The usage is slightly different from the old one: 102 | ```yaml 103 | schema: src/schema/**/*.graphql 104 | extensions: 105 | codegen: 106 | src/generated-types.ts: # Output file name 107 | - typescript # Plugin names to be used 108 | - typescript-resolvers 109 | ``` 110 | 111 | For instance, consider a hypothetical case where you need to generate TypeScript resolvers signatures for your GraphQL project. To do this, you would install the `codegen` plugin and the additional plugins and templates for GraphQL Code Generator. For this case, you would need `typescript` and `typescript-resolvers` plugins: 112 | 113 | ```bash 114 | yarn add @graphql-cli/codegen @graphql-codegen/typescript @graphql-codegen/typescript-resolvers --dev 115 | ``` 116 | 117 | Now, using a single command, you can run GraphQL Code Generator using GraphQL CLI: 118 | ```bash 119 | graphql codegen 120 | ``` 121 | 122 | ## Special Notes for Prisma users 123 | Prisma users will need to download a schema from a URL endpoint. For example, here is a *legacy GraphQL Config file* doing this: 124 | 125 | `.graphqlconfig` 126 | ```yaml 127 | projects: 128 | app: 129 | schemaPath: src/schema.graphql 130 | extensions: 131 | endpoints: 132 | default: http://localhost:4000 133 | database: 134 | schemaPath: src/generated/prisma-client/prisma.graphql 135 | extensions: 136 | prisma: prisma/prisma.yml 137 | ``` 138 | 139 | **To update the configuration for the new GraphQL CLI** you need to rename the file to `.graphqlrc.yml`, and then update the file as follows: 140 | 141 | `.graphqlrc.yml` 142 | ```yaml 143 | projects: 144 | app: 145 | schema: src/schema.graphql 146 | database: 147 | schema: prisma/prisma.yml 148 | extensions: 149 | codegen: 150 | generates: 151 | ./src/generated/prisma-client/prisma.graphql: 152 | plugins: 153 | - schema-ast 154 | ``` 155 | 156 | You can directly point to your `prisma.yml` file instead of the URL endpoint. 157 | 158 | Before running the GraphQL CLI command to use this new configuration, make sure you have installed the `@graphql-cli/codegen` and `@graphql-codegen/schema-ast` plugins using: 159 | ```sh 160 | yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev 161 | ``` 162 | 163 | Now you can run `graphql codegen --project database` for generating your `prisma.graphql` file. 164 | 165 | You will also need to update your `prisma.yml` file if you're using `graphql get-schema` with Prisma: 166 | ```yaml 167 | ... 168 | # Ensures Prisma client is re-generated after a datamodel change. 169 | hooks: 170 | post-deploy: 171 | - graphql codegen --project database # instead of graphql get-schema 172 | - prisma generate 173 | ``` 174 | -------------------------------------------------------------------------------- /website/docs/migration.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: migration 3 | title: Migration 4 | sidebar_label: Migration 5 | --- 6 | 7 | ## Migration from GraphQL CLI 3.x or older 8 | 9 | Starting with GraphQL CLI 4.0 and higher, the way projects are set up is significantly restructured. 10 | 11 | ### Install the new version 12 | To get started, install the new version: 13 | ```sh 14 | yarn global add graphql-cli 15 | ``` 16 | You can also globally install using npm. 17 | 18 | > NOTE: If you have previous version of the GraphQL-CLI installed make sure to uninstall it first 19 | 20 | ```bash 21 | npm uninstall graphql-cli 22 | ``` 23 | 24 | ### Update your configuration file 25 | If you are working from an existing project, the GraphQL Config file that is used by GraphQL CLI is now called `.graphqlrc.yml` (by default) instead of `.graphqlconfig`. [Other options exist](https://graphql-config.com/usage) for naming the config files supported by GraphQL CLI, but this guide will assume you're using YAML syntax. 26 | 27 | To migrate, you will first need to update your GraphQL Configuration file to match GraphQL Config's updated syntax and structure. 28 | 29 | You can [check here](https://graphql-config.com/usage) for more information about the new structure. 30 | 31 | ####Specifying schema(s): 32 | 33 | ```yml 34 | schema: ./src/schema/**/*.graphql #You can have URL endpoint, Git URL and local files using globs here. 35 | ``` 36 | 37 | `schemaPath` is replaced by `schema`, which is now more flexible then the previous approach. This field is used by all commands and plugins of GraphQL CLI. 38 | 39 | ### Comparison of old commands 40 | 41 | #### `get-schema` is no longer available 42 | In previous versions, you were able to download the schema to the given path in `schemaPath` from the URL given inside `endpoint`. In the new version, `schema` refers to the endpoint of the schema. 43 | 44 | If you use Prisma or any other tool that provides your schema under URL endpoint, you must to specify it using the following syntax in your configuration YAML: 45 | 46 | ```yaml 47 | schema: http://localhost:4000/graphql #This is the schema path 48 | ``` 49 | 50 | If you want to download the schema from this URL to your local file system, you will also need to install `codegen` plugin and its `schema-ast` plugin using the following command or its npm equivalent: 51 | 52 | ```bash 53 | yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev 54 | ``` 55 | 56 | After that, you can specify the output path of the local schema file: 57 | 58 | ```yaml 59 | schema: http://localhost:4000/graphql 60 | extensions: 61 | codegen: 62 | generates: 63 | ./schema.graphql: 64 | plugins: 65 | - schema-ast 66 | ``` 67 | 68 | By running `graphql codegen`, the `schema.graphql` file is generated in the root path of your project. 69 | 70 | ##### For JSON Output 71 | If you want to download the schema as a `json` introspection file, you will need to install `@graphql-codegen/introspection` instead, and add `introspection` instead of `schema-ast`. 72 | 73 | ```yaml 74 | schema: http://localhost:4000/graphql 75 | extensions: 76 | codegen: 77 | generates: 78 | ./schema.json: 79 | plugins: 80 | - introspection 81 | ``` 82 | 83 | #### `create` is no longer available: it is replaced by the `init` command. 84 | If you want to create a GraphQL Config file on an existing project or create a project using a template from scratch, you can use `graphql init` command. 85 | This command will ask some questions about your new or existing project to update your dependencies and create a new configuration file for GraphQL CLI. 86 | 87 | #### `diff` has been changed 88 | If you want to see the differences between your schema and another schema, use the `diff` command as follows: 89 | ```yaml 90 | graphql diff git:origin/master:schema.graphql 91 | ``` 92 | For example, `diff` will show the differences between the version in `schema` field of GraphQL Configuration file and the `schema.graphql` file in the remote master branch. 93 | 94 | Alternatively, you can compare `schema` with a URL endpoint: 95 | ```yaml 96 | graphql diff http://my-dev-instance.com/graphql 97 | ``` 98 | 99 | #### `add-endpoint`, `add-project`, `schema-status`, `ping`, `query`, `prepare`, `lint` and `playground` commands are no longer available. 100 | GraphQL CLI (as well as GraphQL Config) no longer separates `endpoints` and `schemaPath`. The new `schema` field refers to the single endpoint of your GraphQL schema, so it can be a URL endpoint or a local file. If your project uses a remote schema, you can directly define this URL in `schema` path without downloading it or defining it as an extra `endpoint` etc. 101 | 102 | Instead of using these legacy commands, you can create a faked server to test your schema using the `yarn serve` command. 103 | 104 | #### `codegen` now uses GraphQL Code Generator 105 | GraphQL CLI now uses GraphQL Code Generator which has a lot of plugins and templates for various environments, platforms and use cases. You can generate resolver signatures, TypeScript representations of your GraphQL Schema and more. [Check it out](https://graphql-code-generator.com/) 106 | 107 | The usage is slightly different from the old one: 108 | ```yaml 109 | schema: src/schema/**/*.graphql 110 | extensions: 111 | codegen: 112 | src/generated-types.ts: # Output file name 113 | - typescript # Plugin names to be used 114 | - typescript-resolvers 115 | ``` 116 | 117 | For instance, consider a hypothetical case where you need to generate TypeScript resolvers signatures for your GraphQL project. To do this, you would install the `codegen` plugin and the additional plugins and templates for GraphQL Code Generator. For this case, you would need `typescript` and `typescript-resolvers` plugins: 118 | 119 | ```bash 120 | yarn add @graphql-cli/codegen @graphql-codegen/typescript @graphql-codegen/typescript-resolvers --dev 121 | ``` 122 | 123 | Now, using a single command, you can run GraphQL Code Generator using GraphQL CLI: 124 | ```bash 125 | graphql codegen 126 | ``` 127 | 128 | ### Special Notes for Prisma users 129 | Prisma users will need to download a schema from a URL endpoint. For example, here is a *legacy GraphQL Config file* doing this: 130 | 131 | `.graphqlconfig` 132 | ```yaml 133 | projects: 134 | app: 135 | schemaPath: src/schema.graphql 136 | extensions: 137 | endpoints: 138 | default: http://localhost:4000 139 | database: 140 | schemaPath: src/generated/prisma-client/prisma.graphql 141 | extensions: 142 | prisma: prisma/prisma.yml 143 | ``` 144 | 145 | **To update the configuration for the new GraphQL CLI** you need to rename the file to `.graphqlrc.yml`, and then update the file as follows: 146 | 147 | `.graphqlrc.yml` 148 | ```yaml 149 | projects: 150 | app: 151 | schema: src/schema.graphql 152 | database: 153 | schema: prisma/prisma.yml 154 | extensions: 155 | codegen: 156 | generates: 157 | ./src/generated/prisma-client/prisma.graphql: 158 | plugins: 159 | - schema-ast 160 | ``` 161 | 162 | You can directly point to your `prisma.yml` file instead of the URL endpoint. 163 | 164 | Before running the GraphQL CLI command to use this new configuration, make sure you have installed the `@graphql-cli/codegen` and `@graphql-codegen/schema-ast` plugins using: 165 | ```sh 166 | yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev 167 | ``` 168 | 169 | Now you can run `graphql codegen --project database` for generating your `prisma.graphql` file. 170 | 171 | You will also need to update your `prisma.yml` file if you're using `graphql get-schema` with Prisma: 172 | ```yaml 173 | ... 174 | ## Ensures Prisma client is re-generated after a datamodel change. 175 | hooks: 176 | post-deploy: 177 | - graphql codegen --project database # instead of graphql get-schema 178 | - prisma generate 179 | ``` 180 | -------------------------------------------------------------------------------- /templates/fullstack/client/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/fullstack/server/src/generated-types.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { GraphQLResolveInfo } from 'graphql'; 3 | import { comment, note } from './generated-db-types'; 4 | import { GraphbackRuntimeContext } from '@graphback/runtime'; 5 | export type Maybe = T | null; 6 | export type Exact = { [K in keyof T]: T[K] }; 7 | export type Omit = Pick>; 8 | export type RequireFields = { [X in Exclude]?: T[X] } & { [P in K]-?: NonNullable }; 9 | 10 | /** All built-in and custom scalars, mapped to their actual values */ 11 | export type Scalars = { 12 | ID: string; 13 | String: string; 14 | Boolean: boolean; 15 | Int: number; 16 | Float: number; 17 | }; 18 | 19 | /** @model */ 20 | export type Comment = { 21 | __typename?: 'Comment'; 22 | id: Scalars['ID']; 23 | text?: Maybe; 24 | description?: Maybe; 25 | /** @manyToOne(field: 'comments', key: 'noteId') */ 26 | note?: Maybe; 27 | }; 28 | 29 | export type CommentFilter = { 30 | id?: Maybe; 31 | text?: Maybe; 32 | description?: Maybe; 33 | noteId?: Maybe; 34 | and?: Maybe>; 35 | or?: Maybe>; 36 | not?: Maybe; 37 | }; 38 | 39 | export type CommentResultList = { 40 | __typename?: 'CommentResultList'; 41 | items: Array>; 42 | offset?: Maybe; 43 | limit?: Maybe; 44 | count?: Maybe; 45 | }; 46 | 47 | export type CommentSubscriptionFilter = { 48 | id?: Maybe; 49 | text?: Maybe; 50 | description?: Maybe; 51 | }; 52 | 53 | export type CreateCommentInput = { 54 | id?: Maybe; 55 | text?: Maybe; 56 | description?: Maybe; 57 | noteId?: Maybe; 58 | }; 59 | 60 | export type CreateNoteInput = { 61 | id?: Maybe; 62 | title: Scalars['String']; 63 | description?: Maybe; 64 | }; 65 | 66 | export type IdInput = { 67 | ne?: Maybe; 68 | eq?: Maybe; 69 | le?: Maybe; 70 | lt?: Maybe; 71 | ge?: Maybe; 72 | gt?: Maybe; 73 | in?: Maybe>; 74 | }; 75 | 76 | export type MutateCommentInput = { 77 | id: Scalars['ID']; 78 | text?: Maybe; 79 | description?: Maybe; 80 | noteId?: Maybe; 81 | }; 82 | 83 | export type MutateNoteInput = { 84 | id: Scalars['ID']; 85 | title?: Maybe; 86 | description?: Maybe; 87 | }; 88 | 89 | export type Mutation = { 90 | __typename?: 'Mutation'; 91 | createNote?: Maybe; 92 | updateNote?: Maybe; 93 | deleteNote?: Maybe; 94 | createComment?: Maybe; 95 | updateComment?: Maybe; 96 | deleteComment?: Maybe; 97 | }; 98 | 99 | 100 | export type MutationCreateNoteArgs = { 101 | input: CreateNoteInput; 102 | }; 103 | 104 | 105 | export type MutationUpdateNoteArgs = { 106 | input: MutateNoteInput; 107 | }; 108 | 109 | 110 | export type MutationDeleteNoteArgs = { 111 | input: MutateNoteInput; 112 | }; 113 | 114 | 115 | export type MutationCreateCommentArgs = { 116 | input: CreateCommentInput; 117 | }; 118 | 119 | 120 | export type MutationUpdateCommentArgs = { 121 | input: MutateCommentInput; 122 | }; 123 | 124 | 125 | export type MutationDeleteCommentArgs = { 126 | input: MutateCommentInput; 127 | }; 128 | 129 | /** @model */ 130 | export type Note = { 131 | __typename?: 'Note'; 132 | id: Scalars['ID']; 133 | title: Scalars['String']; 134 | description?: Maybe; 135 | /** @oneToMany(field: 'note', key: 'noteId') */ 136 | comments: Array>; 137 | }; 138 | 139 | 140 | /** @model */ 141 | export type NoteCommentsArgs = { 142 | filter?: Maybe; 143 | }; 144 | 145 | export type NoteFilter = { 146 | id?: Maybe; 147 | title?: Maybe; 148 | description?: Maybe; 149 | and?: Maybe>; 150 | or?: Maybe>; 151 | not?: Maybe; 152 | }; 153 | 154 | export type NoteResultList = { 155 | __typename?: 'NoteResultList'; 156 | items: Array>; 157 | offset?: Maybe; 158 | limit?: Maybe; 159 | count?: Maybe; 160 | }; 161 | 162 | export type NoteSubscriptionFilter = { 163 | id?: Maybe; 164 | title?: Maybe; 165 | description?: Maybe; 166 | }; 167 | 168 | export type OrderByInput = { 169 | field: Scalars['String']; 170 | order?: Maybe; 171 | }; 172 | 173 | export type PageRequest = { 174 | limit?: Maybe; 175 | offset?: Maybe; 176 | }; 177 | 178 | export type Query = { 179 | __typename?: 'Query'; 180 | getNote?: Maybe; 181 | findNotes: NoteResultList; 182 | getComment?: Maybe; 183 | findComments: CommentResultList; 184 | }; 185 | 186 | 187 | export type QueryGetNoteArgs = { 188 | id: Scalars['ID']; 189 | }; 190 | 191 | 192 | export type QueryFindNotesArgs = { 193 | filter?: Maybe; 194 | page?: Maybe; 195 | orderBy?: Maybe; 196 | }; 197 | 198 | 199 | export type QueryGetCommentArgs = { 200 | id: Scalars['ID']; 201 | }; 202 | 203 | 204 | export type QueryFindCommentsArgs = { 205 | filter?: Maybe; 206 | page?: Maybe; 207 | orderBy?: Maybe; 208 | }; 209 | 210 | export enum SortDirectionEnum { 211 | Desc = 'DESC', 212 | Asc = 'ASC' 213 | } 214 | 215 | export type StringInput = { 216 | ne?: Maybe; 217 | eq?: Maybe; 218 | le?: Maybe; 219 | lt?: Maybe; 220 | ge?: Maybe; 221 | gt?: Maybe; 222 | in?: Maybe>; 223 | contains?: Maybe; 224 | startsWith?: Maybe; 225 | endsWith?: Maybe; 226 | }; 227 | 228 | export type Subscription = { 229 | __typename?: 'Subscription'; 230 | newNote: Note; 231 | updatedNote: Note; 232 | deletedNote: Note; 233 | newComment: Comment; 234 | updatedComment: Comment; 235 | deletedComment: Comment; 236 | }; 237 | 238 | 239 | export type SubscriptionNewNoteArgs = { 240 | filter?: Maybe; 241 | }; 242 | 243 | 244 | export type SubscriptionUpdatedNoteArgs = { 245 | filter?: Maybe; 246 | }; 247 | 248 | 249 | export type SubscriptionDeletedNoteArgs = { 250 | filter?: Maybe; 251 | }; 252 | 253 | 254 | export type SubscriptionNewCommentArgs = { 255 | filter?: Maybe; 256 | }; 257 | 258 | 259 | export type SubscriptionUpdatedCommentArgs = { 260 | filter?: Maybe; 261 | }; 262 | 263 | 264 | export type SubscriptionDeletedCommentArgs = { 265 | filter?: Maybe; 266 | }; 267 | 268 | export type WithIndex = TObject & Record; 269 | export type ResolversObject = WithIndex; 270 | 271 | export type ResolverTypeWrapper = Promise | T; 272 | 273 | 274 | export type LegacyStitchingResolver = { 275 | fragment: string; 276 | resolve: ResolverFn; 277 | }; 278 | 279 | export type NewStitchingResolver = { 280 | selectionSet: string; 281 | resolve: ResolverFn; 282 | }; 283 | export type StitchingResolver = LegacyStitchingResolver | NewStitchingResolver; 284 | export type Resolver = 285 | | ResolverFn 286 | | StitchingResolver; 287 | 288 | export type ResolverFn = ( 289 | parent: TParent, 290 | args: TArgs, 291 | context: TContext, 292 | info: GraphQLResolveInfo 293 | ) => Promise | TResult; 294 | 295 | export type SubscriptionSubscribeFn = ( 296 | parent: TParent, 297 | args: TArgs, 298 | context: TContext, 299 | info: GraphQLResolveInfo 300 | ) => AsyncIterator | Promise>; 301 | 302 | export type SubscriptionResolveFn = ( 303 | parent: TParent, 304 | args: TArgs, 305 | context: TContext, 306 | info: GraphQLResolveInfo 307 | ) => TResult | Promise; 308 | 309 | export interface SubscriptionSubscriberObject { 310 | subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; 311 | resolve?: SubscriptionResolveFn; 312 | } 313 | 314 | export interface SubscriptionResolverObject { 315 | subscribe: SubscriptionSubscribeFn; 316 | resolve: SubscriptionResolveFn; 317 | } 318 | 319 | export type SubscriptionObject = 320 | | SubscriptionSubscriberObject 321 | | SubscriptionResolverObject; 322 | 323 | export type SubscriptionResolver = 324 | | ((...args: any[]) => SubscriptionObject) 325 | | SubscriptionObject; 326 | 327 | export type TypeResolveFn = ( 328 | parent: TParent, 329 | context: TContext, 330 | info: GraphQLResolveInfo 331 | ) => Maybe | Promise>; 332 | 333 | export type IsTypeOfResolverFn = (obj: T, info: GraphQLResolveInfo) => boolean | Promise; 334 | 335 | export type NextResolverFn = () => Promise; 336 | 337 | export type DirectiveResolverFn = ( 338 | next: NextResolverFn, 339 | parent: TParent, 340 | args: TArgs, 341 | context: TContext, 342 | info: GraphQLResolveInfo 343 | ) => TResult | Promise; 344 | 345 | /** Mapping between all available schema types and the resolvers types */ 346 | export type ResolversTypes = ResolversObject<{ 347 | Comment: ResolverTypeWrapper; 348 | ID: ResolverTypeWrapper; 349 | String: ResolverTypeWrapper; 350 | CommentFilter: CommentFilter; 351 | CommentResultList: ResolverTypeWrapper & { items: Array> }>; 352 | Int: ResolverTypeWrapper; 353 | CommentSubscriptionFilter: CommentSubscriptionFilter; 354 | CreateCommentInput: CreateCommentInput; 355 | CreateNoteInput: CreateNoteInput; 356 | IDInput: IdInput; 357 | MutateCommentInput: MutateCommentInput; 358 | MutateNoteInput: MutateNoteInput; 359 | Mutation: ResolverTypeWrapper<{}>; 360 | Note: ResolverTypeWrapper; 361 | NoteFilter: NoteFilter; 362 | NoteResultList: ResolverTypeWrapper & { items: Array> }>; 363 | NoteSubscriptionFilter: NoteSubscriptionFilter; 364 | OrderByInput: OrderByInput; 365 | PageRequest: PageRequest; 366 | Query: ResolverTypeWrapper<{}>; 367 | SortDirectionEnum: SortDirectionEnum; 368 | StringInput: StringInput; 369 | Subscription: ResolverTypeWrapper<{}>; 370 | Boolean: ResolverTypeWrapper; 371 | }>; 372 | 373 | /** Mapping between all available schema types and the resolvers parents */ 374 | export type ResolversParentTypes = ResolversObject<{ 375 | Comment: comment; 376 | ID: Scalars['ID']; 377 | String: Scalars['String']; 378 | CommentFilter: CommentFilter; 379 | CommentResultList: Omit & { items: Array> }; 380 | Int: Scalars['Int']; 381 | CommentSubscriptionFilter: CommentSubscriptionFilter; 382 | CreateCommentInput: CreateCommentInput; 383 | CreateNoteInput: CreateNoteInput; 384 | IDInput: IdInput; 385 | MutateCommentInput: MutateCommentInput; 386 | MutateNoteInput: MutateNoteInput; 387 | Mutation: {}; 388 | Note: note; 389 | NoteFilter: NoteFilter; 390 | NoteResultList: Omit & { items: Array> }; 391 | NoteSubscriptionFilter: NoteSubscriptionFilter; 392 | OrderByInput: OrderByInput; 393 | PageRequest: PageRequest; 394 | Query: {}; 395 | StringInput: StringInput; 396 | Subscription: {}; 397 | Boolean: Scalars['Boolean']; 398 | }>; 399 | 400 | export type CommentResolvers = ResolversObject<{ 401 | id?: Resolver; 402 | text?: Resolver, ParentType, ContextType>; 403 | description?: Resolver, ParentType, ContextType>; 404 | note?: Resolver, ParentType, ContextType>; 405 | __isTypeOf?: IsTypeOfResolverFn; 406 | }>; 407 | 408 | export type CommentResultListResolvers = ResolversObject<{ 409 | items?: Resolver>, ParentType, ContextType>; 410 | offset?: Resolver, ParentType, ContextType>; 411 | limit?: Resolver, ParentType, ContextType>; 412 | count?: Resolver, ParentType, ContextType>; 413 | __isTypeOf?: IsTypeOfResolverFn; 414 | }>; 415 | 416 | export type MutationResolvers = ResolversObject<{ 417 | createNote?: Resolver, ParentType, ContextType, RequireFields>; 418 | updateNote?: Resolver, ParentType, ContextType, RequireFields>; 419 | deleteNote?: Resolver, ParentType, ContextType, RequireFields>; 420 | createComment?: Resolver, ParentType, ContextType, RequireFields>; 421 | updateComment?: Resolver, ParentType, ContextType, RequireFields>; 422 | deleteComment?: Resolver, ParentType, ContextType, RequireFields>; 423 | }>; 424 | 425 | export type NoteResolvers = ResolversObject<{ 426 | id?: Resolver; 427 | title?: Resolver; 428 | description?: Resolver, ParentType, ContextType>; 429 | comments?: Resolver>, ParentType, ContextType, RequireFields>; 430 | __isTypeOf?: IsTypeOfResolverFn; 431 | }>; 432 | 433 | export type NoteResultListResolvers = ResolversObject<{ 434 | items?: Resolver>, ParentType, ContextType>; 435 | offset?: Resolver, ParentType, ContextType>; 436 | limit?: Resolver, ParentType, ContextType>; 437 | count?: Resolver, ParentType, ContextType>; 438 | __isTypeOf?: IsTypeOfResolverFn; 439 | }>; 440 | 441 | export type QueryResolvers = ResolversObject<{ 442 | getNote?: Resolver, ParentType, ContextType, RequireFields>; 443 | findNotes?: Resolver>; 444 | getComment?: Resolver, ParentType, ContextType, RequireFields>; 445 | findComments?: Resolver>; 446 | }>; 447 | 448 | export type SubscriptionResolvers = ResolversObject<{ 449 | newNote?: SubscriptionResolver>; 450 | updatedNote?: SubscriptionResolver>; 451 | deletedNote?: SubscriptionResolver>; 452 | newComment?: SubscriptionResolver>; 453 | updatedComment?: SubscriptionResolver>; 454 | deletedComment?: SubscriptionResolver>; 455 | }>; 456 | 457 | export type Resolvers = ResolversObject<{ 458 | Comment?: CommentResolvers; 459 | CommentResultList?: CommentResultListResolvers; 460 | Mutation?: MutationResolvers; 461 | Note?: NoteResolvers; 462 | NoteResultList?: NoteResultListResolvers; 463 | Query?: QueryResolvers; 464 | Subscription?: SubscriptionResolvers; 465 | }>; 466 | 467 | 468 | /** 469 | * @deprecated 470 | * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. 471 | */ 472 | export type IResolvers = Resolvers; 473 | --------------------------------------------------------------------------------