├── .gitignore ├── README.md ├── app ├── app.ts ├── products │ ├── products.model.ts │ ├── products.sample.ts │ └── products.service.ts └── users │ ├── users.model.ts │ ├── users.sample.ts │ └── users.service.ts ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | **/node_modules 3 | **/build 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node GraphQL Tutorial 2 | 3 | A basic skeleton for an API using Graphql and Express.js 4 | 5 | This project was made to be used within the following article: 6 | 7 | https://www.toptal.com/graphql/graphql-nodejs-api 8 | 9 | ## First things first 10 | 11 | Please run `npm install` before trying to run the program 12 | 13 | You can run the app using `npm start` and the app will load on port 3000. 14 | 15 | ## Structure 16 | 17 | The source code is at `app` folder. As matter of examples, both users and products models have a sample file just to load some data when the server is on. 18 | 19 | This code was made without any external database to easy it up the source code reading. 20 | 21 | In order to create a new module, please make sure to add the new module into the app.ts. 22 | 23 | The products and users are defined in the app.ts as 24 | 25 | ``` 26 | let productsService = new ProductsService(); 27 | let usersService = new UsersService(); 28 | typeDefs += productsService.configTypeDefs(); 29 | typeDefs += usersService.configTypeDefs(); 30 | 31 | productsService.configResolvers(resolvers); 32 | usersService.configResolvers(resolvers); 33 | ``` 34 | 35 | The purpose of this approach is to reduce huge configuration files for typeDefs and resolvers. 36 | -------------------------------------------------------------------------------- /app/app.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import graphqlHTTP from 'express-graphql'; 3 | import {makeExecutableSchema} from 'graphql-tools'; 4 | import {ProductsService} from './products/products.service'; 5 | import {UsersService} from './users/users.service'; 6 | 7 | const app: express.Application = express(); 8 | const port = 3000; 9 | 10 | 11 | let typeDefs: any = [` 12 | type Query { 13 | hello: String 14 | } 15 | 16 | type Mutation { 17 | hello(message: String) : String 18 | } 19 | `]; 20 | 21 | let helloMessage: String = 'World!'; 22 | 23 | let resolvers = { 24 | Query: { 25 | hello: () => helloMessage 26 | }, 27 | Mutation: { 28 | hello: (_: any, helloData: any) => { 29 | helloMessage = helloData.message; 30 | return helloMessage; 31 | } 32 | } 33 | }; 34 | 35 | let productsService = new ProductsService(); 36 | let usersService = new UsersService(); 37 | typeDefs += productsService.configTypeDefs(); 38 | typeDefs += usersService.configTypeDefs(); 39 | 40 | productsService.configResolvers(resolvers); 41 | usersService.configResolvers(resolvers); 42 | 43 | app.use( 44 | '/graphql', 45 | graphqlHTTP({ 46 | schema: makeExecutableSchema({typeDefs, resolvers}), 47 | graphiql: true 48 | }) 49 | ); 50 | app.listen(port, () => console.log(`Node Graphql API listening on port ${port}!`)); 51 | -------------------------------------------------------------------------------- /app/products/products.model.ts: -------------------------------------------------------------------------------- 1 | export class Product { 2 | private id: Number = 0; 3 | private name: String = ''; 4 | private description: String = ''; 5 | private price: Number = 0; 6 | 7 | constructor(productId: Number, 8 | productName: String, 9 | productDescription: String, 10 | price: Number) { 11 | this.id = productId; 12 | this.name = productName; 13 | this.description = productDescription; 14 | this.price = price; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/products/products.sample.ts: -------------------------------------------------------------------------------- 1 | import {Product} from './products.model'; 2 | 3 | const products: Array = []; 4 | 5 | products.push(new Product(1, 'Amazing Graphql Product', 'my product one description', 100)) 6 | products.push(new Product(2, 'Amazing second Graphql Product', 'My second simple Node.js API tutorial', 250)) 7 | exports.productsSample = products; 8 | -------------------------------------------------------------------------------- /app/products/products.service.ts: -------------------------------------------------------------------------------- 1 | const productSample = require('./products.sample').productsSample; 2 | 3 | export class ProductsService { 4 | 5 | public products: any = productSample; 6 | 7 | configTypeDefs() { 8 | let typeDefs = ` 9 | type Product { 10 | name: String, 11 | description: String, 12 | id: Int, 13 | price: Int 14 | } `; 15 | typeDefs += ` 16 | extend type Query { 17 | products: [Product] 18 | } 19 | `; 20 | 21 | typeDefs += ` 22 | extend type Mutation { 23 | product(name:String, id:Int, description: String, price: Int): Product! 24 | }`; 25 | return typeDefs; 26 | } 27 | 28 | configResolvers(resolvers: any) { 29 | resolvers.Query.products = () => { 30 | return this.products; 31 | }; 32 | resolvers.Mutation.product = (_: any, product: any) => { 33 | this.products.push(product); 34 | return product; 35 | }; 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/users/users.model.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | private id: Number = 0; 3 | private firstName: String = ''; 4 | private lastName: String = ''; 5 | private email: String = ''; 6 | private password: String = ''; 7 | private permissionLevel: Number = 1; 8 | 9 | constructor(id: Number, 10 | firstName: String, 11 | lastName: String, 12 | email: String, 13 | password: String, 14 | permissionLevel: Number) { 15 | this.id = id; 16 | this.firstName = firstName; 17 | this.lastName = lastName; 18 | this.email = email; 19 | this.password = password; 20 | this.permissionLevel = permissionLevel; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/users/users.sample.ts: -------------------------------------------------------------------------------- 1 | import { User } from './users.model'; 2 | 3 | const users: Array = []; 4 | 5 | users.push(new User(1, 'Marcos', 'Silva', 'marcos.henrique@toptal.com', '2r828hEoPkDYwj26RWC3tg==$Q1kb3HB+csO65uSVTURrANZiaut2FibpfavNGgU8WZxnJodKbj9JtTSs8O466aii+wGSN9bMUwK3OJNHKcvVMg==', 9)); 6 | users.push(new User(2, 'Silva', 'Marcos', 'my.second.email@toptal.com', '2r828hEoPkDYwj26RWC3tg==$Q1kb3HB+csO65uSVTURrANZiaut2FibpfavNGgU8WZxnJodKbj9JtTSs8O466aii+wGSN9bMUwK3OJNHKcvVMg==', 1)); 7 | 8 | exports.usersSample = users; 9 | -------------------------------------------------------------------------------- /app/users/users.service.ts: -------------------------------------------------------------------------------- 1 | const userSample = require('./users.sample').usersSample; 2 | const crypto = require('crypto'); 3 | 4 | export class UsersService { 5 | 6 | public users: any = userSample; 7 | 8 | configTypeDefs() { 9 | let typeDefs = ` 10 | type User { 11 | firstName: String, 12 | lastName: String, 13 | id: Int, 14 | password: String, 15 | permissionLevel: Int, 16 | email: String 17 | } `; 18 | typeDefs += ` 19 | extend type Query { 20 | users: [User] 21 | } 22 | `; 23 | 24 | typeDefs += ` 25 | extend type Mutation { 26 | user(firstName:String, 27 | lastName: String, 28 | password: String, 29 | permissionLevel: Int, 30 | email: String, 31 | id:Int): User! 32 | }`; 33 | return typeDefs; 34 | } 35 | 36 | configResolvers(resolvers: any) { 37 | resolvers.Query.users = () => { 38 | return this.users; 39 | }; 40 | resolvers.Mutation.user = (_: any, user: any) => { 41 | let salt = crypto.randomBytes(16).toString('base64'); 42 | let hash = crypto.createHmac('sha512', salt).update(user.password).digest("base64"); 43 | user.password = hash; 44 | this.users.push(user); 45 | return user; 46 | }; 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-graphql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "tsc": "tsc", 8 | "start": "npm run tsc && node ./build/app.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/express": "^4.16.1", 14 | "@types/express-graphql": "^0.6.2", 15 | "@types/graphql": "^14.0.7", 16 | "crypto": "^1.0.1", 17 | "express": "^4.16.4", 18 | "express-graphql": "^0.7.1", 19 | "graphql": "^14.1.1", 20 | "graphql-tools": "^4.0.4" 21 | }, 22 | "devDependencies": { 23 | "tslint": "^5.14.0", 24 | "typescript": "^3.3.4000" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2016", 4 | "module": "commonjs", 5 | "outDir": "./build", 6 | "strict": true, 7 | "esModuleInterop": true 8 | } 9 | } 10 | --------------------------------------------------------------------------------