├── src
├── react-app-env.d.ts
├── setupTests.ts
├── App.test.tsx
├── index.css
├── index.tsx
├── App.css
├── logo.svg
├── App.tsx
├── generated.tsx
└── serviceWorker.ts
├── api
├── .funcignore
├── graphql
│ ├── operations
│ │ ├── questions.graphql
│ │ └── answers.graphql
│ ├── function.json
│ ├── index.ts
│ ├── schema.graphql
│ ├── resolvers.ts
│ ├── data.ts
│ └── generated.ts
├── tsconfig.json
├── host.json
├── .gitignore
├── codegen.yml
├── package.json
├── trivia.json
└── graphql.schema.json
├── .editorconfig
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── .vscode
├── extensions.json
├── yml.code-snippets
├── launch.json
├── graphql.code-snippets
└── javascript.code-snippets
├── .gitignore
├── tsconfig.json
├── LICENSE
├── package.json
└── README.md
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/api/.funcignore:
--------------------------------------------------------------------------------
1 | *.js.map
2 | *.ts
3 | .git*
4 | .vscode
5 | local.settings.json
6 | test
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.*]
2 | indent_size = 2
3 | indent_style = space
4 | end_of_line = lf
5 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpowell/graphql-code-generator-sample/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpowell/graphql-code-generator-sample/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpowell/graphql-code-generator-sample/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/api/graphql/operations/questions.graphql:
--------------------------------------------------------------------------------
1 | query randomQuestion {
2 | getRandomQuestion {
3 | id
4 | question
5 | answers
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-azuretools.vscode-azurefunctions",
4 | "ms-azuretools.vscode-azurestaticwebapps"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/api/graphql/operations/answers.graphql:
--------------------------------------------------------------------------------
1 | mutation answerQuestion($id: ID, $answer: String) {
2 | answerQuestion(id: $id, answer: $answer) {
3 | correct
4 | correctAnswer
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/api/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "dist",
6 | "rootDir": ".",
7 | "sourceMap": true,
8 | "strict": false,
9 | "esModuleInterop": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render();
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/api/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | }
10 | },
11 | "extensionBundle": {
12 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
13 | "version": "[1.*, 2.0.0)"
14 | }
15 | }
--------------------------------------------------------------------------------
/api/graphql/function.json:
--------------------------------------------------------------------------------
1 | {
2 | "bindings": [
3 | {
4 | "authLevel": "function",
5 | "type": "httpTrigger",
6 | "direction": "in",
7 | "name": "req",
8 | "methods": ["get", "post"]
9 | },
10 | {
11 | "type": "http",
12 | "direction": "out",
13 | "name": "$return"
14 | }
15 | ],
16 | "scriptFile": "../dist/graphql/index.js"
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/api/graphql/index.ts:
--------------------------------------------------------------------------------
1 | import { ApolloServer } from "apollo-server-azure-functions";
2 | import { importSchema } from "graphql-import";
3 | import resolvers from "./resolvers";
4 | import { dataStore } from "./data";
5 |
6 | const server = new ApolloServer({
7 | typeDefs: importSchema("./graphql/schema.graphql"),
8 | resolvers,
9 | context: {
10 | dataStore,
11 | },
12 | });
13 |
14 | export default server.createHandler();
15 |
--------------------------------------------------------------------------------
/.vscode/yml.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "React Hooks": {
3 | "scope": "yaml",
4 | "prefix": "react",
5 | "body": [
6 | " ../src/generated.tsx:",
7 | " config:",
8 | " withHooks: true",
9 | " withHOC: false",
10 | " withComponent: false",
11 | " plugins:",
12 | " - \"typescript\"",
13 | " - \"typescript-react-apollo\"",
14 | " - \"typescript-operations\""
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/api/graphql/schema.graphql:
--------------------------------------------------------------------------------
1 | type Question {
2 | id: ID!
3 | question: String!
4 | correctAnswer: String!
5 | answers: [String!]!
6 | }
7 |
8 | type Query {
9 | question(id: ID!): Question
10 | getRandomQuestion: Question
11 | }
12 |
13 | type Answer {
14 | questionId: ID
15 | question: String!
16 | submittedAnswer: String!
17 | correctAnswer: String!
18 | correct: Boolean
19 | }
20 |
21 | type Mutation {
22 | answerQuestion(id: ID, answer: String): Answer
23 | }
24 |
25 | schema {
26 | query: Query
27 | mutation: Mutation
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want your app to work offline and load faster, you can change
15 | // unregister() to register() below. Note this comes with some pitfalls.
16 | // Learn more about service workers: https://bit.ly/CRA-PWA
17 | serviceWorker.unregister();
18 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "npm start",
6 | "name": "Run frontend",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | },
10 | {
11 | "command": "npm start",
12 | "name": "Run backend",
13 | "request": "launch",
14 | "type": "node-terminal",
15 | "cwd": "${workspaceFolder}/api"
16 | }
17 | ],
18 | "compounds": [
19 | {
20 | "name": "Run full stack",
21 | "configurations": ["Run frontend", "Run backend"]
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | obj
3 | csx
4 | .vs
5 | edge
6 | Publish
7 |
8 | *.user
9 | *.suo
10 | *.cscfg
11 | *.Cache
12 | project.lock.json
13 |
14 | /packages
15 | /TestResults
16 |
17 | /tools/NuGet.exe
18 | /App_Data
19 | /secrets
20 | /data
21 | .secrets
22 | appsettings.json
23 | local.settings.json
24 |
25 | node_modules
26 | dist
27 |
28 | # Local python packages
29 | .python_packages/
30 |
31 | # Python Environments
32 | .env
33 | .venv
34 | env/
35 | venv/
36 | ENV/
37 | env.bak/
38 | venv.bak/
39 |
40 | # Byte-compiled / optimized / DLL files
41 | __pycache__/
42 | *.py[cod]
43 | *$py.class
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/api/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: "./graphql/schema.graphql"
3 | documents: "./graphql/operations/*.graphql"
4 | generates:
5 | graphql/generated.ts:
6 | config:
7 | contextType: "./data#Context"
8 | avoidOptionals:
9 | field: false
10 | inputValue: false
11 | object: false
12 | mappers:
13 | Question: ./data#QuestionModel
14 | plugins:
15 | - "typescript"
16 | - "typescript-resolvers"
17 | ./graphql.schema.json:
18 | plugins:
19 | - "introspection"
20 | ../src/generated.tsx:
21 | config:
22 | withHooks: true
23 | withHOC: false
24 | withComponent: false
25 | plugins:
26 | - "typescript"
27 | - "typescript-react-apollo"
28 | - "typescript-operations"
29 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
40 | ul {
41 | list-style: none;
42 | }
43 |
--------------------------------------------------------------------------------
/.vscode/graphql.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "GraphQL types": {
3 | "scope": "graphql",
4 | "prefix": "types",
5 | "body": [
6 | "type Question {",
7 | "id: ID!",
8 | "question: String!",
9 | "correctAnswer: String!",
10 | "answers: [String!]!",
11 | "}"
12 | ]
13 | },
14 |
15 | "Schema": {
16 | "scope": "graphql",
17 | "prefix": "schema",
18 | "body": [
19 | "type Query {",
20 | "\tquestion(id: ID!): Question",
21 | "\tgetRandomQuestion: Question",
22 | "}",
23 | "",
24 | "schema {",
25 | "\tquery: Query",
26 | "}"
27 | ]
28 | },
29 |
30 | "Mutation": {
31 | "scope": "graphql",
32 | "prefix": "mutation",
33 | "body": [
34 | "type Answer {",
35 | "\tquestionId: ID",
36 | "\tquestion: String!",
37 | "\tsubmittedAnswer: String!",
38 | "\tcorrectAnswer: String!",
39 | "\tcorrect: Boolean",
40 | "}",
41 | "",
42 | "type Mutation {",
43 | "\tanswerQuestion(id: ID, answer: String): Answer",
44 | "}"
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Aaron Powell
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 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "version": "",
4 | "scripts": {
5 | "build": "tsc",
6 | "build:production": "npm run prestart && npm prune --production",
7 | "watch": "tsc --w",
8 | "prestart": "npm run build && func extensions install",
9 | "start:host": "func start",
10 | "start": "npm-run-all --parallel start:host watch",
11 | "test": "echo \"No tests yet...\"",
12 | "gen": "graphql-codegen --config codegen.yml"
13 | },
14 | "description": "",
15 | "devDependencies": {
16 | "@azure/functions": "^1.0.1-beta1",
17 | "@graphql-codegen/cli": "1.16.2",
18 | "azure-functions-core-tools": "^3.0.2630",
19 | "npm-run-all": "^4.1.5",
20 | "typescript": "^3.3.3",
21 | "@graphql-codegen/typescript": "1.16.2",
22 | "@graphql-codegen/typescript-resolvers": "1.16.2",
23 | "@graphql-codegen/introspection": "1.16.2"
24 | },
25 | "dependencies": {
26 | "@azure/cosmos": "^3.7.4",
27 | "@graphql-codegen/typescript-operations": "^1.16.2",
28 | "@graphql-codegen/typescript-react-apollo": "^1.16.2",
29 | "apollo-server-azure-functions": "^2.15.1",
30 | "axios": "^0.19.2",
31 | "graphql-import": "^1.0.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aswa-react-template",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@apollo/react-hooks": "^3.1.5",
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.5.0",
9 | "@testing-library/user-event": "^7.2.1",
10 | "@types/jest": "^24.9.1",
11 | "@types/node": "^12.12.47",
12 | "@types/react": "^16.9.41",
13 | "@types/react-dom": "^16.9.8",
14 | "apollo-boost": "^0.4.9",
15 | "graphql": "^15.3.0",
16 | "graphql-tag": "^2.10.3",
17 | "react": "^16.13.1",
18 | "react-dom": "^16.13.1",
19 | "react-scripts": "3.4.1",
20 | "typescript": "^3.7.5"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject"
27 | },
28 | "eslintConfig": {
29 | "extends": "react-app"
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/api/graphql/resolvers.ts:
--------------------------------------------------------------------------------
1 | import { Resolvers } from "./generated";
2 |
3 | const resolvers: Resolvers = {
4 | Query: {
5 | question(_, { id }, { dataStore }) {
6 | return dataStore.getQuestionById(id);
7 | },
8 | async getRandomQuestion(_, __, { dataStore }) {
9 | const questions = await dataStore.getQuestions();
10 | return questions[Math.floor(Math.random() * questions.length) + 1];
11 | },
12 | },
13 | Question: {
14 | answers(question) {
15 | return question.incorrect_answers
16 | .concat([question.correct_answer])
17 | .sort();
18 | },
19 | correctAnswer(question) {
20 | return question.correct_answer;
21 | },
22 | id(question) {
23 | return question.id;
24 | },
25 | question(question) {
26 | return question.question;
27 | },
28 | },
29 |
30 | Mutation: {
31 | async answerQuestion(_, { id, answer }, { dataStore }) {
32 | const question = await dataStore.getQuestionById(id);
33 |
34 | return {
35 | questionId: id,
36 | question: question.question,
37 | correctAnswer: question.correct_answer,
38 | submittedAnswer: answer,
39 | correct: question.correct_answer === answer,
40 | };
41 | },
42 | },
43 |
44 | Answer: {
45 | correct({ correct }) {
46 | return correct;
47 | },
48 | correctAnswer({ correctAnswer }) {
49 | return correctAnswer;
50 | },
51 | question({ question }) {
52 | return question;
53 | },
54 | questionId({ questionId }) {
55 | return questionId;
56 | },
57 | submittedAnswer({ submittedAnswer }) {
58 | return submittedAnswer;
59 | },
60 | },
61 | };
62 |
63 | export default resolvers;
64 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GraphQL Code Generator Sample App
2 |
3 | This is a sample application showing how to use [GraphQL Code Generator](https://graphql-code-generator.com/) to generate an the TypeScript definitions from a GraphQL schema, then how to combine that with a data store that uses a different data model. The backend storage model is [CosmosDB](https://azure.microsoft.com/services/cosmos-db/?WT.mc_id=javascript-0000-aapowell) by default, using the [`trivia.json`](api/trivia.json) data (the data was generated from [Open Trivia DB](https://opentdb.com)), but there is an in-memory store you can switch to by uncommenting code in `data.ts`.
4 |
5 | You can watch the video of the talk that this was presented at on the [BrisJS](https://brisjs.org) [YouTube Channel](https://youtu.be/p8aJqeX7TT4?t=2741).
6 |
7 | ## Azure Static Website React Template
8 |
9 | This repository contains a template for creating an [Azure Static Web App](https://docs.microsoft.com/azure/static-web-apps/?WT.mc_id=javascript-0000-aapowell) projects using React + TypeScript.
10 |
11 | In the template there is [Create React App](https://create-react-app.dev) site using TypeScript and an `api` folder with an empty [Azure Functions](https://docs.microsoft.com/azure/functions/?WT.mc_id=javascript-0000-aapowell), also using TypeScript.
12 |
13 | To get started, click the **Use this template** button to create a repository from this template, and check out the [GitHub docs on using templates](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template).
14 |
15 | ## Running The Application
16 |
17 | From a terminal run `npm start` from both the repository root and `api` folder to start the two servers, the web application will be on `http://localhost:3000` and the API on `http://localhost:7071`. Alternatively, you can use the VS Code launch of `Run full stack` to run both together with debuggers attached.
18 |
--------------------------------------------------------------------------------
/api/graphql/data.ts:
--------------------------------------------------------------------------------
1 | import { CosmosClient } from "@azure/cosmos";
2 |
3 | export type QuestionModel = {
4 | id: string;
5 | question: string;
6 | category: string;
7 | incorrect_answers: string[];
8 | correct_answer: string;
9 | type: string;
10 | difficulty: "easy" | "medium" | "hard";
11 | };
12 |
13 | interface DataStore {
14 | getQuestionById(id: string): Promise;
15 | getQuestions(): Promise;
16 | }
17 |
18 | class CosmosDataStore implements DataStore {
19 | #client: CosmosClient;
20 | #databaseName = "trivia";
21 | #containerName = "questions";
22 |
23 | #getContainer = () => {
24 | return this.#client
25 | .database(this.#databaseName)
26 | .container(this.#containerName);
27 | };
28 |
29 | constructor(client: CosmosClient) {
30 | this.#client = client;
31 | }
32 |
33 | async getQuestionById(id: string) {
34 | const container = this.#getContainer();
35 |
36 | const question = await container.items
37 | .query({
38 | query: "SELECT * FROM c WHERE c.id = @id",
39 | parameters: [{ name: "@id", value: id }],
40 | })
41 | .fetchAll();
42 |
43 | return question.resources[0];
44 | }
45 |
46 | async getQuestions() {
47 | const container = this.#getContainer();
48 |
49 | const question = await container.items
50 | .query({
51 | query: "SELECT * FROM c",
52 | })
53 | .fetchAll();
54 |
55 | return question.resources;
56 | }
57 | }
58 |
59 | class MockDataStore implements DataStore {
60 | #data: QuestionModel[];
61 | constructor() {
62 | this.#data = require("../../trivia.json");
63 | }
64 |
65 | getQuestionById(id: string): Promise {
66 | return Promise.resolve(this.#data.find((q) => q.id === id));
67 | }
68 | getQuestions(): Promise {
69 | return Promise.resolve(this.#data);
70 | }
71 | }
72 |
73 | export const dataStore = new CosmosDataStore(
74 | new CosmosClient(process.env.CosmosDB)
75 | );
76 |
77 | // export const dataStore = new MockDataStore();
78 |
79 | export type Context = {
80 | dataStore: DataStore;
81 | };
82 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import logo from "./logo.svg";
3 | import "./App.css";
4 | import {
5 | useAnswerQuestionMutation,
6 | useRandomQuestionLazyQuery,
7 | Question,
8 | } from "./generated";
9 | import ApolloClient from "apollo-boost";
10 |
11 | const client = new ApolloClient