├── .gitignore ├── LICENSE ├── Makefile ├── Package.swift ├── README.md ├── Sources ├── Handlers.swift ├── Schema.swift ├── User.swift └── main.swift └── Tests ├── GraphQLHandlerTests └── GraphQLHandlerTest.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | Packages/ 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata/ 20 | 21 | ## Other 22 | *.moved-aside 23 | *.xcuserstate 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | # Pods/ 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vinicius Souza 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT=GraphQLHandler 2 | 3 | 4 | build: 5 | @swift build 6 | 7 | run: build 8 | @./.build/debug/$(PROJECT) 9 | 10 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "GraphQLHandler", 5 | dependencies: [ 6 | .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 3, minor: 0), 7 | .Package(url: "https://github.com/GraphQLSwift/GraphQL.git", majorVersion: 0, minor: 3) 8 | ] 9 | ) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL Handler 2 | 3 | > Swift GraphQL Handler 4 | 5 | [![Swift Version][swift-image]][swift-url] 6 | [![License][license-image]][license-url] 7 | [![codebeat badge](https://codebeat.co/badges/1c0f9ac0-84e2-4c1d-96ef-5c0094bca263)](https://codebeat.co/projects/github-com-vsouza-graphql-handler-master) 8 | 9 | A simple handler implementation of GraphQL using [Perfect](http://perfect.org) 10 | 11 | 12 | ## How it works? 13 | 14 | A simple route with a `indexHandler` call when `GET` on `/graphql` route. 15 | 16 | ```swift 17 | routes.add(method: .get, uri: "/graphql", handler: indexHandler) 18 | ``` 19 | 20 | Define your schema with `RootQueries`, `RootMutations`, `CustomTypes` e etc... 21 | 22 | ```swift 23 | let UserType = try! GraphQLObjectType( 24 | name: "User", 25 | description: "A user in system", 26 | fields:[ 27 | "id": GraphQLField( 28 | type: GraphQLNonNull(GraphQLString), 29 | description: "The id of the user." 30 | ), 31 | "name": GraphQLField( 32 | type: GraphQLString, 33 | description: "The name of the user." 34 | ), 35 | "email": GraphQLField( 36 | type: GraphQLString, 37 | description: "The email of the user." 38 | ), 39 | ] 40 | ) 41 | ``` 42 | 43 | In handler you will call the GraphQL parser: 44 | 45 | ```swift 46 | let result = try graphql(schema: schema, request: query) 47 | ``` 48 | 49 | __[GraphQL library](https://github.com/GraphQLSwift/GraphQL)__ returns a `map` 50 | by default, to get a JSON you should use `result.description` like @paulofaria 51 | says here: 52 | [#1](https://github.com/GraphQLSwift/GraphQL/issues/1#issuecomment-257182572) 53 | 54 | 55 | 56 | ## Usage example 57 | 58 | `make run` 59 | 60 | listen on `:8080` 61 | 62 | `http://localhost:8080/graphql?query=query{user(id: "3000"){id,name,email}}` 63 | 64 | 65 | ## Release History 66 | 67 | * 0.0.2 68 | * Improve Schema and add sample data 69 | * 0.0.1 70 | * Simple handler with basic schema 71 | 72 | ## Meta 73 | 74 | Vinicius Souza – [@iamvsouza](https://twitter.com/iamvsouza) – hi@vsouza.com 75 | 76 | Distributed under the MIT license. See [License](http://vsouza.mit-license.org/) 77 | 78 | [https://github.com/vsouza](https://github.com/vsouza/) 79 | 80 | [swift-image]:https://img.shields.io/badge/swift-4.0-orange.svg 81 | [swift-url]: https://swift.org/ 82 | [license-image]: https://img.shields.io/badge/License-MIT-blue.svg 83 | [license-url]: LICENSE 84 | -------------------------------------------------------------------------------- /Sources/Handlers.swift: -------------------------------------------------------------------------------- 1 | import PerfectLib 2 | import PerfectHTTP 3 | import GraphQL 4 | 5 | 6 | // indexHandler 7 | func indexHandler(request: HTTPRequest, _ response: HTTPResponse) { 8 | // check "query" param on query string 9 | if let query = request.param(name: "query"){ 10 | do { 11 | let schema = UserSchema 12 | // call graphql query parser 13 | let result = try graphql(schema: schema, request: query) 14 | response.setHeader(.contentType, value: "application/json") 15 | response.appendBody(string: result.description) 16 | response.completed() 17 | } catch { 18 | response.status = .internalServerError 19 | response.completed() 20 | } 21 | } 22 | response.status = .badRequest 23 | response.completed() 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Schema.swift: -------------------------------------------------------------------------------- 1 | import GraphQL 2 | 3 | extension User : MapFallibleRepresentable {} 4 | 5 | let UserType = try! GraphQLObjectType( 6 | name: "User", 7 | description: "A user in system", 8 | fields:[ 9 | "id": GraphQLField( 10 | type: GraphQLString, 11 | description: "The id of the id." 12 | ), 13 | "name": GraphQLField( 14 | type: GraphQLString, 15 | description: "The name of the name." 16 | ), 17 | "email": GraphQLField( 18 | type: GraphQLString, 19 | description: "The email of the user." 20 | ), 21 | ] 22 | ) 23 | 24 | let QueryType = try! GraphQLObjectType( 25 | name: "Query", 26 | fields: [ 27 | "user": GraphQLField( 28 | type: UserType, 29 | args: [ 30 | "id": GraphQLArgument( 31 | type: GraphQLString, 32 | description: "Get user by ID" 33 | ) 34 | ], 35 | resolve: { _, arguments, _, _ in 36 | return getUser(id: arguments["id"].string!) 37 | } 38 | ), 39 | ] 40 | ) 41 | 42 | let UserSchema = try! GraphQLSchema( 43 | query: QueryType, 44 | types: [UserType] 45 | ) -------------------------------------------------------------------------------- /Sources/User.swift: -------------------------------------------------------------------------------- 1 | //SAMPLE DATA 2 | 3 | let c3po = User( 4 | id: "1000", 5 | name: "C-3PO", 6 | email: "c3po@email.com" 7 | ) 8 | 9 | let john = User( 10 | id: "2000", 11 | name: "John Doe", 12 | email: "john@email.com" 13 | ) 14 | 15 | let mike = User( 16 | id: "3000", 17 | name: "Mike Michael", 18 | email: "miike@email.com" 19 | ) 20 | 21 | let userData: [String: User] = [ 22 | "1000": c3po, 23 | "2000": john, 24 | "3000": mike, 25 | ] 26 | 27 | 28 | struct User { 29 | let id: String 30 | let name: String 31 | let email: String 32 | 33 | init(id: String, name: String, email: String) { 34 | self.id = id 35 | self.name = name 36 | self.email = email 37 | } 38 | } 39 | 40 | func getUser(id: String) -> User? { 41 | return userData[id] 42 | } -------------------------------------------------------------------------------- /Sources/main.swift: -------------------------------------------------------------------------------- 1 | import PerfectLib 2 | import PerfectHTTP 3 | import PerfectHTTPServer 4 | 5 | let server = HTTPServer() 6 | 7 | 8 | var routes = Routes() 9 | routes.add(method: .get, uri: "/graphql", handler: indexHandler) 10 | 11 | server.addRoutes(routes) 12 | server.serverPort = 8080 13 | 14 | do { 15 | try server.start() 16 | } catch PerfectError.networkError(let err, let msg) { 17 | print("Network error thrown: \(err) \(msg)") 18 | } 19 | -------------------------------------------------------------------------------- /Tests/GraphQLHandlerTests/GraphQLHandlerTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import GraphQLHandler 3 | 4 | class TestGraphQLHandler: XCTestCase { 5 | 6 | 7 | static var allTests: [(String, (TestGraphQLHandler) -> () throws -> Void)] { 8 | return [ 9 | ("test200Ok", testShouldReturnStatusOk), 10 | ("testBadRequest", testShouldReturnStatusBadRequest), 11 | ] 12 | } 13 | 14 | func testShouldReturnStatusOk(){ 15 | XCTAssertEqual(200, 200) 16 | } 17 | 18 | func testShouldReturnStatusBadRequest(){ 19 | XCTAssertEqual(200, 200) 20 | } 21 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | @testable import GraphQLHandlerTests 4 | 5 | XCTMain([ 6 | testCase(TestGraphQLHandler.allTests), 7 | ]) 8 | --------------------------------------------------------------------------------