Tutorial - GraphQL
Tutorial - GraphQL
In this tutorial, we will create a chat app using the Skeet Framework GraphQL. This is a comprehensive cloud app development tutorial that includes the programming language TypeScript, GraphQL, and GitHub.
In this tutorial, we will create a basic chatbot app. In the Quick Start, you learned the basics of using Skeet Framework GraphQL, but in this tutorial, you will learn how the features of the Skeet Framework can make things that were not easy before, easy. We express our deep gratitude to the developers who have published the library as open source.
The Skeet Framework is designed to allow developers to achieve more with less code by efficiently using computer resources. Furthermore, in today's world where environmental issues are becoming serious, we believe that it is the responsibility of developers to use energy efficiently.
The techniques you will learn in this tutorial are basic for any Skeet Framework app and mastering them will give you a deep understanding of Skeet.
In this chapter, we will add new features to the chatbot app using the machine learning (OpenAI) API that we created in the Quick Start.
Tutorial Objectives
In this tutorial, you will learn how to:
- Define an RDB schema
- Execute database migrations
- Generate GraphQL files using Scaffold
- Obtain a development login authentication key
- Send API requests using the GraphQL Playground
- Deploy to Cloud Run## Prerequisites for the Tutorial
Please complete the Quick Start if you have not done so already.
Development Environment
Skeet Framework recommends VScode or Cursor as the editor. By proceeding with development according to the framework, Get powerful code completion support using GitHub Copilot and OpenAI.
For chatbots, we use the OpenAI API.
Skeet GraphQL recommends schema-driven development. With schema-driven development, you can minimize what developers need to focus on by defining a schema.
For RDBMS, we use PostgreSQL or MySQL. For ORM, we use Prisma.
- PostgreSQL
- MySQL
- Prisma## Installation of Skeet CLI
Skeet CLI is a command-line tool for efficiently using the Skeet framework. You can install it with the following command.
$ npm i -g @skeet-framework/cli
Example of Vscode/Cursor settings
By adding the following settings to Vscode's settings.json, you can streamline development. If you use a cursor, you can import the VScode settings as is.
⚠️ These settings are just an example. ⚠️
{ "workbench.colorTheme": "One Monokai", "files.autoSave": "onFocusChange", "editor.tabSize": 2, "editor.wordWrap": "on", "explorer.confirmDelete": false, "editor.suggestSelection": "first", "editor.formatOnSave": true, "files.autoGuessEncoding": true, "launch": { "inputs": [], "configurations": [], "compounds": [] }, "indentRainbow.errorColor": "rgba(128,32,32,0)", "security.workspace.trust.untrustedFiles": "open", "json.schemas": [], "explorer.confirmDragAndDrop": false, "[dart]": { "editor.formatOnSave": true, "editor.formatOnType": true, "editor.rulers": [80], "editor.selectionHighlight": false, "editor.suggest.snippetsPreventQuickSuggestions": false, "editor.suggestSelection": "first", "editor.tabCompletion": "onlySnippets", "editor.wordBasedSuggestions": false }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnType": true, "terminal.integrated.defaultProfile.linux": "zsh", "terminal.integrated.enableMultiLinePasteWarning": false, "debug.disassemblyView.showSourceCode": false, "typescript.updateImportsOnFileMove.enabled": "always", "workbench.iconTheme": "material-icon-theme", "[prisma]": { "editor.defaultFormatter": "Prisma.prisma" }, "editor.inlineSuggest.enabled": true, "settingsSync.ignoredExtensions": [], "github.copilot.enable": { "*": true, "plaintext": false, "markdown": true, "scminput": false } }
The settings for the following files will be automatically set by the skeet create command.
- .eslintrc.json
- .eslintignore
- .prettierrc
- .prettierignore
- tsconfig.json
Defining RDB Schema
With Skeet Framework, you can automatically generate GraphQL schemas by defining RDB schemas.
The default model is defined in
graphql/prisma/schema.prisma
Sample models necessary for creating a chatbot app using OpenAI and VertexAI APIs are defined.
Running the skeet db generate command
Execute the following command to configure prisma and the database.
$ skeet db generate
Adding Models
You can add models directly to schema.prisma,
but by running the $ prisma mode of the skeet ai command,
you can automatically generate templates for Prisma models.
Also, by using the prettier-plugin-prisma plugin, you can automatically format Prisma schemas.
Running skeet ai in Prisma Mode
After running the skeet ai command, entering $ prisma will put you in Prisma Mode.
$ skeet ai ╔═════════════╤════════════════╗ │ Option │ Value │ ╟─────────────┼────────────────╢ │ AI Type │ VertexAI │ ╟─────────────┼────────────────╢ │ Model │ chat-bison@001 │ ╟─────────────┼────────────────╢ │ Max Tokens │ 1000 │ ╟─────────────┼────────────────╢ │ Temperature │ 0 │ ╚═════════════╧════════════════╝ VertexAI is selected 🤖 (type "q" to quit) You: $ prisma Skeet: 🤖 Prisma Scheme Generating Mode 🤖 Please describe your Database use case.
You can add models as follows.
You: $ prisma Skeet: 🤖 Prisma Scheme Generating Mode 🤖 Please describe your Database use case. You: I want to add a blog feature, so I want to add Post and Comment models. Skeet: How about this one? (Showing only the new parts of the models. prisma format (also there is vscode plugin) will add the relations automatically to the existing models.) ```prisma.schema model Post { id Int @id @default(autoincrement()) title String content String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt Comment Comment[] User User @relation(fields: [userId], references: [id]) userId Int @@unique([userId, title]) } model Comment { id Int @id @default(autoincrement()) content String postId Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt Post Post @relation(fields: [postId], references: [id]) User User @relation(fields: [userId], references: [id]) userId Int } ``` ❓ Is this schema good for you? (Yes/No)
In this way, you can use the skeet ai command to automatically generate model templates. If you are satisfied with this content, entering Yes will cause the AI to automatically generate and display the next necessary migration command from the schema content.
❓ Is this schema good for you? (Yes/No) yes Edit: ./graphql/prisma/schema.prisma Then run: skeet db migrate addPostAndComment ❓ Do you want me to run the migration now? (Yes/No)
Editing prisma.schema
With the skeet framework, you can create migration files using the skeet db migrate <migrationName> command. When you run the skeet ai command as above, it suggests name candidates by inferring from the new schema to add <migrationName>.
Copy the schema and paste it into schema.prisma. When you save, the format is automatically done, and the relations are automatically added.
Execute the skeet db migrate <migrationName> command
Next, if you input yes, the command will be executed, and a migration file will be created.
❓ Do you want me to run the migration now? (Yes/No) yes Environment variables loaded from .env Prisma schema loaded from prisma/schema.prisma Datasource "db": PostgreSQL database "skeet-api-dev", schema "public" at "localhost:5432" Applying migration `20230830074747_add_post_comment_and_user` The following migration(s) have been created and applied from new schema changes: migrations/ └─ 20230830074747_add_post_comment_and_user/ └─ migration.sql Your database is now in sync with your schema. Running generate... - Nexus Prisma ✔ Generated Prisma Client (v5.2.0) to ./node_modules/@prisma/client in 97ms ✔ Generated Nexus Prisma to ./node_modules/.nexus-prisma in 35ms Then run: skeet g scaffold ❓ Do you want me to run scaffold now? (Yes/No)
Running the skeet g scaffold command
Once the migration file is created, you can automatically generate a GraphQL API with CRUD functionality by executing the skeet g scaffold command.
❓ Do you want me to run scaffold now? (Yes/No) yes ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Post/mutation.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Post/model.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Post/query.ts 🎉 ✔ successfully created - ./graphql/src/graphql/modelManager/Post/index.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Comment/mutation.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Comment/model.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/Comment/query.ts 🎉 ✔ successfully created - ./graphql/src/graphql/modelManager/Comment/index.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/index.ts 🎉 ✔ successfully created ✔ - ./graphql/src/graphql/modelManager/index.ts 🎉
In this way, with Skeet GraphQL, you can automatically generate GraphQL schemas by defining the schema.
Opening the GraphQL Playground
Now, let's run the $ skeet s command to start the emulator.
You can also run the $ skeet command within the skeet ai prompt. -g is the option to start only GraphQL.
You: $ skeet s -g
If you have added a new GraphQL schema, it will be updated by running the $ skeet s command.
Open the GraphQL Playground and confirm that the schema has been updated.
In this way, you can test the GraphQL API from the Apollo Server Playground.
The GraphQL queries created here can be copied and used to create files in functions/skeet/src/queries. Later, you can use the skeetGraphql function to send API requests.
In Skeet Framework GraphQL, it is recommended to handle data-related processing in the GraphQL API, and to handle tasks and third-party API processing in functions.
To access the data of the GraphQL API from the instances in Functions, you can use the skeetGraphql function. This allows you to use the queries generated in the Apollo GraphQL Playground to access the GraphQL API directly.
The skeetGraphql function is included in the @skeet-framwork/utils package.
For detailed usage, please refer to the following document.
- @skeet-framework/utils## Obtaining Development Login Authentication Key
Let's get started with the development preparation. First, start the Firebase emulator and obtain the ACCESS_TOKEN.
$ skeet s
In a separate window, execute the following command to obtain the accessToken.
$ skeet login 🚸 === Copy & Paste below command to your terminal === 🚸 export ACCESS_TOKEN={accessToken} 🚸 ========= END ========= 🚸
By setting the accessToken displayed in the console log as an environment variable, you can send API requests using the skeetGraphql function.
When the login command is successful, the trigger of the Auth instance defined in authOnCreateUser.ts by default is activated, and user information is saved in Firebase Firestore.
You can confirm that the user information is saved by accessing the following URL.
functions/skeet/routings/auth/authOnCreateUser.ts
By default, a notification is sent to Discord when a user is created. By setting the DISCORD_WEBHOOK_URL of Discord as an environment variable, you can receive notifications.
import * as functions from 'firebase-functions/v1' import { authPublicOption } from '@/routings' import { gravatarIconUrl, sendDiscord, skeetGraphql, } from '@skeet-framework/utils' import skeetConfig from '../../../skeetOptions.json' import { defineSecret } from 'firebase-functions/params' import { inspect } from 'util' import { CreateUserQuery } from '@/queries' const DISCORD_WEBHOOK_URL = defineSecret('DISCORD_WEBHOOK_URL') const SKEET_GRAPHQL_ENDPOINT_URL = defineSecret('SKEET_GRAPHQL_ENDPOINT_URL') const { region } = skeetConfig export const authOnCreateUser = functions .runWith({ ...authPublicOption, secrets: [DISCORD_WEBHOOK_URL, SKEET_GRAPHQL_ENDPOINT_URL], }) .region(region) .auth.user() .onCreate(async (user) => { try { if (!user.email) throw new Error(`no email`) const { uid, email, displayName, photoURL } = user const accessToken = 'skeet-access-token' const variables = { uid: uid, email: email, username: displayName || email?.split('@')[0], iconUrl: photoURL == '' || !photoURL ? gravatarIconUrl(email ?? '[email protected]') : photoURL, } const createUserResponse = await skeetGraphql( accessToken, SKEET_GRAPHQL_ENDPOINT_URL.value(), CreateUserQuery, variables ) console.log( inspect(createUserResponse, false, null, true /* enable colors */) ) // Send Discord message when new user is created const content = `Skeet APP New user: ${variables.username} \nemail: ${variables.email}\niconUrl: ${variables.iconUrl}` if (process.env.NODE_ENV === 'production') { await sendDiscord(content) } console.log({ status: 'success' }) } catch (error) { console.log({ status: 'error', message: String(error) }) } })
After creating a user in Firestore, the user information is also saved in the relational database used by GraphQL with the same uid.
The skeetGraphql function is used for data exchange between GraphQL and Functions.
To access data from the GraphQL API from instances in Functions, you can use the skeetGraphql function to use the query generated in the Apollo GraphQL Playground to access the GraphQL API.
The skeetGraphql function is included in the @skeet-framwork/utils package.
For detailed usage, please refer to the following document.
In Skeet Framework GraphQL, it is recommended to handle data-related processing in the GraphQL API and handle tasks and third-party API processing in functions.
Retrieving User Information
User information can be retrieved from Firebase using
await getLoginUser(req)
import { getLoginUser } from '@/lib' const user: UserAuthType = await getLoginUser(req)
The return type definition of getLoginUser is by default as follows:
export type UserAuthType = { uid: string username: string email: string iconUrl: string }
Deploying to Cloud Run
$ skeet deploy
Skeet yarn build
The Skeet yarn build command allows you to build all functions by pressing the 'a' key.
$ skeet yarn build
Deploying Skeet Framework
There are two ways to deploy the Skeet Framework.
- CI/CD with GitHub Actions
- Deployment with Skeet CLI## CI/CD with GitHub Actions
$ git add . $ git commit -m "first deploy" $ git push origin main
Once you push to GitHub, the deployment will be carried out automatically by GitHub Actions.
⚠️ You must complete the Deploy for Production. ⚠️## Deploying with Skeet CLI
$ skeet deploy ? Select Services to run functions command (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed) = Services = ❯◯ graphql ◯ skeet
Select the service to deploy, only the selected service will be deployed. Press 'a' to select all services.
With this, the deployment of the Skeet Framework is complete 🎉 All that's left is to implement your ideas 🎉