├── .gitignore ├── README.md ├── crusty-api ├── .gitignore ├── .idea │ ├── .gitignore │ ├── crusty-api.iml │ ├── misc.xml │ ├── modules.xml │ └── vcs.xml ├── Cargo.toml ├── load-test.js └── src │ ├── main.rs │ ├── models │ ├── game.rs │ └── mod.rs │ ├── routes │ ├── graphql │ │ ├── controller.rs │ │ └── mod.rs │ ├── landing │ │ ├── controller.rs │ │ └── mod.rs │ └── mod.rs │ └── services │ ├── mod.rs │ ├── query.rs │ ├── schema.rs │ └── subscriptions.rs └── slide └── GraphQL in 3ms.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node,macos,code,python,jetbrains,rust 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,code,python,jetbrains,rust 4 | 5 | ### Code ### 6 | .vscode/* 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | *.code-workspace 10 | 11 | ### JetBrains ### 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 13 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 14 | 15 | # User-specific stuff 16 | .idea/**/workspace.xml 17 | .idea/**/tasks.xml 18 | .idea/**/usage.statistics.xml 19 | .idea/**/dictionaries 20 | .idea/**/shelf 21 | 22 | # Generated files 23 | .idea/**/contentModel.xml 24 | 25 | # Sensitive or high-churn files 26 | .idea/**/dataSources/ 27 | .idea/**/dataSources.ids 28 | .idea/**/dataSources.local.xml 29 | .idea/**/sqlDataSources.xml 30 | .idea/**/dynamic.xml 31 | .idea/**/uiDesigner.xml 32 | .idea/**/dbnavigator.xml 33 | 34 | # Gradle 35 | .idea/**/gradle.xml 36 | .idea/**/libraries 37 | 38 | # Gradle and Maven with auto-import 39 | # When using Gradle or Maven with auto-import, you should exclude module files, 40 | # since they will be recreated, and may cause churn. Uncomment if using 41 | # auto-import. 42 | # .idea/artifacts 43 | # .idea/compiler.xml 44 | # .idea/jarRepositories.xml 45 | # .idea/modules.xml 46 | # .idea/*.iml 47 | # .idea/modules 48 | # *.iml 49 | # *.ipr 50 | 51 | # CMake 52 | cmake-build-*/ 53 | 54 | # Mongo Explorer plugin 55 | .idea/**/mongoSettings.xml 56 | 57 | # File-based project format 58 | *.iws 59 | 60 | # IntelliJ 61 | out/ 62 | 63 | # mpeltonen/sbt-idea plugin 64 | .idea_modules/ 65 | 66 | # JIRA plugin 67 | atlassian-ide-plugin.xml 68 | 69 | # Cursive Clojure plugin 70 | .idea/replstate.xml 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | # Editor-based Rest Client 79 | .idea/httpRequests 80 | 81 | # Android studio 3.1+ serialized cache file 82 | .idea/caches/build_file_checksums.ser 83 | 84 | ### JetBrains Patch ### 85 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 86 | 87 | # *.iml 88 | # modules.xml 89 | # .idea/misc.xml 90 | # *.ipr 91 | 92 | # Sonarlint plugin 93 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 94 | .idea/**/sonarlint/ 95 | 96 | # SonarQube Plugin 97 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 98 | .idea/**/sonarIssues.xml 99 | 100 | # Markdown Navigator plugin 101 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 102 | .idea/**/markdown-navigator.xml 103 | .idea/**/markdown-navigator-enh.xml 104 | .idea/**/markdown-navigator/ 105 | 106 | # Cache file creation bug 107 | # See https://youtrack.jetbrains.com/issue/JBR-2257 108 | .idea/$CACHE_FILE$ 109 | 110 | # CodeStream plugin 111 | # https://plugins.jetbrains.com/plugin/12206-codestream 112 | .idea/codestream.xml 113 | 114 | ### macOS ### 115 | # General 116 | .DS_Store 117 | .AppleDouble 118 | .LSOverride 119 | 120 | # Icon must end with two \r 121 | Icon 122 | 123 | # Thumbnails 124 | ._* 125 | 126 | # Files that might appear in the root of a volume 127 | .DocumentRevisions-V100 128 | .fseventsd 129 | .Spotlight-V100 130 | .TemporaryItems 131 | .Trashes 132 | .VolumeIcon.icns 133 | .com.apple.timemachine.donotpresent 134 | 135 | # Directories potentially created on remote AFP share 136 | .AppleDB 137 | .AppleDesktop 138 | Network Trash Folder 139 | Temporary Items 140 | .apdisk 141 | 142 | ### Node ### 143 | # Logs 144 | logs 145 | *.log 146 | npm-debug.log* 147 | yarn-debug.log* 148 | yarn-error.log* 149 | lerna-debug.log* 150 | 151 | # Diagnostic reports (https://nodejs.org/api/report.html) 152 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 153 | 154 | # Runtime data 155 | pids 156 | *.pid 157 | *.seed 158 | *.pid.lock 159 | 160 | # Directory for instrumented libs generated by jscoverage/JSCover 161 | lib-cov 162 | 163 | # Coverage directory used by tools like istanbul 164 | coverage 165 | *.lcov 166 | 167 | # nyc test coverage 168 | .nyc_output 169 | 170 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 171 | .grunt 172 | 173 | # Bower dependency directory (https://bower.io/) 174 | bower_components 175 | 176 | # node-waf configuration 177 | .lock-wscript 178 | 179 | # Compiled binary addons (https://nodejs.org/api/addons.html) 180 | build/Release 181 | 182 | # Dependency directories 183 | node_modules/ 184 | jspm_packages/ 185 | 186 | # TypeScript v1 declaration files 187 | typings/ 188 | 189 | # TypeScript cache 190 | *.tsbuildinfo 191 | 192 | # Optional npm cache directory 193 | .npm 194 | 195 | # Optional eslint cache 196 | .eslintcache 197 | 198 | # Microbundle cache 199 | .rpt2_cache/ 200 | .rts2_cache_cjs/ 201 | .rts2_cache_es/ 202 | .rts2_cache_umd/ 203 | 204 | # Optional REPL history 205 | .node_repl_history 206 | 207 | # Output of 'npm pack' 208 | *.tgz 209 | 210 | # Yarn Integrity file 211 | .yarn-integrity 212 | 213 | # dotenv environment variables file 214 | .env 215 | .env.test 216 | .env*.local 217 | 218 | # parcel-bundler cache (https://parceljs.org/) 219 | .cache 220 | .parcel-cache 221 | 222 | # Next.js build output 223 | .next 224 | 225 | # Nuxt.js build / generate output 226 | .nuxt 227 | dist 228 | 229 | # Gatsby files 230 | .cache/ 231 | # Comment in the public line in if your project uses Gatsby and not Next.js 232 | # https://nextjs.org/blog/next-9-1#public-directory-support 233 | # public 234 | 235 | # vuepress build output 236 | .vuepress/dist 237 | 238 | # Serverless directories 239 | .serverless/ 240 | 241 | # FuseBox cache 242 | .fusebox/ 243 | 244 | # DynamoDB Local files 245 | .dynamodb/ 246 | 247 | # TernJS port file 248 | .tern-port 249 | 250 | # Stores VSCode versions used for testing VSCode extensions 251 | .vscode-test 252 | 253 | ### Python ### 254 | # Byte-compiled / optimized / DLL files 255 | __pycache__/ 256 | *.py[cod] 257 | *$py.class 258 | 259 | # C extensions 260 | *.so 261 | 262 | # Distribution / packaging 263 | .Python 264 | build/ 265 | develop-eggs/ 266 | dist/ 267 | downloads/ 268 | eggs/ 269 | .eggs/ 270 | lib/ 271 | lib64/ 272 | parts/ 273 | sdist/ 274 | var/ 275 | wheels/ 276 | pip-wheel-metadata/ 277 | share/python-wheels/ 278 | *.egg-info/ 279 | .installed.cfg 280 | *.egg 281 | MANIFEST 282 | 283 | # PyInstaller 284 | # Usually these files are written by a python script from a template 285 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 286 | *.manifest 287 | *.spec 288 | 289 | # Installer logs 290 | pip-log.txt 291 | pip-delete-this-directory.txt 292 | 293 | # Unit test / coverage reports 294 | htmlcov/ 295 | .tox/ 296 | .nox/ 297 | .coverage 298 | .coverage.* 299 | nosetests.xml 300 | coverage.xml 301 | *.cover 302 | *.py,cover 303 | .hypothesis/ 304 | .pytest_cache/ 305 | pytestdebug.log 306 | 307 | # Translations 308 | *.mo 309 | *.pot 310 | 311 | # Django stuff: 312 | local_settings.py 313 | db.sqlite3 314 | db.sqlite3-journal 315 | 316 | # Flask stuff: 317 | instance/ 318 | .webassets-cache 319 | 320 | # Scrapy stuff: 321 | .scrapy 322 | 323 | # Sphinx documentation 324 | docs/_build/ 325 | doc/_build/ 326 | 327 | # PyBuilder 328 | target/ 329 | 330 | # Jupyter Notebook 331 | .ipynb_checkpoints 332 | 333 | # IPython 334 | profile_default/ 335 | ipython_config.py 336 | 337 | # pyenv 338 | .python-version 339 | 340 | # pipenv 341 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 342 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 343 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 344 | # install all needed dependencies. 345 | #Pipfile.lock 346 | 347 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 348 | __pypackages__/ 349 | 350 | # Celery stuff 351 | celerybeat-schedule 352 | celerybeat.pid 353 | 354 | # SageMath parsed files 355 | *.sage.py 356 | 357 | # Environments 358 | .venv 359 | env/ 360 | venv/ 361 | ENV/ 362 | env.bak/ 363 | venv.bak/ 364 | pythonenv* 365 | 366 | # Spyder project settings 367 | .spyderproject 368 | .spyproject 369 | 370 | # Rope project settings 371 | .ropeproject 372 | 373 | # mkdocs documentation 374 | /site 375 | 376 | # mypy 377 | .mypy_cache/ 378 | .dmypy.json 379 | dmypy.json 380 | 381 | # Pyre type checker 382 | .pyre/ 383 | 384 | # pytype static type analyzer 385 | .pytype/ 386 | 387 | # profiling data 388 | .prof 389 | 390 | ### Rust ### 391 | # Generated by Cargo 392 | # will have compiled files and executables 393 | /target/ 394 | 395 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 396 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 397 | Cargo.lock 398 | 399 | # End of https://www.toptal.com/developers/gitignore/api/node,macos,code,python,jetbrains,rust 400 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL in 3 milliseconds 2 | 3 | Hey! This is Poom's talk at GraphQL Bangkok 9.0.0, "GraphQL in 3 milliseconds.", where I demo how to use Juniper, Rust and Subscriptions to make a super fast GraphQL server. 4 | 5 | The code sample is in `crusty-api/`, while my talk's slide is in `slide/`. 6 | 7 | Have fun! 8 | -------------------------------------------------------------------------------- /crusty-api/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node,macos,code,python,jetbrains,rust 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,code,python,jetbrains,rust 4 | 5 | ### Code ### 6 | .vscode/* 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | *.code-workspace 10 | 11 | ### JetBrains ### 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 13 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 14 | 15 | # User-specific stuff 16 | .idea/**/workspace.xml 17 | .idea/**/tasks.xml 18 | .idea/**/usage.statistics.xml 19 | .idea/**/dictionaries 20 | .idea/**/shelf 21 | 22 | # Generated files 23 | .idea/**/contentModel.xml 24 | 25 | # Sensitive or high-churn files 26 | .idea/**/dataSources/ 27 | .idea/**/dataSources.ids 28 | .idea/**/dataSources.local.xml 29 | .idea/**/sqlDataSources.xml 30 | .idea/**/dynamic.xml 31 | .idea/**/uiDesigner.xml 32 | .idea/**/dbnavigator.xml 33 | 34 | # Gradle 35 | .idea/**/gradle.xml 36 | .idea/**/libraries 37 | 38 | # Gradle and Maven with auto-import 39 | # When using Gradle or Maven with auto-import, you should exclude module files, 40 | # since they will be recreated, and may cause churn. Uncomment if using 41 | # auto-import. 42 | # .idea/artifacts 43 | # .idea/compiler.xml 44 | # .idea/jarRepositories.xml 45 | # .idea/modules.xml 46 | # .idea/*.iml 47 | # .idea/modules 48 | # *.iml 49 | # *.ipr 50 | 51 | # CMake 52 | cmake-build-*/ 53 | 54 | # Mongo Explorer plugin 55 | .idea/**/mongoSettings.xml 56 | 57 | # File-based project format 58 | *.iws 59 | 60 | # IntelliJ 61 | out/ 62 | 63 | # mpeltonen/sbt-idea plugin 64 | .idea_modules/ 65 | 66 | # JIRA plugin 67 | atlassian-ide-plugin.xml 68 | 69 | # Cursive Clojure plugin 70 | .idea/replstate.xml 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | # Editor-based Rest Client 79 | .idea/httpRequests 80 | 81 | # Android studio 3.1+ serialized cache file 82 | .idea/caches/build_file_checksums.ser 83 | 84 | ### JetBrains Patch ### 85 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 86 | 87 | # *.iml 88 | # modules.xml 89 | # .idea/misc.xml 90 | # *.ipr 91 | 92 | # Sonarlint plugin 93 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 94 | .idea/**/sonarlint/ 95 | 96 | # SonarQube Plugin 97 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 98 | .idea/**/sonarIssues.xml 99 | 100 | # Markdown Navigator plugin 101 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 102 | .idea/**/markdown-navigator.xml 103 | .idea/**/markdown-navigator-enh.xml 104 | .idea/**/markdown-navigator/ 105 | 106 | # Cache file creation bug 107 | # See https://youtrack.jetbrains.com/issue/JBR-2257 108 | .idea/$CACHE_FILE$ 109 | 110 | # CodeStream plugin 111 | # https://plugins.jetbrains.com/plugin/12206-codestream 112 | .idea/codestream.xml 113 | 114 | ### macOS ### 115 | # General 116 | .DS_Store 117 | .AppleDouble 118 | .LSOverride 119 | 120 | # Icon must end with two \r 121 | Icon 122 | 123 | # Thumbnails 124 | ._* 125 | 126 | # Files that might appear in the root of a volume 127 | .DocumentRevisions-V100 128 | .fseventsd 129 | .Spotlight-V100 130 | .TemporaryItems 131 | .Trashes 132 | .VolumeIcon.icns 133 | .com.apple.timemachine.donotpresent 134 | 135 | # Directories potentially created on remote AFP share 136 | .AppleDB 137 | .AppleDesktop 138 | Network Trash Folder 139 | Temporary Items 140 | .apdisk 141 | 142 | ### Node ### 143 | # Logs 144 | logs 145 | *.log 146 | npm-debug.log* 147 | yarn-debug.log* 148 | yarn-error.log* 149 | lerna-debug.log* 150 | 151 | # Diagnostic reports (https://nodejs.org/api/report.html) 152 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 153 | 154 | # Runtime data 155 | pids 156 | *.pid 157 | *.seed 158 | *.pid.lock 159 | 160 | # Directory for instrumented libs generated by jscoverage/JSCover 161 | lib-cov 162 | 163 | # Coverage directory used by tools like istanbul 164 | coverage 165 | *.lcov 166 | 167 | # nyc test coverage 168 | .nyc_output 169 | 170 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 171 | .grunt 172 | 173 | # Bower dependency directory (https://bower.io/) 174 | bower_components 175 | 176 | # node-waf configuration 177 | .lock-wscript 178 | 179 | # Compiled binary addons (https://nodejs.org/api/addons.html) 180 | build/Release 181 | 182 | # Dependency directories 183 | node_modules/ 184 | jspm_packages/ 185 | 186 | # TypeScript v1 declaration files 187 | typings/ 188 | 189 | # TypeScript cache 190 | *.tsbuildinfo 191 | 192 | # Optional npm cache directory 193 | .npm 194 | 195 | # Optional eslint cache 196 | .eslintcache 197 | 198 | # Microbundle cache 199 | .rpt2_cache/ 200 | .rts2_cache_cjs/ 201 | .rts2_cache_es/ 202 | .rts2_cache_umd/ 203 | 204 | # Optional REPL history 205 | .node_repl_history 206 | 207 | # Output of 'npm pack' 208 | *.tgz 209 | 210 | # Yarn Integrity file 211 | .yarn-integrity 212 | 213 | # dotenv environment variables file 214 | .env 215 | .env.test 216 | .env*.local 217 | 218 | # parcel-bundler cache (https://parceljs.org/) 219 | .cache 220 | .parcel-cache 221 | 222 | # Next.js build output 223 | .next 224 | 225 | # Nuxt.js build / generate output 226 | .nuxt 227 | dist 228 | 229 | # Gatsby files 230 | .cache/ 231 | # Comment in the public line in if your project uses Gatsby and not Next.js 232 | # https://nextjs.org/blog/next-9-1#public-directory-support 233 | # public 234 | 235 | # vuepress build output 236 | .vuepress/dist 237 | 238 | # Serverless directories 239 | .serverless/ 240 | 241 | # FuseBox cache 242 | .fusebox/ 243 | 244 | # DynamoDB Local files 245 | .dynamodb/ 246 | 247 | # TernJS port file 248 | .tern-port 249 | 250 | # Stores VSCode versions used for testing VSCode extensions 251 | .vscode-test 252 | 253 | ### Python ### 254 | # Byte-compiled / optimized / DLL files 255 | __pycache__/ 256 | *.py[cod] 257 | *$py.class 258 | 259 | # C extensions 260 | *.so 261 | 262 | # Distribution / packaging 263 | .Python 264 | build/ 265 | develop-eggs/ 266 | dist/ 267 | downloads/ 268 | eggs/ 269 | .eggs/ 270 | lib/ 271 | lib64/ 272 | parts/ 273 | sdist/ 274 | var/ 275 | wheels/ 276 | pip-wheel-metadata/ 277 | share/python-wheels/ 278 | *.egg-info/ 279 | .installed.cfg 280 | *.egg 281 | MANIFEST 282 | 283 | # PyInstaller 284 | # Usually these files are written by a python script from a template 285 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 286 | *.manifest 287 | *.spec 288 | 289 | # Installer logs 290 | pip-log.txt 291 | pip-delete-this-directory.txt 292 | 293 | # Unit test / coverage reports 294 | htmlcov/ 295 | .tox/ 296 | .nox/ 297 | .coverage 298 | .coverage.* 299 | nosetests.xml 300 | coverage.xml 301 | *.cover 302 | *.py,cover 303 | .hypothesis/ 304 | .pytest_cache/ 305 | pytestdebug.log 306 | 307 | # Translations 308 | *.mo 309 | *.pot 310 | 311 | # Django stuff: 312 | local_settings.py 313 | db.sqlite3 314 | db.sqlite3-journal 315 | 316 | # Flask stuff: 317 | instance/ 318 | .webassets-cache 319 | 320 | # Scrapy stuff: 321 | .scrapy 322 | 323 | # Sphinx documentation 324 | docs/_build/ 325 | doc/_build/ 326 | 327 | # PyBuilder 328 | target/ 329 | 330 | # Jupyter Notebook 331 | .ipynb_checkpoints 332 | 333 | # IPython 334 | profile_default/ 335 | ipython_config.py 336 | 337 | # pyenv 338 | .python-version 339 | 340 | # pipenv 341 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 342 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 343 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 344 | # install all needed dependencies. 345 | #Pipfile.lock 346 | 347 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 348 | __pypackages__/ 349 | 350 | # Celery stuff 351 | celerybeat-schedule 352 | celerybeat.pid 353 | 354 | # SageMath parsed files 355 | *.sage.py 356 | 357 | # Environments 358 | .venv 359 | env/ 360 | venv/ 361 | ENV/ 362 | env.bak/ 363 | venv.bak/ 364 | pythonenv* 365 | 366 | # Spyder project settings 367 | .spyderproject 368 | .spyproject 369 | 370 | # Rope project settings 371 | .ropeproject 372 | 373 | # mkdocs documentation 374 | /site 375 | 376 | # mypy 377 | .mypy_cache/ 378 | .dmypy.json 379 | dmypy.json 380 | 381 | # Pyre type checker 382 | .pyre/ 383 | 384 | # pytype static type analyzer 385 | .pytype/ 386 | 387 | # profiling data 388 | .prof 389 | 390 | ### Rust ### 391 | # Generated by Cargo 392 | # will have compiled files and executables 393 | /target/ 394 | 395 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 396 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 397 | Cargo.lock 398 | 399 | # End of https://www.toptal.com/developers/gitignore/api/node,macos,code,python,jetbrains,rust 400 | -------------------------------------------------------------------------------- /crusty-api/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /crusty-api/.idea/crusty-api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /crusty-api/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /crusty-api/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /crusty-api/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /crusty-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crusty-api" 3 | version = "0.1.0" 4 | authors = ["Phoomparin Mano "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | #actix-web = "3.2.0" 11 | #actix-cors = "0.5.3" 12 | reqwest = "0.10.9" 13 | 14 | actix-web = "3.3" 15 | actix-cors = "0.5.3" 16 | futures = "0.3" 17 | tokio = { version = "0.2", features = ["macros", "rt-core"] } 18 | env_logger = "0.8" 19 | #serde = "1.0" 20 | #serde_json = "1.0" 21 | rand = "0.7" 22 | 23 | #juniper = "0.15.1" 24 | 25 | juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" } 26 | juniper_actix = { git = "https://github.com/graphql-rust/juniper", features = ["subscriptions"], branch = "master" } 27 | juniper_subscriptions = { git = "https://github.com/graphql-rust/juniper", branch = "master" } 28 | 29 | juniper_graphql_ws = { git = "https://github.com/graphql-rust/juniper", branch = "master" } 30 | 31 | openssl = "0.10.29" 32 | 33 | serde = "1.0.60" 34 | serde_json = "1.0.60" 35 | chrono = "0.4" 36 | 37 | lazy_static = "1.4.0" 38 | hashbrown = "0.9" 39 | #tokio = { version = "0.2", features = ["time", "macros", "rt-core"] } 40 | -------------------------------------------------------------------------------- /crusty-api/load-test.js: -------------------------------------------------------------------------------- 1 | import {check, sleep} from "k6" 2 | import http from "k6/http" 3 | 4 | const query = ` 5 | query Crustacean { 6 | crabs { 7 | level 8 | amount 9 | } 10 | 11 | lobsters { 12 | level 13 | amount 14 | } 15 | }` 16 | 17 | export default function loadTest() { 18 | const url = "http://localhost:8080/graphql" 19 | const body = JSON.stringify({query}) 20 | const headers = {"Content-Type": "application/json"} 21 | 22 | const res = http.post(url, body, {headers}) 23 | console.log("Response Time =", res.timings.duration, "ms") 24 | 25 | check(res, {"is status 200": (r) => r.status === 200}) 26 | sleep(0.3) 27 | } 28 | -------------------------------------------------------------------------------- /crusty-api/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(async_closure)] 2 | 3 | #[macro_use] 4 | extern crate lazy_static; 5 | extern crate juniper; 6 | 7 | mod models; 8 | mod services; 9 | mod routes; 10 | 11 | use actix_cors::Cors; 12 | use actix_web::{http, middleware::Compress, App, HttpServer}; 13 | 14 | use std::sync::Arc; 15 | 16 | use services::schema::create_schema; 17 | use routes::graphql::controller::graphql_route; 18 | use routes::landing::controller::landing_route; 19 | 20 | #[actix_web::main] 21 | async fn main() -> std::io::Result<()> { 22 | let schema = Arc::new(create_schema()); 23 | 24 | HttpServer::new(move || { 25 | // Allow fetch requests from any domain. 26 | let cors = Cors::default() 27 | .allow_any_origin() 28 | .send_wildcard() 29 | .allowed_methods(vec!["GET", "POST"]) 30 | .allowed_headers(vec![http::header::CONTENT_TYPE, http::header::ACCEPT]) 31 | .max_age(86400); 32 | 33 | App::new() 34 | .wrap(cors) 35 | .wrap(Compress::default()) 36 | .data(schema.clone()) 37 | .configure(landing_route) 38 | .configure(graphql_route) 39 | }) 40 | .bind("0.0.0.0:8080")? 41 | .run() 42 | .await 43 | } 44 | -------------------------------------------------------------------------------- /crusty-api/src/models/game.rs: -------------------------------------------------------------------------------- 1 | use juniper::GraphQLObject; 2 | use serde::Serialize; 3 | 4 | #[derive(Serialize, GraphQLObject)] 5 | pub struct Crustacean { 6 | #[graphql(description = "Amount of crustaceans")] 7 | pub amount: i32, 8 | 9 | #[graphql(description = "The level of crustaceans")] 10 | pub level: i32, 11 | } 12 | -------------------------------------------------------------------------------- /crusty-api/src/models/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod game; 2 | -------------------------------------------------------------------------------- /crusty-api/src/routes/graphql/controller.rs: -------------------------------------------------------------------------------- 1 | #![feature(async_closure)] 2 | 3 | use std::sync::Arc; 4 | 5 | use actix_web::{Error, HttpResponse, web, get, post, Result, web::ServiceConfig, HttpRequest}; 6 | 7 | use juniper::http::GraphQLRequest; 8 | use juniper::http::graphiql::graphiql_source; 9 | 10 | use crate::services::schema::Schema; 11 | use juniper_graphql_ws::ConnectionConfig; 12 | use tokio::time::Duration; 13 | use juniper_actix::subscriptions::subscriptions_handler; 14 | 15 | #[get("/graphiql")] 16 | pub async fn graphiql() -> HttpResponse { 17 | let source = graphiql_source( 18 | "/graphql", 19 | Some("/subscriptions")); 20 | 21 | HttpResponse::Ok() 22 | .content_type("text/html; charset=utf-8") 23 | .body(source) 24 | } 25 | 26 | #[post("/graphql")] 27 | pub async fn graphql( 28 | data: web::Data>, 29 | request: web::Json 30 | ) -> Result { 31 | let res = request.execute(&data, &()).await; 32 | 33 | Ok(HttpResponse::Ok() 34 | .json(res) 35 | ) 36 | } 37 | 38 | #[get("/subscriptions")] 39 | async fn subscriptions( 40 | schema: web::Data>, 41 | request: HttpRequest, 42 | stream: web::Payload, 43 | ) -> Result { 44 | let config = ConnectionConfig::new(()); 45 | 46 | // set the keep alive interval to 15 secs so that it doesn't timeout in playground 47 | // playground has a hard-coded timeout set to 20 secs 48 | let config = config.with_keep_alive_interval(Duration::from_secs(15)); 49 | 50 | let rootNode = (*schema.into_inner()).clone(); 51 | 52 | subscriptions_handler(request, stream, rootNode, config).await 53 | } 54 | 55 | pub fn graphql_route(config: &mut ServiceConfig) { 56 | config 57 | .service(graphiql) 58 | .service(graphql) 59 | .service(subscriptions); 60 | } 61 | -------------------------------------------------------------------------------- /crusty-api/src/routes/graphql/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod controller; -------------------------------------------------------------------------------- /crusty-api/src/routes/landing/controller.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{ get, Responder, web::ServiceConfig }; 2 | 3 | #[get("/")] 4 | async fn landing() -> impl Responder { 5 | "Crustacean API is ready!" 6 | } 7 | 8 | pub fn landing_route(config: &mut ServiceConfig) { 9 | config.service(landing); 10 | } 11 | -------------------------------------------------------------------------------- /crusty-api/src/routes/landing/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod controller; -------------------------------------------------------------------------------- /crusty-api/src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod graphql; 2 | pub mod landing; 3 | -------------------------------------------------------------------------------- /crusty-api/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod query; 2 | pub mod schema; 3 | pub mod subscriptions; -------------------------------------------------------------------------------- /crusty-api/src/services/query.rs: -------------------------------------------------------------------------------- 1 | use juniper::graphql_object; 2 | use crate::models::game::Crustacean; 3 | 4 | pub struct Query; 5 | 6 | #[graphql_object] 7 | impl Query { 8 | pub async fn crabs() -> Crustacean { 9 | Crustacean { 10 | level: 50, 11 | amount: 50, 12 | } 13 | } 14 | 15 | pub async fn lobsters() -> Crustacean { 16 | Crustacean { 17 | level: 30, 18 | amount: 30, 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crusty-api/src/services/schema.rs: -------------------------------------------------------------------------------- 1 | use crate::services::query::Query; 2 | 3 | use juniper::{EmptyMutation, EmptySubscription, RootNode}; 4 | use crate::services::subscriptions::Subscription; 5 | 6 | pub type Schema = RootNode<'static, Query, EmptyMutation, Subscription>; 7 | 8 | pub fn create_schema() -> Schema { 9 | Schema::new(Query {}, EmptyMutation::new(), Subscription {}) 10 | } 11 | -------------------------------------------------------------------------------- /crusty-api/src/services/subscriptions.rs: -------------------------------------------------------------------------------- 1 | use std::{env, pin::Pin, time::Duration}; 2 | use juniper::{futures, FieldError}; 3 | use juniper::graphql_object; 4 | use juniper::graphql_subscription; 5 | 6 | use crate::models::game::Crustacean; 7 | 8 | use juniper::futures::StreamExt; 9 | 10 | type CrustaceanStream = 11 | Pin 13 | > + Send>>; 14 | 15 | 16 | pub struct Subscription; 17 | 18 | #[graphql_subscription] 19 | impl Subscription { 20 | #[graphql(description = "Get the Crab's level in real-time.")] 21 | async fn crabs() -> CrustaceanStream { 22 | let mut level = 0; 23 | let mut amount = 0; 24 | 25 | let delay = Duration::from_millis(10); 26 | 27 | let stream = tokio::time::interval(delay) 28 | .map(move |_| { 29 | level += 1; 30 | amount += 1111; 31 | 32 | Ok(Crustacean { level, amount }) 33 | }); 34 | 35 | Box::pin(stream) 36 | } 37 | } -------------------------------------------------------------------------------- /slide/GraphQL in 3ms.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heypoom/graphql-in-3ms/b8f6ff813e5b6c8783be128c58dea73e04178e6a/slide/GraphQL in 3ms.pdf --------------------------------------------------------------------------------