Getting Started
To quickly get started with GQLoom, we will build a simple GraphQL backend application together.
We will build a cattery application and provide a GraphQL API to the outside. This application will include some simple functions:
- Cat basic information management: Enter the basic information of cats, including name, birthday, etc., update, delete and query cats;
- User (cat owner) registration management: Enter user information, a simple login function, and view one's own or other users' cats;
We will use the following technologies:
- TypeScript: As our development language;
- Node.js: As the runtime of our application;
- graphql.js: The JavaScript implementation of GraphQL;
- GraphQL Yoga: A comprehensive GraphQL HTTP adapter;
- Drizzle ORM: A fast and type-safe ORM that helps us operate the database;
- Valibot or Zod: Used to define and validate inputs;
GQLoom
: Allows us to define GraphQL Schema comfortably and efficiently and write resolvers;
Prerequisites
We only need to install Node.js version 20 or higher to run our application.
Create the Application
Project Structure
Our application will have the following structure:
Among them, the functions of each folder or file under the src
directory are as follows:
contexts
: Store contexts, such as the current user;providers
: Store functions that need to interact with external services, such as database connections and Redis connections;resolvers
: Store GraphQL resolvers;schema
: Store the schema, mainly the database table structure;services
: Store business logic, such as user login, user registration, etc.;index.ts
: Used to run the GraphQL application in the form of an HTTP service;
GQLoom has no requirements for the project's file structure. Here is just for reference. In practice, you can organize the files according to your needs and preferences.
Initialize the Project
First, let's create a new folder and initialize the project:
Then, we will install some necessary dependencies to run a TypeScript application in Node.js:
Next, we will install GQLoom and related dependencies. We can choose Valibot or Zod to define and validate inputs:
Hello World
Let's write our first resolver:
We need to weave this resolver into a GraphQL Schema and run it as an HTTP server:
Great, we have already created a simple GraphQL application.
Next, let's try to run this application. Add the dev
script to the package.json
:
Now let's run it:
Open http://localhost:4000/graphql in the browser and you can see the GraphQL playground. Let's try to send a GraphQL query. Enter the following in the playground:
Click the query button, and you can see the result:
So far, we have created the simplest GraphQL application.
Next, we will use Drizzle ORM to interact with the database and add complete functions.
Initialize the Database and Tables
First, let's install Drizzle ORM. We will use it to operate the SQLite database.
Define Database Tables
Next, define the database tables in the src/schema/index.ts
file. We will define two tables, users
and cats
, and establish the relationship between them:
Initialize the Database
We need to create a configuration file:
Then we run the drizzle-kit push
command to create the defined tables in the database:
Use the Database
To use the database in the application, we need to create a database instance:
Let's first create a user service, which will contain a series of operations on the user table.
We will implement the user service in the src/services/user.ts
file and export the entire user.ts
as userService
in the src/resolvers/index.ts
file:
Resolvers
Now, we can use the user service in the resolver. We will create a user resolver and add the following operations:
usersByName
: Find users by nameuserByPhone
: Find users by phone numbercreateUser
: Create a user
After completing the user resolver, we also need to add it to the resolvers
in the src/resolvers/index.ts
file:
Great, now let's try it in the playground:
Let's continue to try to retrieve the user we just created:
Current User Context
Next, let's try to add a simple login function and add a query operation to the user resolver:
mine
: Return the current user information
To implement this query, we first need to have a login function. Let's write a simple one:
In the above code, we created a context function for getting the current user, which will return the information of the current user. We use createMemoization()
to memoize this function, which ensures that this function is only executed once within the same request to avoid unnecessary database queries.
We used useContext()
to get the context provided by Yoga, and obtained the user's phone number from the request header, and found the user according to the phone number. If the user does not exist, a GraphQLError
will be thrown.
As you can see, this login function is very simple and is only used for demonstration purposes, and it does not guarantee security at all. In practice, it is usually recommended to use solutions such as session
or jwt
.
Now, we add the new query operation in the resolver:
If we directly call this new query in the playground, the application will give us an unauthorized error:
Open the Headers
at the bottom of the playground and add the authorization
field to the request header. Here we use the phone number of Bob
created in the previous step, so we are logged in as Bob
:
Resolver Factory
Next, we will add the business logic related to cats.
We use the resolver factory to quickly create interfaces:
In the above code, we used drizzleResolverFactory()
to create catResolverFactory
for quickly building resolvers.
We added a query that uses catResolverFactory
to select data and named it cats
. This query will provide full query operations on the cats
table.
In addition, we also added an additional age
field for cats to get the age of the cats.
Next, let's try to add a createCat
mutation. We want only logged-in users to access this interface, and the created cats will belong to the current user:
In the above code, we used catResolverFactory
to create a mutation that adds more data to the cats
table, and we overwrote the input of this mutation. When validating the input, we used useCurrentUser()
to get the ID of the currently logged-in user and pass it as the value of ownerId
to the cats
table.
Now let's try to add a few cats in the playground:
Let's use the cats
query to confirm the data in the database again:
Associated Objects
We want to be able to get the owner of a cat when querying the cat, and also be able to get all the cats of a user when querying the user.
This is very easy to achieve in GraphQL.
Let's add an additional owner
field to cats
and an additional cats
field to users
:
In the above code, we used the resolver factory to create the owner
field for cats
; similarly, we also created the cats
field for users
.
Behind the scenes, the relationship fields created by the resolver factory will use DataLoader
to query from the database to avoid the N+1 problem.
Let's try to query the owner of a cat in the playground:
Let's try to query the cats of the current user:
Conclusion
In this article, we created a simple GraphQL server-side application. We used the following tools:
Valibot
orZod
: Used to define and validate inputs;Drizzle
: Used to operate the database, and directly use theDrizzle
table as theGraphQL
output type;- Context: Used to share data between different parts of the program, which is very useful for scenarios such as implementing login and tracking logs;
- Resolver factory: Used to quickly create resolvers and operations;
GraphQL Yoga
: Used to create a GraphQL HTTP service and provides a GraphiQL playground;
Our application has implemented the functions of adding and querying users
and cats
, but due to space limitations, the update and delete functions have not been implemented. They can be quickly added through the resolver factory.
Next Steps
- Check out the core concepts of GQLoom: Silk, Resolver, Weave;
- Learn about common functions: Context, DataLoader, Middleware
- Add a GraphQL client to the front-end project: gql.tada, Urql, Apollo Client, TanStack Query, Graffle