├── examples
├── exampleWithReactClient
│ ├── .gitignore
│ ├── .babelrc
│ ├── dist
│ │ └── index.html
│ ├── src
│ │ ├── index.html
│ │ └── app.js
│ ├── package.json
│ └── index.js
├── logo.png
├── exampleWithRouter
│ ├── index.js
│ └── router.js
├── exampleWithSubscription.js
├── example.js
├── exampleWithAuth.js
└── exampleWithRelations.js
├── .gitignore
├── package.json
├── ops
├── CONTRIBUTING.md
├── __tests__
└── index.js
├── index.js
└── README.md
/examples/exampleWithReactClient/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .cache
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | package-lock.json
4 | _production-test
--------------------------------------------------------------------------------
/examples/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tylerthebuildor/graphqless/HEAD/examples/logo.png
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "plugins": ["@babel/plugin-proposal-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/exampleWithRouter/index.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess } = require('../../index.js');
2 | const app = new GraphQLess();
3 | const router = require('./router');
4 |
5 | app.use(router);
6 |
7 | app.listen(3000, () => {
8 | console.log('Visit: http://localhost:3000/graphql');
9 | });
10 |
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/dist/index.html:
--------------------------------------------------------------------------------
1 |
React Example
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React Example
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "graphqless",
3 | "version": "1.3.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "files": [
7 | "index.js",
8 | "CONTRIBUTING.md"
9 | ],
10 | "scripts": {
11 | "test": "node ./node_modules/jest/bin/jest.js"
12 | },
13 | "dependencies": {
14 | "apollo-server-express": "^2.4.8",
15 | "express": "^4.16.4",
16 | "graphql": "^14.1.1",
17 | "merge-graphql-schemas": "^1.5.8"
18 | },
19 | "devDependencies": {
20 | "jest": "^24.3.1",
21 | "jsonwebtoken": "^8.5.0",
22 | "nodemon": "^1.18.10"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/exampleWithSubscription.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess, ApolloServerExpress } = require('../index.js');
2 | const { PubSub } = ApolloServerExpress;
3 |
4 | const app = new GraphQLess();
5 | const pubsub = new PubSub();
6 | const COUNT_UP = 'COUNT_UP';
7 |
8 | let count = 0;
9 | setInterval(() => pubsub.publish(COUNT_UP, { count: count++ }), 1000);
10 |
11 | app.subscription('/count', {
12 | subscribe: () => pubsub.asyncIterator(COUNT_UP),
13 | });
14 |
15 | app.query('/dummy', (req, res) => {
16 | res.send('Hello');
17 | });
18 |
19 | app.useSchema(`
20 | type Subscription {
21 | count: Int
22 | }
23 | type Query {
24 | dummy: String
25 | }
26 | `);
27 |
28 | app.listen(3000, () => {
29 | console.log('Visit: http://localhost:3000/graphql');
30 | });
31 |
--------------------------------------------------------------------------------
/ops:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | # stop script on frist error
4 |
5 | # These commands take this form
6 | # ./ops [script] [option]
7 |
8 | script=${1}
9 | option=${2}
10 | if [ ! -z $script ] ; then shift ; fi
11 | if [ ! -z $option ] ; then shift ; fi
12 | rest="$@"
13 |
14 | if [ -z $script ] ; then
15 | echo "please enter a command after ./ops"
16 |
17 | elif [ $script == "setup" ] ; then
18 | yarn install
19 | brew install nektos/tap/act
20 |
21 | elif [ $script == "setup-bash" ] ; then
22 | yarn install
23 | curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
24 |
25 | elif [ $script == "test" ] ; then
26 | if [ $option ] && [ $option != "--watchAll" ] ; then
27 | yarn test "__tests__/$option-test.js" $rest
28 | else
29 | yarn test $option $rest
30 | fi
31 |
32 | fi
--------------------------------------------------------------------------------
/examples/exampleWithRouter/router.js:
--------------------------------------------------------------------------------
1 | const { Router } = require('../../index.js');
2 | const router = new Router();
3 |
4 | const db = { users: [{ name: 'Tyler' }] };
5 |
6 | router.get('/users', (req, res) => {
7 | const { users } = db;
8 | res.send(users);
9 | });
10 |
11 | router.get('/user', (req, res) => {
12 | const user = db.users.find(user => user.name === req.body.name);
13 | res.send(user);
14 | });
15 |
16 | router.post('/createUser', (req, res) => {
17 | const userCount = db.users.push({ name: req.body.name });
18 | res.send(userCount);
19 | });
20 |
21 | router.useSchema(`
22 | type Query {
23 | users: [User]
24 | user(name: String): User
25 | }
26 | type Mutation {
27 | createUser(name: String): Int
28 | }
29 | type User {
30 | name: String
31 | }
32 | `);
33 |
34 | module.exports = router;
35 |
--------------------------------------------------------------------------------
/examples/example.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess } = require('../index.js');
2 | const app = new GraphQLess();
3 |
4 | const db = { users: [{ name: 'Tyler' }] };
5 |
6 | app.get('/users', (req, res) => {
7 | const { users } = db;
8 | res.send(users);
9 | });
10 |
11 | app.get('/user', (req, res) => {
12 | const user = db.users.find(user => user.name === req.body.name);
13 | res.send(user);
14 | });
15 |
16 | app.post('/createUser', (req, res) => {
17 | const userCount = db.users.push({ name: req.body.name });
18 | res.send(userCount);
19 | });
20 |
21 | app.useSchema(`
22 | type Query {
23 | users: [User]
24 | user(name: String): User
25 | }
26 | type Mutation {
27 | createUser(name: String): Int
28 | }
29 | type User {
30 | name: String
31 | }
32 | `);
33 |
34 | app.listen(3000, () => {
35 | console.log('Visit: http://localhost:3000/graphql');
36 | });
37 |
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-with-react-client",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "rm -rf dist && NODE_ENV=development parcel watch src/index.html --no-source-maps",
8 | "build": "rm -rf dist && NODE_ENV=production parcel build src/index.html --no-source-maps --public-url /"
9 | },
10 | "dependencies": {
11 | "apollo-cache-inmemory": "^1.5.1",
12 | "apollo-client": "^2.5.1",
13 | "apollo-link-http": "^1.5.11",
14 | "graphql-tag": "^2.10.1",
15 | "react": "^16.7.0",
16 | "react-apollo": "^2.4.0",
17 | "react-dom": "^16.7.0"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.0.0-0",
21 | "@babel/plugin-proposal-class-properties": "^7.3.0",
22 | "@babel/preset-env": "^7.3.1",
23 | "@babel/preset-react": "^7.0.0",
24 | "parcel-bundler": "^1.11.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/index.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess, express } = require('../../index.js');
2 | const app = new GraphQLess();
3 |
4 | const db = {
5 | users: [{ name: 'Tyler' }, { name: 'Brett' }, { name: 'Josh' }],
6 | };
7 |
8 | app.get('/users', (req, res) => {
9 | const { users } = db;
10 | res.send(users);
11 | });
12 |
13 | app.post('/createUser', (req, res) => {
14 | const user = { name: req.body.name };
15 | db.users.push(user);
16 | return res.send(user);
17 | });
18 |
19 | app.useSchema(`
20 | type Query {
21 | users: [User]
22 | }
23 | type Mutation {
24 | createUser(name: String): User
25 | }
26 | type User {
27 | name: String
28 | }
29 | `);
30 |
31 | app.express.use('/', express.static(__dirname + '/dist'));
32 | app.listen(3000, () => {
33 | console.log('Visit: http://localhost:3000/ for React.js front-end');
34 | console.log('Visit: http://localhost:3000/graphql for GraphQLPlayground');
35 | });
36 |
--------------------------------------------------------------------------------
/examples/exampleWithAuth.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jsonwebtoken');
2 |
3 | const { GraphQLess } = require('../index.js');
4 | const app = new GraphQLess({ context: ({ req }) => ({ userId: req.jwt }) });
5 |
6 | const JWT_SECRET = 'SHHH';
7 | const db = { users: [{ id: 'abc', name: 'Tyler' }] };
8 |
9 | // This is our authentication middleware
10 | app.use((req, res, next) => {
11 | req.jwt = req.headers.authorization
12 | ? jwt.verify(req.headers.authorization.replace('Bearer ', ''), JWT_SECRET)
13 | : null;
14 | next();
15 | });
16 |
17 | // Hit this route to get a token
18 | app.get('getToken', (req, res) => {
19 | res.send(jwt.sign(db.users[0].id, JWT_SECRET));
20 | });
21 |
22 | // Now we can access in our route
23 | // context is passed inside req
24 | app.get('/me', (req, res) => {
25 | res.send(db.users.find(user => user.id === req.context.userId));
26 | });
27 |
28 | app.useSchema(`
29 | type Query {
30 | me: User
31 | getToken: String
32 | }
33 | type User {
34 | id: ID
35 | name: String
36 | }
37 | `);
38 |
39 | app.listen(3000, () => {
40 | console.log('Visit: http://localhost:3000/graphql');
41 | });
42 |
--------------------------------------------------------------------------------
/examples/exampleWithRelations.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess } = require('../index.js');
2 | const app = new GraphQLess();
3 |
4 | const db = {
5 | users: [{ id: 1, name: 'Tyler', favoriteIds: [2] }],
6 | favorites: [{ id: 2, name: 'Pizza', userId: 1 }],
7 | };
8 |
9 | class User {
10 | constructor({ name, favoriteIds }) {
11 | this.name = name;
12 | this.favoriteIds = favoriteIds;
13 | }
14 |
15 | favorites() {
16 | return db.favorites
17 | .filter(favorite => this.favoriteIds.includes(favorite.id))
18 | .map(favorite => new Favorite(favorite));
19 | }
20 | }
21 |
22 | class Favorite {
23 | constructor({ name, userId }) {
24 | this.name = name;
25 | this.userId = userId;
26 | }
27 |
28 | user() {
29 | return new User(db.users.find(user => user.id === this.userId));
30 | }
31 | }
32 |
33 | app.get('/users', (req, res) => {
34 | const { users } = db;
35 | res.send(users.map(user => new User(user)));
36 | });
37 |
38 | app.get('/favorites', (req, res) => {
39 | const { favorites } = db;
40 | res.send(favorites.map(favorite => new Favorite(favorite)));
41 | });
42 |
43 | app.useSchema(`
44 | type Query {
45 | users: [User]
46 | favorites: [Favorite]
47 | }
48 | type User {
49 | id: ID!
50 | name: String
51 | favorites: [Favorite]
52 | }
53 | type Favorite {
54 | id: ID!
55 | name: String
56 | user: User
57 | }
58 | `);
59 |
60 | app.listen(3000, () => {
61 | console.log('Visit: http://localhost:3000/graphql');
62 | });
63 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Welcome to the contributing doc! Thanks for showing interest, we're stoked to have you help us make this project even better! Anything you can think of no matter how small just open an issue or PR and we can explore your idea together.
4 |
5 | To get started clone this repo then run `./ops setup`. If you're not running MacOS you'll need a bash shell installed on your OS then you can alternatively run `./ops setup-bash`.
6 |
7 | Checkout the `examples` folder to get started:
8 |
9 | ```bash
10 | npx nodemon examples/example.js
11 | npx nodemon examples/exampleWithAuth.js
12 | npx nodemon examples/exampleWithRouter/index.js
13 | npx nodemon examples/exampleWithReactClient/index.js
14 | npx nodemon examples/exampleWithSubscription.js
15 | npx nodemon examples/exampleWithRelations.js
16 | ```
17 |
18 | The `__tests__` folder provides good documentation of the inner workings of GraphQLess:
19 |
20 | ```bash
21 | ./ops test
22 | ```
23 |
24 | ## Setup
25 |
26 | ```bash
27 | ./ops setup # MacOS
28 | ./ops setup-bash # other systems with BASH installed
29 | ```
30 |
31 | ## Test
32 |
33 | ```bash
34 | ./ops test
35 | ```
36 |
37 | ## Todo
38 |
39 | - Write tests with nock and or graphql-request (Use examples apps as tests)
40 | - Create Github action for building/releasing npm package and use Github package for local development of actions
41 | - Maybe add variable route resolution ie: `/users/:id` req.query.id or something??? Use pick to pull the route defined variables from the GraphQL input vars and place them in req.query then leave the rest in req.body
42 | - Throw error for any route with more than one name `/users` okay. `/users/:id` okay. `/users/:id/:name` okay. `/users/favorites` -> throw Error("Because routes map directly to resolvers they can only have a depth of 1")
43 |
--------------------------------------------------------------------------------
/examples/exampleWithReactClient/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { ApolloClient } from 'apollo-client';
4 | import { InMemoryCache } from 'apollo-cache-inmemory';
5 | import { HttpLink } from 'apollo-link-http';
6 | import { ApolloProvider, Query, Mutation } from 'react-apollo';
7 | import gql from 'graphql-tag';
8 |
9 | const CREATE_USER = gql(`
10 | mutation createUser($name: String) {
11 | createUser(name: $name) {
12 | name
13 | }
14 | }
15 | `);
16 |
17 | const GET_USERS = gql(`
18 | query getUsers {
19 | users {
20 | name
21 | }
22 | }
23 | `);
24 |
25 | class UserList extends React.Component {
26 | state = { name: '' };
27 |
28 | createUserHandler = (cache, { data: { createUser } }) => {
29 | const { users } = cache.readQuery({ query: GET_USERS });
30 | cache.writeQuery({
31 | query: GET_USERS,
32 | data: { users: users.concat([createUser]) },
33 | });
34 | };
35 |
36 | renderSubmitButton = createUser => (
37 | createUser({ variables: { name: this.state.name } })}
39 | >
40 | Create
41 |
42 | );
43 |
44 | renderContent = ({ loading, error, data }) => {
45 | if (loading) return Loading... ;
46 | if (error) return Error ;
47 | return (
48 |
49 |
User List
50 |
this.setState({ name: ev.target.value })}
52 | placeholder="New username..."
53 | value={this.state.name}
54 | />
55 |
56 | {this.renderSubmitButton}
57 |
58 |
59 | {data.users.map((user, index) => (
60 | {user.name}
61 | ))}
62 |
63 |
64 | );
65 | };
66 |
67 | render() {
68 | return {this.renderContent} ;
69 | }
70 | }
71 |
72 | const client = new ApolloClient({
73 | cache: new InMemoryCache(),
74 | link: new HttpLink({ uri: 'http://localhost:3000/graphql' }),
75 | });
76 |
77 | ReactDOM.render(
78 |
79 |
80 | ,
81 | document.getElementById('root')
82 | );
83 |
--------------------------------------------------------------------------------
/__tests__/index.js:
--------------------------------------------------------------------------------
1 | const { GraphQLess, Router } = require('../index.js');
2 |
3 | describe('Router()', () => {
4 | it('Router variables are not static and should not be shared between routers', () => {
5 | const router1 = new Router();
6 | const router2 = new Router();
7 |
8 | router1.useSchema('a dog');
9 | router2.useSchema('a cat');
10 |
11 | expect(router1.schemas).not.toEqual(['a dog', 'a cat']);
12 | expect(router1.schemas).toEqual(['a dog']);
13 | expect(router2.schemas).toEqual(['a cat']);
14 | });
15 | });
16 |
17 | describe('use()', () => {
18 | it('should only accept an instance of Function or Router', () => {
19 | const app = new GraphQLess();
20 | const router = new Router();
21 |
22 | expect(app.use(() => {})).toEqual(app);
23 | expect(app.use(router)).toEqual(app);
24 | expect(() => app.use('red')).toThrow(
25 | Error('use() expects an instance of Function or Router')
26 | );
27 | });
28 |
29 | it('should chain multiple middleware functions', () => {
30 | const app = new GraphQLess();
31 |
32 | app.use(() => 'first in chain');
33 | app.use(() => 'second in chain');
34 |
35 | expect(app.middlewares.length).toEqual(2);
36 | expect(app.middlewares[0]()).toEqual('first in chain');
37 | expect(app.middlewares[1]()).toEqual('second in chain');
38 | });
39 |
40 | it('should merge class variables if input is a GraphQLess instance', () => {
41 | const app = new GraphQLess();
42 | const router = new Router();
43 |
44 | router.get('/get', () => {});
45 | router.post('/post', () => {});
46 | router.put('/put', () => {});
47 | router.delete('/delete', () => {});
48 | router.query('/query', () => {});
49 | router.mutation('/mutation', () => {});
50 | router.subscription('/mutation', () => {});
51 |
52 | router.useSchema('test');
53 | router.useSchema('test2');
54 |
55 | router.use(() => {});
56 | router.use(() => {});
57 |
58 | app.use(router);
59 | expect(app.middlewares.length).toEqual(2);
60 | expect(
61 | Object.keys({
62 | ...app.resolvers.Query,
63 | ...app.resolvers.Mutation,
64 | ...app.resolvers.Subscription,
65 | }).length
66 | ).toBe(6);
67 | expect(app.schemas.length).toBe(2);
68 | });
69 |
70 | it('should correctly chain Router middlewares to existing app middleswares', () => {
71 | const app = new GraphQLess();
72 | const router = new Router();
73 |
74 | router.use(() => 'router middleware');
75 | app.use(router);
76 | app.use(() => 'app middleware');
77 |
78 | expect(app.middlewares.length).toEqual(2);
79 | expect(app.middlewares[0]()).toEqual('router middleware');
80 | expect(app.middlewares[1]()).toEqual('app middleware');
81 | });
82 | });
83 |
84 | describe('express', () => {
85 | it('should be accessible from GraphQLess instance', () => {
86 | const app = new GraphQLess();
87 | app.express.use('/example', (req, res) => res.send('test'));
88 |
89 | // express internals
90 | const layer = app.express._router.stack.pop();
91 | const output = layer.handle(null, { send: val => val });
92 |
93 | expect(output).toEqual('test');
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const { mergeResolvers, mergeTypes } = require('merge-graphql-schemas');
2 | const ApolloServerExpress = require('apollo-server-express');
3 | const express = require('express');
4 | const http = require('http');
5 |
6 | const { ApolloServer, gql } = ApolloServerExpress;
7 |
8 | class Router {
9 | constructor() {
10 | this.RESOLVER_TYPES = {
11 | Query: 'Query',
12 | Mutation: 'Mutation',
13 | Subscription: 'Subscription',
14 | };
15 | this.delete = this.patch = this.post = this.put = this.mutation;
16 | this.get = this.query;
17 | this.middlewares = [];
18 | this.resolvers = {};
19 | this.schemas = [];
20 | }
21 |
22 | use(func) {
23 | if (func instanceof Router) {
24 | this.middlewares = this.middlewares.concat(func.middlewares);
25 | this.resolvers = mergeResolvers([this.resolvers, func.resolvers]);
26 | this.schemas = [...this.schemas, ...func.schemas];
27 | } else if (func instanceof Function) {
28 | this.middlewares.push(func);
29 | } else {
30 | throw Error('use() expects an instance of Function or Router');
31 | }
32 | return this;
33 | }
34 |
35 | query(route, resolver) {
36 | const { Query } = this.RESOLVER_TYPES;
37 | if (!this.resolvers[Query]) {
38 | this.resolvers[Query] = {};
39 | }
40 | return this.useResolver(route, resolver, Query);
41 | }
42 |
43 | mutation(route, resolver) {
44 | const { Mutation } = this.RESOLVER_TYPES;
45 | if (!this.resolvers[Mutation]) {
46 | this.resolvers[Mutation] = {};
47 | }
48 | return this.useResolver(route, resolver, Mutation);
49 | }
50 |
51 | subscription(route, resolver) {
52 | const { Subscription } = this.RESOLVER_TYPES;
53 | if (!this.resolvers[Subscription]) {
54 | this.resolvers[Subscription] = {};
55 | }
56 | return this.useResolver(route, resolver, Subscription);
57 | }
58 |
59 | useResolver(route, resolver, type) {
60 | const name = route.replace('/', '');
61 | if (this.resolvers[type][name]) {
62 | throw Error(`Cannot declare duplicate resolver name: ${name}`);
63 | }
64 | const { Subscription } = this.RESOLVER_TYPES;
65 | this.resolvers[type][name] =
66 | type === Subscription
67 | ? resolver
68 | : (parent, args, context, info) =>
69 | new Promise(resolve =>
70 | resolver({ body: args, context }, { send: resolve })
71 | );
72 | return this;
73 | }
74 |
75 | useSchema(schema) {
76 | this.schemas.push(schema);
77 | return this;
78 | }
79 | }
80 |
81 | class GraphQLess extends Router {
82 | constructor(config = { options: {} }) {
83 | super();
84 | this.express = express();
85 | const { options, ...rest } = config;
86 | this.configApolloServer = rest;
87 | this.options = {
88 | endpoint: '/graphql',
89 | playground: true,
90 | mergeTypes: { all: true },
91 | ...options,
92 | };
93 | }
94 |
95 | listen(...argsListen) {
96 | const server = new ApolloServer({
97 | typeDefs: gql(mergeTypes(this.schemas, this.options.mergeTypes)),
98 | resolvers: this.resolvers,
99 | playground: this.options.playground,
100 | ...this.configApolloServer,
101 | });
102 |
103 | this.middlewares.forEach(middleware => {
104 | this.express.use(this.options.endpoint, middleware);
105 | });
106 |
107 | server.applyMiddleware({ app: this.express, path: this.options.endpoint });
108 |
109 | const { Subscription } = this.RESOLVER_TYPES;
110 | const hasSubscriptions = this.resolvers[Subscription];
111 |
112 | if (hasSubscriptions) {
113 | const httpServer = http.createServer(this.express);
114 | server.installSubscriptionHandlers(httpServer);
115 | return httpServer.listen(...argsListen);
116 | }
117 |
118 | return this.express.listen(...argsListen);
119 | }
120 | }
121 |
122 | module.exports = {
123 | GraphQLess,
124 | Router,
125 | ApolloServerExpress,
126 | express,
127 | };
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GraphQLess
6 |
7 |
8 | ⚛️ 🚀🤘
9 |
10 |
11 | REST and GraphQL really aren't that different. I'll prove it!
12 | GraphQLess is a thin wrapper around the official apollo-server-express project.
13 |
14 | GraphQLess lets you write a GraphQL server in an Express.js style.
15 |
16 |
17 | ## Setup
18 |
19 | ```bash
20 | yarn add graphqless
21 | ```
22 |
23 | And here is how you write a server... Look familiar?
24 |
25 | ```jsx
26 | const { GraphQLess } = require('graphqless');
27 | const app = new GraphQLess();
28 |
29 | const db = { users: [{ name: 'Tyler' }] };
30 |
31 | app.get('/users', (req, res) => {
32 | const { users } = db;
33 | res.send(users);
34 | });
35 |
36 | app.get('/user', (req, res) => {
37 | const user = db.users.find(user => user.name === req.body.name);
38 | res.send(user);
39 | });
40 |
41 | app.post('/createUser', (req, res) => {
42 | const userCount = db.users.push({ name: req.body.name });
43 | res.send(userCount);
44 | });
45 |
46 | app.listen(3000, () => {
47 | console.log('Visit: http://localhost:3000/playground');
48 | });
49 | ```
50 |
51 | I know it looks like Express.js but the code above is a GraphQL server! There is one caveat though...
52 |
53 | GraphQL requires us to write a schema that describes the `.get` and `.post` functions' inputs and outputs.
54 |
55 | Just know that `.get === Query && .post === Mutation`. Now let's modify the last few lines of the snippet above to include the required schema:
56 |
57 | ```jsx
58 | app
59 | .useSchema(
60 | `
61 | type Query {
62 | users: [User]
63 | user(name: String): User
64 | }
65 | type Mutation {
66 | createUser(name: String): Int
67 | }
68 | type User {
69 | name: String
70 | }
71 | `
72 | )
73 | .listen(3000, () => {
74 | console.log('Visit: http://localhost:3000/playground');
75 | });
76 | ```
77 |
78 | That's the only catch! You now have a fully functioning and extendable GraphQL server.
79 |
80 | You can find more examples in the [examples](/examples) folder.
81 |
82 | ## Examples
83 |
84 | ### Queries for examples/example
85 |
86 | ```bash
87 | npx nodemon examples/example.js
88 | ```
89 |
90 | ```graphql
91 | mutation createUser {
92 | createUser(name: "Buchea")
93 | }
94 |
95 | query getUsers {
96 | users {
97 | name
98 | }
99 | user(name: "Tyler") {
100 | name
101 | }
102 | }
103 | ```
104 |
105 | ### Queries for examples/exampleWithAuth
106 |
107 | ```bash
108 | npx nodemon examples/exampleWithAuth.js
109 | ```
110 |
111 | ```graphql
112 | # Add this to "HTTP HEADERS" in GraphQL Playground:
113 | # { "Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.YWJj.4noRC-c0ay0hOeZ5Cgc80MVS0P4p4FrR2lJFzMNSnE4" }
114 |
115 | query getMe {
116 | getToken
117 | me {
118 | id
119 | name
120 | }
121 | }
122 | ```
123 |
124 | ### Queries for examples/exampleWithRouter
125 |
126 | ```bash
127 | npx nodemon examples/exampleWithRouter/index.js
128 | ```
129 |
130 | ```graphql
131 | mutation createUser {
132 | createUser(name: "Buchea")
133 | }
134 |
135 | query getUsers {
136 | users {
137 | name
138 | }
139 | user(name: "Tyler") {
140 | name
141 | }
142 | }
143 | ```
144 |
145 | ### Queries for examples/exampleWithReactClient
146 |
147 | ```bash
148 | npx nodemon examples/exampleWithReactClient/index.js
149 | ```
150 |
151 | ```graphql
152 | mutation createUser {
153 | createUser(name: "Buchea") {
154 | name
155 | }
156 | }
157 |
158 | query getUsers {
159 | users {
160 | name
161 | }
162 | }
163 | ```
164 |
165 | ### Queries for examples/exampleWithSubscription
166 |
167 | ```bash
168 | npx nodemon examples/exampleWithSubscription.js
169 | ```
170 |
171 | ```graphql
172 | subscription subscribeToCount {
173 | count
174 | }
175 |
176 | query getDummyData {
177 | dummy
178 | }
179 | ```
180 |
181 | ### Queries for examples/exampleWithRelations
182 |
183 | ```bash
184 | npx nodemon examples/exampleWithRelations.js
185 | ```
186 |
187 | ```graphql
188 | query getDeepRelations {
189 | users {
190 | name
191 | favorites {
192 | name
193 | user {
194 | name
195 | }
196 | }
197 | }
198 | }
199 | ```
200 |
--------------------------------------------------------------------------------