---
url: /docs/getting-started.md
---
# Getting Started

This guide will help you get started with GQLoom and create a simple GraphQL backend application.

## Prerequisites

You only need a JavaScript/TypeScript runtime, such as Node.js, Bun, Deno, or Cloudflare Workers.

## Initialize the project

::: tip Tip
If you already have a project, you can skip this step and jump straight to [Installation](#installation).
:::

First, create a new folder and initialize the project:

::: code-group

```sh [npm]
mkdir gqloom-app # Create a new folder
cd ./gqloom-app # Enter the folder
npm init -y # Initialize an empty project

npm i -D typescript @types/node tsx # Install TypeScript and related dependencies
npx tsc --init # Initialize TypeScript configuration
```

```sh [pnpm]
mkdir gqloom-app # Create a new folder
cd ./gqloom-app # Enter the folder
yarn init -y # Initialize an empty project

yarn add -D typescript @types/node tsx # Install TypeScript and related dependencies
yarn dlx -q -p typescript tsc --init # Initialize TypeScript configuration
```

```sh [yarn]
mkdir gqloom-app # Create a new folder
cd ./gqloom-app # Enter the folder
pnpm init # Initialize an empty project

pnpm add -D typescript @types/node tsx # Install TypeScript and related dependencies
pnpm exec tsc --init # Initialize TypeScript configuration
```

```sh [bun]
mkdir gqloom-app # Create a new folder
cd ./gqloom-app # Enter the folder
bun init # Initialize the project
```

```sh [deno]
mkdir gqloom-app # Create a new folder
cd ./gqloom-app # Enter the folder
deno init # Initialize the project
```

:::

## Installation

GQLoom supports multiple runtime types, choose your favorite ORM and validation library!

::: code-group

```sh [npm]
npm i graphql @gqloom/core valibot @gqloom/valibot
```

```sh [pnpm]
pnpm add graphql @gqloom/core valibot @gqloom/valibot
```

```sh [yarn]
yarn add graphql @gqloom/core valibot @gqloom/valibot
```

```sh [bun]
bun add graphql @gqloom/core valibot @gqloom/valibot
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:valibot npm:@gqloom/valibot
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core zod @gqloom/zod
```

```sh [pnpm]
pnpm add graphql @gqloom/core zod @gqloom/zod
```

```sh [yarn]
yarn add graphql @gqloom/core zod @gqloom/zod
```

```sh [bun]
bun add graphql @gqloom/core zod @gqloom/zod
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:zod npm:@gqloom/zod
```

:::

Please refer to MikroORM's [Quick Start guide](https://mikro-orm.io/docs/quick-start) to install MikroORM and the corresponding database driver.

After completing the MikroORM installation, install `@gqloom/mikro-orm`:

::: code-group

```sh [npm]
npm i graphql @gqloom/core @gqloom/mikro-orm
```

```sh [pnpm]
pnpm add graphql @gqloom/core @gqloom/mikro-orm
```

```sh [yarn]
yarn add graphql @gqloom/core @gqloom/mikro-orm
```

```sh [bun]
bun add graphql @gqloom/core @gqloom/mikro-orm
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:@gqloom/mikro-orm
```

:::

Please refer to Drizzle's [Getting Started guide](https://orm.drizzle.team/docs/get-started) and [Upgrading to Drizzle v1](https://orm.drizzle.team/docs/upgrade-v1).

After completing the Drizzle installation, install `@gqloom/drizzle@rc`:

::: code-group

```sh [npm]
npm i graphql @gqloom/core drizzle-orm@rc @gqloom/drizzle@rc
```

```sh [pnpm]
pnpm add graphql @gqloom/core drizzle-orm@rc @gqloom/drizzle@rc
```

```sh [yarn]
yarn add graphql @gqloom/core drizzle-orm@rc @gqloom/drizzle@rc
```

```sh [bun]
bun add graphql @gqloom/core drizzle-orm@rc @gqloom/drizzle@rc
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:drizzle-orm@rc npm:@gqloom/drizzle@rc
```

:::

Please refer to Drizzle's [Getting Started guide](https://orm.drizzle.team/docs/get-started) to install Drizzle and the corresponding database integration.

After completing the Drizzle installation, install `@gqloom/drizzle`:

::: code-group

```sh [npm]
npm i graphql @gqloom/core @gqloom/drizzle
```

```sh [pnpm]
pnpm add graphql @gqloom/core @gqloom/drizzle
```

```sh [yarn]
yarn add graphql @gqloom/core @gqloom/drizzle
```

```sh [bun]
bun add graphql @gqloom/core @gqloom/drizzle
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:@gqloom/drizzle
```

:::

Please refer to Prisma's [documentation](https://www.prisma.io/docs) to install Prisma and the corresponding database driver.

::: code-group

```sh [npm]
npm i graphql @gqloom/core @gqloom/prisma
```

```sh [pnpm]
pnpm add graphql @gqloom/core @gqloom/prisma
```

```sh [yarn]
yarn add graphql @gqloom/core @gqloom/prisma
```

```sh [bun]
bun add graphql @gqloom/core @gqloom/prisma
```

```sh [deno]
deno add npm:graphql npm:prisma npm:@gqloom/core npm:@gqloom/prisma
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core yup @gqloom/yup
```

```sh [pnpm]
pnpm add graphql @gqloom/core yup @gqloom/yup
```

```sh [yarn]
yarn add graphql @gqloom/core yup @gqloom/yup
```

```sh [bun]
bun add graphql @gqloom/core yup @gqloom/yup
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:yup npm:@gqloom/yup
```

:::

Additionally, we need to declare GQLoom metadata for Yup in the project:

```ts [yup.d.ts]
import 'yup'
import { type GQLoomMetadata } from "@gqloom/yup"

declare module "yup" {
  export interface CustomSchemaMetadata extends GQLoomMetadata {}
}
```

::: code-group

```sh [npm]
npm i graphql @gqloom/core effect @gqloom/effect
```

```sh [pnpm]
pnpm add graphql @gqloom/core effect @gqloom/effect
```

```sh [yarn]
yarn add graphql @gqloom/core effect @gqloom/effect
```

```sh [bun]
bun add graphql @gqloom/core effect @gqloom/effect
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:effect npm:@gqloom/effect
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core @gqloom/json
```

```sh [pnpm]
pnpm add graphql @gqloom/core @gqloom/json
```

```sh [yarn]
yarn add graphql @gqloom/core @gqloom/json
```

```sh [bun]
bun add graphql @gqloom/core @gqloom/json
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:@gqloom/json
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core
```

```sh [pnpm]
pnpm add graphql @gqloom/core
```

```sh [yarn]
yarn add graphql @gqloom/core
```

```sh [bun]
bun add graphql @gqloom/core
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core typebox @gqloom/json
```

```sh [pnpm]
pnpm add graphql @gqloom/core typebox @gqloom/json
```

```sh [yarn]
yarn add graphql @gqloom/core typebox @gqloom/json
```

```sh [bun]
bun add graphql @gqloom/core typebox @gqloom/json
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:typebox npm:@gqloom/json
```

:::

::: code-group

```sh [npm]
npm i graphql @gqloom/core arktype @gqloom/json
```

```sh [pnpm]
pnpm add graphql @gqloom/core arktype @gqloom/json
```

```sh [yarn]
yarn add graphql @gqloom/core arktype @gqloom/json
```

```sh [bun]
bun add graphql @gqloom/core arktype @gqloom/json
```

```sh [deno]
deno add npm:graphql npm:@gqloom/core npm:arktype npm:@gqloom/json
```

:::

In addition, we need to choose an [adapter](./advanced/adapters/) to run our GraphQL server.\
Here we choose the [graphql-yoga](https://the-guild.dev/graphql/yoga-server) adapter.

::: code-group

```sh [npm]
npm i graphql-yoga
```

```sh [pnpm]
pnpm add graphql-yoga
```

```sh [yarn]
yarn add graphql-yoga
```

```sh [bun]
bun add graphql-yoga
```

```sh [deno]
deno add npm:graphql-yoga
```

:::

## Hello, World

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { ValibotWeaver } from "@gqloom/valibot"
import { createYoga } from "graphql-yoga"
import * as v from "valibot"

const helloResolver = resolver({
  hello: query(v.string())
    .input({ name: v.nullish(v.string(), "World") })
    .resolve(({ name }) => `Hello, ${name}!`),
})

const schema = weave(ValibotWeaver, helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { ZodWeaver } from "@gqloom/zod"
import { createYoga } from "graphql-yoga"
import * as z from "zod"

const helloResolver = resolver({
  hello: query(z.string())
    .input({
      name: z
        .string()
        .nullish()
        .transform((value) => value ?? "World"),
    })
    .resolve(({ name }) => `Hello, ${name}!`),
})

const schema = weave(ZodWeaver, helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

In GQLoom, the easiest way to use `MikroORM` is the [resolver factory](./schema/mikro-orm#resolver-factory),\
with just a few lines you can create a GraphQL app with full CRUD:

::: code-group
<<< @/snippets/home/mikro/index.ts{ts twoslash}

```ts twoslash
import { mikroSilk } from "@gqloom/mikro-orm"
import { defineEntity, type InferEntity } from "@mikro-orm/core"

const UserEntity = defineEntity({
  name: "User",
  properties: (p) => ({
    id: p.integer().primary().autoincrement(),
    createdAt: p.datetime().onCreate(() => new Date()),
    email: p.string(),
    name: p.string(),
    role: p.string().$type<"admin" | "user">().default("user"),
    posts: () => p.oneToMany(PostEntity).mappedBy("author"),
  }),
})
export interface IUser extends InferEntity<typeof UserEntity> {}

const PostEntity = defineEntity({
  name: "Post",
  properties: (p) => ({
    id: p.integer().primary().autoincrement(),
    createdAt: p.datetime().onCreate(() => new Date()),
    updatedAt: p
      .datetime()
      .onCreate(() => new Date())
      .onUpdate(() => new Date()),
    published: p.boolean().default(false),
    title: p.string(),
    author: () => p.manyToOne(UserEntity),
  }),
})
export interface IPost extends InferEntity<typeof PostEntity> {}

export const User = mikroSilk(UserEntity)
export const Post = mikroSilk(PostEntity)

```

```GraphQL
input BooleanComparisonOperators {
  """
  <@
  """
  contained: [Boolean!]

  """
  @>
  """
  contains: [Boolean!]

  """
  Equals. Matches values that are equal to a specified value.
  """
  eq: Boolean

  """
  Greater. Matches values that are greater than a specified value.
  """
  gt: Boolean

  """
  Greater or Equal. Matches values that are greater than or equal to a specified value.
  """
  gte: Boolean

  """
  Contains, Contains, Matches any of the values specified in an array.
  """
  in: [Boolean!]

  """
  Lower, Matches values that are less than a specified value.
  """
  lt: Boolean

  """
  Lower or equal, Matches values that are less than or equal to a specified value.
  """
  lte: Boolean

  """
  Not equal. Matches all values that are not equal to a specified value.
  """
  ne: Boolean

  """
  Not contains. Matches none of the values specified in an array.
  """
  nin: [Boolean!]

  """
  &&
  """
  overlap: [Boolean!]
}

input IDComparisonOperators {
  """
  <@
  """
  contained: [ID!]

  """
  @>
  """
  contains: [ID!]

  """
  Equals. Matches values that are equal to a specified value.
  """
  eq: ID

  """
  Greater. Matches values that are greater than a specified value.
  """
  gt: ID

  """
  Greater or Equal. Matches values that are greater than or equal to a specified value.
  """
  gte: ID

  """
  Contains, Contains, Matches any of the values specified in an array.
  """
  in: [ID!]

  """
  Lower, Matches values that are less than a specified value.
  """
  lt: ID

  """
  Lower or equal, Matches values that are less than or equal to a specified value.
  """
  lte: ID

  """
  Not equal. Matches all values that are not equal to a specified value.
  """
  ne: ID

  """
  Not contains. Matches none of the values specified in an array.
  """
  nin: [ID!]

  """
  &&
  """
  overlap: [ID!]
}

enum MikroOnConflictAction {
  ignore
  merge
}

type Mutation {
  createPost(data: PostRequiredInput!): Post!
  createUser(data: UserRequiredInput!): User!
  deletePost(where: PostFilter): Int!
  deleteUser(where: UserFilter): Int!
  insertManyPost(data: [PostRequiredInput]!): [Post!]!
  insertManyUser(data: [UserRequiredInput]!): [User!]!
  insertPost(data: PostRequiredInput!): Post!
  insertUser(data: UserRequiredInput!): User!
  updatePost(data: PostPartialInput!, where: PostFilter): Int!
  updateUser(data: UserPartialInput!, where: UserFilter): Int!
  upsertManyPost(
    data: [PostPartialInput!]!
    onConflictAction: MikroOnConflictAction
    onConflictExcludeFields: [String!]
    onConflictFields: [String!]
    onConflictMergeFields: [String!]
  ): [Post!]!
  upsertManyUser(
    data: [UserPartialInput!]!
    onConflictAction: MikroOnConflictAction
    onConflictExcludeFields: [String!]
    onConflictFields: [String!]
    onConflictMergeFields: [String!]
  ): [User!]!
  upsertPost(
    data: PostPartialInput!
    onConflictAction: MikroOnConflictAction
    onConflictExcludeFields: [String!]
    onConflictFields: [String!]
    onConflictMergeFields: [String!]
  ): Post!
  upsertUser(
    data: UserPartialInput!
    onConflictAction: MikroOnConflictAction
    onConflictExcludeFields: [String!]
    onConflictFields: [String!]
    onConflictMergeFields: [String!]
  ): User!
}

type Post {
  author: User
  createdAt: String!
  id: ID!
  published: Boolean!
  title: String!
  updatedAt: String!
}

type PostCursor {
  endCursor: String
  hasNextPage: Boolean!
  hasPrevPage: Boolean!
  items: [Post!]!
  length: Int
  startCursor: String
  totalCount: Int!
}

input PostFilter {
  """
  Joins query clauses with a logical AND returns all documents that match the conditions of both clauses.
  """
  AND: [PostFilter!]

  """
  Inverts the effect of a query expression and returns documents that do not match the query expression.
  """
  NOT: PostFilter

  """
  Joins query clauses with a logical OR returns all documents that match the conditions of either clause.
  """
  OR: [PostFilter!]
  createdAt: StringComparisonOperators
  id: IDComparisonOperators
  published: BooleanComparisonOperators
  title: StringComparisonOperators
  updatedAt: StringComparisonOperators
}

input PostOrderBy {
  createdAt: QueryOrder
  id: QueryOrder
  published: QueryOrder
  title: QueryOrder
  updatedAt: QueryOrder
}

input PostPartialInput {
  author: ID
  createdAt: String
  id: ID
  published: Boolean
  title: String
  updatedAt: String
}

input PostRequiredInput {
  author: ID!
  createdAt: String
  id: ID
  published: Boolean
  title: String!
  updatedAt: String
}

type Query {
  countPost(where: PostFilter): Int!
  countUser(where: UserFilter): Int!
  findOnePost(offset: Int, orderBy: PostOrderBy, where: PostFilter!): Post
  findOnePostOrFail(
    offset: Int
    orderBy: PostOrderBy
    where: PostFilter!
  ): Post!
  findOneUser(offset: Int, orderBy: UserOrderBy, where: UserFilter!): User
  findOneUserOrFail(
    offset: Int
    orderBy: UserOrderBy
    where: UserFilter!
  ): User!
  findPost(
    limit: Int
    offset: Int
    orderBy: PostOrderBy
    where: PostFilter
  ): [Post!]!
  findPostByCursor(
    after: String
    before: String
    first: Int
    last: Int
    orderBy: PostOrderBy
    where: PostFilter
  ): PostCursor
  findUser(
    limit: Int
    offset: Int
    orderBy: UserOrderBy
    where: UserFilter
  ): [User!]!
  findUserByCursor(
    after: String
    before: String
    first: Int
    last: Int
    orderBy: UserOrderBy
    where: UserFilter
  ): UserCursor
}

enum QueryOrder {
  ASC
  ASC_NULLS_FIRST
  ASC_NULLS_LAST
  DESC
  DESC_NULLS_FIRST
  DESC_NULLS_LAST
}

input StringComparisonOperators {
  """
  <@
  """
  contained: [String!]

  """
  @>
  """
  contains: [String!]

  """
  Equals. Matches values that are equal to a specified value.
  """
  eq: String

  """
  Full text.	A driver specific full text search function.
  """
  fulltext: String

  """
  Greater. Matches values that are greater than a specified value.
  """
  gt: String

  """
  Greater or Equal. Matches values that are greater than or equal to a specified value.
  """
  gte: String

  """
  ilike
  """
  ilike: String

  """
  Contains, Contains, Matches any of the values specified in an array.
  """
  in: [String!]

  """
  Like. Uses LIKE operator
  """
  like: String

  """
  Lower, Matches values that are less than a specified value.
  """
  lt: String

  """
  Lower or equal, Matches values that are less than or equal to a specified value.
  """
  lte: String

  """
  Not equal. Matches all values that are not equal to a specified value.
  """
  ne: String

  """
  Not contains. Matches none of the values specified in an array.
  """
  nin: [String!]

  """
  &&
  """
  overlap: [String!]

  """
  Regexp. Uses REGEXP operator
  """
  re: String
}

type User {
  createdAt: String!
  email: String!
  id: ID!
  name: String!
  posts(where: PostFilter): [Post!]!
  role: String!
}

type UserCursor {
  endCursor: String
  hasNextPage: Boolean!
  hasPrevPage: Boolean!
  items: [User!]!
  length: Int
  startCursor: String
  totalCount: Int!
}

input UserFilter {
  """
  Joins query clauses with a logical AND returns all documents that match the conditions of both clauses.
  """
  AND: [UserFilter!]

  """
  Inverts the effect of a query expression and returns documents that do not match the query expression.
  """
  NOT: UserFilter

  """
  Joins query clauses with a logical OR returns all documents that match the conditions of either clause.
  """
  OR: [UserFilter!]
  createdAt: StringComparisonOperators
  email: StringComparisonOperators
  id: IDComparisonOperators
  name: StringComparisonOperators
  role: StringComparisonOperators
}

input UserOrderBy {
  createdAt: QueryOrder
  email: QueryOrder
  id: QueryOrder
  name: QueryOrder
  role: QueryOrder
}

input UserPartialInput {
  createdAt: String
  email: String
  id: ID
  name: String
  posts: [ID]
  role: String
}

input UserRequiredInput {
  createdAt: String
  email: String!
  id: ID
  name: String!
  posts: [ID]
  role: String
}

```

We can also build resolvers from `MikroORM` entities:

```ts twoslash
// @paths: {"src/*": ["snippets/home/mikro/*"]}
import { field, query, resolver } from "@gqloom/core"
import { Post, User } from "src/entities"
import * as v from "valibot"

export const userResolver = resolver.of(User, {
  user: query(User.nullable()) // Declare a query that returns a user
    .input({ id: v.number() }) // This query accepts an `id` as parameter
    .resolve(async ({ id }) => {
      const em = await useEm()
      return await em.findOne(User, id)
    }),

  posts: field(Post.list()) // Declare a derived field that returns a list of posts
    .derivedFrom("posts") // This field depends on the `posts` field
    .resolve(async (user) => {
      return await user.posts.loadItems({ dataloader: true })
    }),
})

// ---cut-after---
import { createMemoization } from "@gqloom/core/context"
import { MikroORM } from "@mikro-orm/libsql"

const ormPromise = MikroORM.init({
  dbName: ":memory:",
  entities: [User, Post],
})
const useEm = createMemoization(async () => (await ormPromise).em.fork())

```

In GQLoom, the easiest way to use `Drizzle` is the [resolver factory](./schema/drizzle#resolver-factory),\
with just a few lines you can create a GraphQL app with full CRUD:

::: code-group
<<< @/snippets/home/drizzle/index.ts{ts twoslash}

```ts twoslash
import { drizzleSilk } from "@gqloom/drizzle"
import { defineRelations } from "drizzle-orm"
import {
  boolean,
  integer,
  pgEnum,
  pgTable,
  serial,
  text,
  timestamp,
  varchar,
} from "drizzle-orm/pg-core"

export const roleEnum = pgEnum("role", ["user", "admin"])

export const User = drizzleSilk(
  pgTable("users", {
    id: serial().primaryKey(),
    createdAt: timestamp().defaultNow(),
    email: text().unique().notNull(),
    name: text(),
    role: roleEnum().default("user"),
  })
)

export const Post = drizzleSilk(
  pgTable("posts", {
    id: serial().primaryKey(),
    createdAt: timestamp().defaultNow(),
    updatedAt: timestamp()
      .defaultNow()
      .$onUpdateFn(() => new Date()),
    published: boolean().default(false),
    title: varchar({ length: 255 }).notNull(),
    authorId: integer(),
  })
)

export const relations = defineRelations({ users: User, posts: Post }, (r) => ({
  users: {
    posts: r.many.posts({
      from: r.users.id,
      to: r.posts.authorId,
    }),
  },
  posts: {
    author: r.one.users({
      from: r.posts.authorId,
      to: r.users.id,
    }),
  },
}))

```

```GraphQL
type UsersItem {
  id: Int!
  createdAt: String
  email: String!
  name: String
  role: String
  posts: [PostsItem!]!
}

type PostsItem {
  id: Int!
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
  authorId: Int
  author: UsersItem
}

type Query {
  users(
    offset: Int
    limit: Int
    orderBy: [UsersOrderBy!]
    where: UsersFilters
  ): [UsersItem!]!
  usersSingle(
    offset: Int
    orderBy: [UsersOrderBy!]
    where: UsersFilters
  ): UsersItem
  posts(
    offset: Int
    limit: Int
    orderBy: [PostsOrderBy!]
    where: PostsFilters
  ): [PostsItem!]!
  postsSingle(
    offset: Int
    orderBy: [PostsOrderBy!]
    where: PostsFilters
  ): PostsItem
}

input UsersOrderBy {
  id: OrderDirection
  createdAt: OrderDirection
  email: OrderDirection
  name: OrderDirection
  role: OrderDirection
}

enum OrderDirection {
  asc
  desc
}

input UsersFilters {
  id: PgSerialFilters
  createdAt: PgTimestampFilters
  email: PgTextFilters
  name: PgTextFilters
  role: RoleFilters
  OR: [UsersFiltersOr!]
}

input PgSerialFilters {
  eq: Int
  ne: Int
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  inArray: [Int!]
  notInArray: [Int!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgSerialFiltersOr!]
}

input PgSerialFiltersOr {
  eq: Int
  ne: Int
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  inArray: [Int!]
  notInArray: [Int!]
  isNull: Boolean
  isNotNull: Boolean
}

input PgTimestampFilters {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgTimestampFiltersOr!]
}

input PgTimestampFiltersOr {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
}

input PgTextFilters {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgTextFiltersOr!]
}

input PgTextFiltersOr {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
}

input RoleFilters {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [RoleFiltersOr!]
}

input RoleFiltersOr {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
}

input UsersFiltersOr {
  id: PgSerialFilters
  createdAt: PgTimestampFilters
  email: PgTextFilters
  name: PgTextFilters
  role: RoleFilters
}

input PostsOrderBy {
  id: OrderDirection
  createdAt: OrderDirection
  updatedAt: OrderDirection
  published: OrderDirection
  title: OrderDirection
  authorId: OrderDirection
}

input PostsFilters {
  id: PgSerialFilters
  createdAt: PgTimestampFilters
  updatedAt: PgTimestampFilters
  published: PgBooleanFilters
  title: PgVarcharFilters
  authorId: PgIntegerFilters
  OR: [PostsFiltersOr!]
}

input PgBooleanFilters {
  eq: Boolean
  ne: Boolean
  lt: Boolean
  lte: Boolean
  gt: Boolean
  gte: Boolean
  inArray: [Boolean!]
  notInArray: [Boolean!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgBooleanFiltersOr!]
}

input PgBooleanFiltersOr {
  eq: Boolean
  ne: Boolean
  lt: Boolean
  lte: Boolean
  gt: Boolean
  gte: Boolean
  inArray: [Boolean!]
  notInArray: [Boolean!]
  isNull: Boolean
  isNotNull: Boolean
}

input PgVarcharFilters {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgVarcharFiltersOr!]
}

input PgVarcharFiltersOr {
  eq: String
  ne: String
  lt: String
  lte: String
  gt: String
  gte: String
  like: String
  notLike: String
  ilike: String
  notIlike: String
  inArray: [String!]
  notInArray: [String!]
  isNull: Boolean
  isNotNull: Boolean
}

input PgIntegerFilters {
  eq: Int
  ne: Int
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  inArray: [Int!]
  notInArray: [Int!]
  isNull: Boolean
  isNotNull: Boolean
  OR: [PgIntegerFiltersOr!]
}

input PgIntegerFiltersOr {
  eq: Int
  ne: Int
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  inArray: [Int!]
  notInArray: [Int!]
  isNull: Boolean
  isNotNull: Boolean
}

input PostsFiltersOr {
  id: PgSerialFilters
  createdAt: PgTimestampFilters
  updatedAt: PgTimestampFilters
  published: PgBooleanFilters
  title: PgVarcharFilters
  authorId: PgIntegerFilters
}

type Mutation {
  insertIntoUsers(values: [UsersInsertInput!]!): [UsersItem!]!
  insertIntoUsersSingle(value: UsersInsertInput!): UsersItem
  updateUsers(where: UsersFilters, set: UsersUpdateInput!): [UsersItem!]!
  deleteFromUsers(where: UsersFilters): [UsersItem!]!
  insertIntoPosts(values: [PostsInsertInput!]!): [PostsItem!]!
  insertIntoPostsSingle(value: PostsInsertInput!): PostsItem
  updatePosts(where: PostsFilters, set: PostsUpdateInput!): [PostsItem!]!
  deleteFromPosts(where: PostsFilters): [PostsItem!]!
}

input UsersInsertInput {
  id: Int
  createdAt: String
  email: String!
  name: String
  role: String
}

input UsersUpdateInput {
  id: Int
  createdAt: String
  email: String
  name: String
  role: String
}

input PostsInsertInput {
  id: Int
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
  authorId: Int
}

input PostsUpdateInput {
  id: Int
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String
  authorId: Int
}

```

We can also build resolvers from `Drizzle` tables:

```ts twoslash
// @paths: {"src/*": ["snippets/home/drizzle/*"]}
import { field, query, resolver } from "@gqloom/core"
import { useSelectedColumns } from "@gqloom/drizzle/context"
import { eq, inArray } from "drizzle-orm"
import { Post, User } from "src/schema"
import * as v from "valibot"

export const userResolver = resolver.of(User, {
  user: query(User.$nullable()) // Declare a query that returns a user
    .input({ id: v.number() }) // This query accepts an `id` as parameter
    .resolve(async ({ id }) => {
      const [user] = await db
        .select(useSelectedColumns(User)) // Select only the columns that are being queried
        .from(User)
        .where(eq(User.id, id))
      return user
    }),

  posts: field(Post.$list()) // Declare a derived field that returns a list of posts
    .derivedFrom("id") // This field depends on the `id` field
    .load(async (users) => {
      const postList = await db
        .select(useSelectedColumns(Post)) // Select only the columns that are being queried
        .from(Post)
        .where(
          inArray(
            Post.authorId,
            users.map((u) => u.id)
          )
        )
      const postMap = Map.groupBy(postList, (p) => p.authorId)
      return users.map((u) => postMap.get(u.id) ?? [])
    }),
})

// ---cut-after---

import { drizzle } from "drizzle-orm/node-postgres"

const db = drizzle(process.env.DATABASE_URL!)

```

In GQLoom, the easiest way to use `Prisma` is the [resolver factory](./schema/prisma#resolver-factory),\
with just a few lines you can create a GraphQL app with full CRUD:

::: code-group

```ts [index.ts]
import { createServer } from "node:http"
import { weave } from "@gqloom/core"
import { PrismaResolverFactory } from "@gqloom/prisma"
import { createYoga } from "graphql-yoga"
import { PrismaClient } from "./generated/client"
import { Post, User } from "./generated/gqloom"

const db = new PrismaClient()

const userResolver = new PrismaResolverFactory(User, db).resolver()
const postResolver = new PrismaResolverFactory(Post, db).resolver()

const schema = weave(userResolver, postResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})
```

```Prisma [schema.prisma]
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
  output = "../src/generated/client"
}

generator gqloom {
  provider = "prisma-gqloom"
  output   = "../src/generated/gqloom"
}

model User {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  email     String   @unique
  name      String?
  role      Role     @default(USER)
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean  @default(false)
  title     String   @db.VarChar(255)
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?
}

enum Role {
  USER
  ADMIN
}
```

```GraphQL [schema.graphql]
type User {
  id: ID!
  createdAt: String!
  email: String!
  name: String
  role: Role!
  posts: [Post!]!
}

enum Role {
  USER
  ADMIN
}

type Post {
  id: ID!
  createdAt: String!
  updatedAt: String!
  published: Boolean!
  title: String!
  authorId: Int
  author: User
}

type Query {
  countUser(where: UserWhereInput, orderBy: [UserOrderByWithRelationInput!], cursor: UserWhereUniqueInput, skip: Int, take: Int): Int!
  findFirstUser(where: UserWhereInput, orderBy: [UserOrderByWithRelationInput!], cursor: UserWhereUniqueInput, skip: Int, take: Int, distinct: [UserScalarFieldEnum!]): User
  findManyUser(where: UserWhereInput, orderBy: [UserOrderByWithRelationInput!], cursor: UserWhereUniqueInput, skip: Int, take: Int, distinct: [UserScalarFieldEnum!]): [User!]!
  findUniqueUser(where: UserWhereUniqueInput): User
  countPost(where: PostWhereInput, orderBy: [PostOrderByWithRelationInput!], cursor: PostWhereUniqueInput, skip: Int, take: Int): Int!
  findFirstPost(where: PostWhereInput, orderBy: [PostOrderByWithRelationInput!], cursor: PostWhereUniqueInput, skip: Int, take: Int, distinct: [PostScalarFieldEnum!]): Post
  findManyPost(where: PostWhereInput, orderBy: [PostOrderByWithRelationInput!], cursor: PostWhereUniqueInput, skip: Int, take: Int, distinct: [PostScalarFieldEnum!]): [Post!]!
  findUniquePost(where: PostWhereUniqueInput): Post
}

input UserWhereInput {
  AND: [UserWhereInput!]
  OR: [UserWhereInput!]
  NOT: [UserWhereInput!]
  id: IntFilter
  createdAt: DateTimeFilter
  email: StringFilter
  name: StringNullableFilter
  role: EnumRoleFilter
  posts: PostListRelationFilter
}

input IntFilter {
  equals: Int
  in: [Int!]
  notIn: [Int!]
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  not: NestedIntFilter
}

input NestedIntFilter {
  equals: Int
  in: [Int!]
  notIn: [Int!]
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  not: NestedIntFilter
}

input DateTimeFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  not: NestedDateTimeFilter
}

input NestedDateTimeFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  not: NestedDateTimeFilter
}

input StringFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  contains: String
  startsWith: String
  endsWith: String
  mode: QueryMode
  not: NestedStringFilter
}

enum QueryMode {
  default
  insensitive
}

input NestedStringFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  contains: String
  startsWith: String
  endsWith: String
  not: NestedStringFilter
}

input StringNullableFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  contains: String
  startsWith: String
  endsWith: String
  mode: QueryMode
  not: NestedStringNullableFilter
}

input NestedStringNullableFilter {
  equals: String
  in: [String!]
  notIn: [String!]
  lt: String
  lte: String
  gt: String
  gte: String
  contains: String
  startsWith: String
  endsWith: String
  not: NestedStringNullableFilter
}

input EnumRoleFilter {
  equals: Role
  in: [Role!]
  notIn: [Role!]
  not: NestedEnumRoleFilter
}

input NestedEnumRoleFilter {
  equals: Role
  in: [Role!]
  notIn: [Role!]
  not: NestedEnumRoleFilter
}

input PostListRelationFilter {
  every: PostWhereInput
  some: PostWhereInput
  none: PostWhereInput
}

input PostWhereInput {
  AND: [PostWhereInput!]
  OR: [PostWhereInput!]
  NOT: [PostWhereInput!]
  id: IntFilter
  createdAt: DateTimeFilter
  updatedAt: DateTimeFilter
  published: BoolFilter
  title: StringFilter
  authorId: IntNullableFilter
  author: UserNullableRelationFilter
}

input BoolFilter {
  equals: Boolean
  not: NestedBoolFilter
}

input NestedBoolFilter {
  equals: Boolean
  not: NestedBoolFilter
}

input IntNullableFilter {
  equals: Int
  in: [Int!]
  notIn: [Int!]
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  not: NestedIntNullableFilter
}

input NestedIntNullableFilter {
  equals: Int
  in: [Int!]
  notIn: [Int!]
  lt: Int
  lte: Int
  gt: Int
  gte: Int
  not: NestedIntNullableFilter
}

input UserNullableRelationFilter {
  is: UserWhereInput
  isNot: UserWhereInput
}

input UserOrderByWithRelationInput {
  id: SortOrder
  createdAt: SortOrder
  email: SortOrder
  name: SortOrderInput
  role: SortOrder
  posts: PostOrderByRelationAggregateInput
}

enum SortOrder {
  asc
  desc
}

input SortOrderInput {
  sort: SortOrder!
  nulls: NullsOrder
}

enum NullsOrder {
  first
  last
}

input PostOrderByRelationAggregateInput {
  _count: SortOrder
}

input UserWhereUniqueInput {
  id: Int
  email: String
  AND: [UserWhereInput!]
  OR: [UserWhereInput!]
  NOT: [UserWhereInput!]
  createdAt: DateTimeFilter
  name: StringNullableFilter
  role: EnumRoleFilter
  posts: PostListRelationFilter
}

enum UserScalarFieldEnum {
  id
  createdAt
  email
  name
  role
}

input PostOrderByWithRelationInput {
  id: SortOrder
  createdAt: SortOrder
  updatedAt: SortOrder
  published: SortOrder
  title: SortOrder
  authorId: SortOrderInput
  author: UserOrderByWithRelationInput
}

input PostWhereUniqueInput {
  id: Int
  AND: [PostWhereInput!]
  OR: [PostWhereInput!]
  NOT: [PostWhereInput!]
  createdAt: DateTimeFilter
  updatedAt: DateTimeFilter
  published: BoolFilter
  title: StringFilter
  authorId: IntNullableFilter
  author: UserNullableRelationFilter
}

enum PostScalarFieldEnum {
  id
  createdAt
  updatedAt
  published
  title
  authorId
}

type Mutation {
  createUser(data: UserCreateInput!): User
  createManyUser(data: [UserCreateManyInput!]!): BatchPayload
  deleteUser(where: UserWhereUniqueInput!): User
  deleteManyUser(where: UserWhereInput): BatchPayload
  updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User!
  updateManyUser(data: UserUpdateManyMutationInput!, where: UserWhereInput): BatchPayload
  upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User!
  createPost(data: PostCreateInput!): Post
  createManyPost(data: [PostCreateManyInput!]!): BatchPayload
  deletePost(where: PostWhereUniqueInput!): Post
  deleteManyPost(where: PostWhereInput): BatchPayload
  updatePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post!
  updateManyPost(data: PostUpdateManyMutationInput!, where: PostWhereInput): BatchPayload
  upsertPost(where: PostWhereUniqueInput!, create: PostCreateInput!, update: PostUpdateInput!): Post!
}

input UserCreateInput {
  createdAt: String
  email: String!
  name: String
  role: Role
  posts: PostCreateNestedManyWithoutAuthorInput
}

input PostCreateNestedManyWithoutAuthorInput {
  create: [PostCreateWithoutAuthorInput!]
  connectOrCreate: [PostCreateOrConnectWithoutAuthorInput!]
  createMany: PostCreateManyAuthorInputEnvelope
  connect: [PostWhereUniqueInput!]
}

input PostCreateWithoutAuthorInput {
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
}

input PostCreateOrConnectWithoutAuthorInput {
  where: PostWhereUniqueInput!
  create: PostCreateWithoutAuthorInput!
}

input PostCreateManyAuthorInputEnvelope {
  data: [PostCreateManyAuthorInput!]!
  skipDuplicates: Boolean
}

input PostCreateManyAuthorInput {
  id: Int
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
}

type BatchPayload {
  count: Int!
}

input UserCreateManyInput {
  id: Int
  createdAt: String
  email: String!
  name: String
  role: Role
}

input UserUpdateInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  email: StringFieldUpdateOperationsInput
  name: NullableStringFieldUpdateOperationsInput
  role: EnumRoleFieldUpdateOperationsInput
  posts: PostUpdateManyWithoutAuthorNestedInput
}

input DateTimeFieldUpdateOperationsInput {
  set: String
}

input StringFieldUpdateOperationsInput {
  set: String
}

input NullableStringFieldUpdateOperationsInput {
  set: String
}

input EnumRoleFieldUpdateOperationsInput {
  set: Role
}

input PostUpdateManyWithoutAuthorNestedInput {
  create: [PostCreateWithoutAuthorInput!]
  connectOrCreate: [PostCreateOrConnectWithoutAuthorInput!]
  upsert: [PostUpsertWithWhereUniqueWithoutAuthorInput!]
  createMany: PostCreateManyAuthorInputEnvelope
  set: [PostWhereUniqueInput!]
  disconnect: [PostWhereUniqueInput!]
  delete: [PostWhereUniqueInput!]
  connect: [PostWhereUniqueInput!]
  update: [PostUpdateWithWhereUniqueWithoutAuthorInput!]
  updateMany: [PostUpdateManyWithWhereWithoutAuthorInput!]
  deleteMany: [PostScalarWhereInput!]
}

input PostUpsertWithWhereUniqueWithoutAuthorInput {
  where: PostWhereUniqueInput!
  update: PostUpdateWithoutAuthorInput!
  create: PostCreateWithoutAuthorInput!
}

input PostUpdateWithoutAuthorInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  updatedAt: DateTimeFieldUpdateOperationsInput
  published: BoolFieldUpdateOperationsInput
  title: StringFieldUpdateOperationsInput
}

input BoolFieldUpdateOperationsInput {
  set: Boolean
}

input PostUpdateWithWhereUniqueWithoutAuthorInput {
  where: PostWhereUniqueInput!
  data: PostUpdateWithoutAuthorInput!
}

input PostUpdateManyWithWhereWithoutAuthorInput {
  where: PostScalarWhereInput!
  data: PostUpdateManyMutationInput!
}

input PostScalarWhereInput {
  AND: [PostScalarWhereInput!]
  OR: [PostScalarWhereInput!]
  NOT: [PostScalarWhereInput!]
  id: IntFilter
  createdAt: DateTimeFilter
  updatedAt: DateTimeFilter
  published: BoolFilter
  title: StringFilter
  authorId: IntNullableFilter
}

input PostUpdateManyMutationInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  updatedAt: DateTimeFieldUpdateOperationsInput
  published: BoolFieldUpdateOperationsInput
  title: StringFieldUpdateOperationsInput
}

input UserUpdateManyMutationInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  email: StringFieldUpdateOperationsInput
  name: NullableStringFieldUpdateOperationsInput
  role: EnumRoleFieldUpdateOperationsInput
}

input PostCreateInput {
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
  author: UserCreateNestedOneWithoutPostsInput
}

input UserCreateNestedOneWithoutPostsInput {
  create: UserCreateWithoutPostsInput
  connectOrCreate: UserCreateOrConnectWithoutPostsInput
  connect: UserWhereUniqueInput
}

input UserCreateWithoutPostsInput {
  createdAt: String
  email: String!
  name: String
  role: Role
}

input UserCreateOrConnectWithoutPostsInput {
  where: UserWhereUniqueInput!
  create: UserCreateWithoutPostsInput!
}

input PostCreateManyInput {
  id: Int
  createdAt: String
  updatedAt: String
  published: Boolean
  title: String!
  authorId: Int
}

input PostUpdateInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  updatedAt: DateTimeFieldUpdateOperationsInput
  published: BoolFieldUpdateOperationsInput
  title: StringFieldUpdateOperationsInput
  author: UserUpdateOneWithoutPostsNestedInput
}

input UserUpdateOneWithoutPostsNestedInput {
  create: UserCreateWithoutPostsInput
  connectOrCreate: UserCreateOrConnectWithoutPostsInput
  upsert: UserUpsertWithoutPostsInput
  disconnect: UserWhereInput
  delete: UserWhereInput
  connect: UserWhereUniqueInput
  update: UserUpdateToOneWithWhereWithoutPostsInput
}

input UserUpsertWithoutPostsInput {
  update: UserUpdateWithoutPostsInput!
  create: UserCreateWithoutPostsInput!
  where: UserWhereInput
}

input UserUpdateWithoutPostsInput {
  createdAt: DateTimeFieldUpdateOperationsInput
  email: StringFieldUpdateOperationsInput
  name: NullableStringFieldUpdateOperationsInput
  role: EnumRoleFieldUpdateOperationsInput
}

input UserUpdateToOneWithWhereWithoutPostsInput {
  where: UserWhereInput
  data: UserUpdateWithoutPostsInput!
}
```

:::

We can also build resolvers from `Prisma` models:

```ts
import { field, query, resolver } from "@gqloom/core"
import * as v from "valibot"
import { Post, User } from "./generated/gqloom"

export const userResolver = resolver.of(User, {
  user: query(User.nullable())
    .input({ id: v.number() })
    .resolve(({ id }) => {
      return db.user.findUnique({ where: { id } })
    }),

  posts: field(Post.list())
    .derivedFrom("id")
    .resolve(async (users) => {
      return (
        (await db.user.findUnique({ where: { id: users.id } }).posts()) ?? []
      )
    }),
})
```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { YupWeaver } from "@gqloom/yup"
import { createYoga } from "graphql-yoga"
import { string } from "yup"

const helloResolver = resolver({
  hello: query(string().required())
    .input({ name: string().default("World") })
    .resolve(({ name }) => `Hello, ${name}!`),
})

const schema = weave(YupWeaver, helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { EffectWeaver } from "@gqloom/effect"
import { Schema } from "effect"
import { createYoga } from "graphql-yoga"

const standard = Schema.standardSchemaV1

const helloResolver = resolver({
  hello: query(standard(Schema.String))
    .input({
      name: standard(Schema.NullishOr(Schema.String)),
    })
    .resolve(({ name }) => `Hello, ${name ?? "World"}!`),
})

const schema = weave(EffectWeaver, helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { jsonSilk } from "@gqloom/json"
import { createYoga } from "graphql-yoga"

const helloResolver = resolver({
  hello: query(jsonSilk({ type: "string" }))
    .input({ name: jsonSilk({ type: "string" }) })
    .resolve(({ name }) => `Hello, ${name ?? "World"}!`),
})

const schema = weave(helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, silk, weave } from "@gqloom/core"
import { GraphQLNonNull, GraphQLString } from "graphql"
import { createYoga } from "graphql-yoga"

const helloResolver = resolver({
  hello: query(silk(new GraphQLNonNull(GraphQLString)))
    .input({ name: silk(GraphQLString) })
    .resolve(({ name }) => `Hello, ${name ?? "World"}!`),
})

const schema = weave(helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

```ts twoslash
import { createServer } from "node:http"
import { type GraphQLSilk, query, resolver, weave } from "@gqloom/core"
import { JSONWeaver } from "@gqloom/json"
import { createYoga } from "graphql-yoga"
import Type from "typebox"

const helloResolver = resolver({
  hello: query(typeSilk(Type.String()))
    .input({ name: typeSilk(Type.String()) })
    .resolve(({ name }) => `Hello, ${name ?? "World"}!`),
})

const schema = weave(helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

function typeSilk<T extends Type.TSchema>(
  type: T
): T & GraphQLSilk<Type.Static<T>, Type.Static<T>> {
  return JSONWeaver.unravel(type) as T &
    GraphQLSilk<Type.Static<T>, Type.Static<T>>
}

```

```ts twoslash
import { createServer } from "node:http"
import { query, resolver, weave } from "@gqloom/core"
import { JSONWeaver } from "@gqloom/json"
import { type } from "arktype"
import { createYoga } from "graphql-yoga"

const helloResolver = resolver({
  hello: query(type("string"))
    .input(type({ "name?": "string | null" }))
    .resolve(({ name }) => `Hello, ${name ?? "World"}!`),
})

const schema = weave(JSONWeaver, helloResolver)

const yoga = createYoga({ schema })
const server = createServer(yoga)
server.listen(4000, () => {
  console.info("Server is running on http://localhost:4000/graphql")
})

```

## Next steps

* Learn GQLoom's core concepts: [Silk](./silk), [Resolver](./resolver), [Weave](./weave);
* Learn common features: [Context](./context), [DataLoader](./dataloader), [Middleware](./middleware)
* Add a GraphQL client to your frontend project: [gql.tada](https://gql-tada.0no.co/), [Urql](https://commerce.nearform.com/open-source/urql/), [Apollo Client](https://www.apollographql.com/docs/react), [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/graphql), [Graffle](https://graffle.js.org/)
