├── .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
--------------------------------------------------------------------------------