Giter Site home page Giter Site logo

garph's Introduction

garph

Screen.Recording.2023-02-27.at.19.32.45.mov

Warning: We would love to hear your Feedback in our Discord

Note: tRPC-style client for Garph has arrived! See garph-gqty for more ๐Ÿš€

Garph is a fullstack GraphQL framework for TypeScript, that aims to deliver the best GraphQL Developer-Experience.

Get started

  1. Install the dependencies

    npm i garph graphql-yoga
    
  2. Create example GraphQL API

    import { g, InferResolvers, buildSchema } from 'garph'
    import { createYoga } from 'graphql-yoga'
    import { createServer } from 'http'
    
    const queryType = g.type('Query', {
      greet: g.string()
        .args({
          name: g.string().optional().default('Max')
        })
        .description('Greets a person')
    })
    
    const resolvers: InferResolvers<{ Query: typeof queryType }, {}> = {
      Query: {
        greet: (parent, args, context, info) => `Hello, ${args.name}`
      }
    }
    
    const schema = buildSchema({ g, resolvers })
    const yoga = createYoga({ schema })
    const server = createServer(yoga)
    server.listen(4000, () => {
      console.info('Server is running on http://localhost:4000/graphql')
    })
  3. Start the server

    npx ts-node server.ts
    
  4. Query the API

    Go to: http://localhost:4000/graphl

    Enter the following query:

    {
      greet(name: "Max")
    }

    Click on the play button

Documentation

Documentation is available on garph.dev/docs

Examples

Example projects can be found under examples/

Feedback

We would love to hear your Feedback in our Discord community

garph's People

Contributors

gaiusvalerius avatar lorefnon avatar mishushakov avatar na2na-p avatar noverby avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

garph's Issues

Helper methods: `resolve`

Specify resolvers directly in schema

Example:

g.type('Query', {
  test: g.string().resolve(() => 'Hello World')
})

Dedicated support for loaders

Thank you for creating this library. The API for defining schemas is really nice.

I am curious if you'd be interested in supporting batch loaders at the framework level similar to mercurius or quarkus.

Having batching supported at framework level reduces quite a bit of boilerplate around the usual dance of defining data loaders, attaching them to context & later using them in actual resolver.

Drawing inspiration from mercurius loaders, I'd imagine we could have something like:

const batchResolvers: InferBatchResolvers<
  { Query: typeof queryType; },
  {}
> = {
  Query: {
    greet: (queries, context) => queries.map(({ parent, args, info }) => `Hello, ${args.name}`),
  },
};

export const schema = buildSchema({ g, batchResolvers });

So a batch resolver is similar to a normal resolver except it receives an array of queries rather than a single query.

Subscriptions

Subscriptions are currently not supported by Garph. We want to fix this

  • Add subscriptions to resolvers' type-inference (InferResolvers)
  • Add subscriptions to the GraphQL layer
    Blocked by graphql-compose/graphql-compose#405
  • Add documentation article

Middleware

.use((parent, args, context, info) => {})

If used on an object, use should apply to all fields on that object

Semantics

We want to propose a optional, but recommended field type which helps with semantics, especially on bigger projects

Before:

const blogType = g
  .type("Blog", {
    title: g
      .string()
      .optional()
      .list()
      .args({
        test: g.string(),
      })
      .description("The title of the blog"),
  })
  .description("The blog of the user");

After:

const newBlogType = g
  .type("Blog", {
    title: g
      .field(g.string().optional().list())
      .args({
        test: g.string(),
      })
      .description("The title of the blog"),
  })
  .description("The blog of the user");

Notice the change in LoC

Extend field types when in reference

Example:

export const userType = g.type('User', {
    id: g.string(),
    email: g.string(),
    name: g.string().optional(),
});

const userMutationType = g.type('Mutation', {
    register: g.ref(userType)
        .args({
            email: g.string().required(),
            password: g.string().required(),
            name: g.string().optional()
        })
        .description('Register a new user')
});

const userQueryType = g.type('Query', {
    me: g.ref(userType)
    .extend({             // <-- extend the user type with additional fields
        createdAt: g.string(),
        updatedAt: g.string()
    })
        .description('User who is currently logged in')
})

This issue pertains to the userQueryType object and proposes extending it to include additional fields, createdAt and updatedAt, without duplicating the schema definition.

Need to resolve the type like pothos.

const builder = new SchemaBuilder({});

builder.queryType({
  fields: (t) => ({
    hello: t.string({
      args: {
        name: t.arg.string(),
      },
      resolve: (parent, { name }) => `hello, ${name || 'World'}`,
    }),
  }),
});

Automatic Filters

Adds automatic filtering based on the shape of your data like in Gatsby.js

Default nullability

I want to be able to configure default nullability of the schema. The nullability setting should change both the output of the schema and type inference

Zod Integration

Here's a proposal

  1. Create garph types from Zod types:
const type = g.zodType('name', yourZodType)
  1. Create garph scalars from Zod types:
const type = g.zodScalar('name', yourZodType)
  1. Create garph inputs from Zod types:
const type = g.zodInput('name', yourZodType)
  1. Create Zod types from Garph types:
type.toZod()

Yoga Next.js Edge

export const config = {
  runtime: 'edge'
}

export const schema = buildSchema({ g, resolvers })

// Next.JS + Yoga API
const yoga = createYoga({
  schema,
  graphqlEndpoint: '/api/graphql'
})

export default yoga.handleRequest

How to handle schema declarations in separate files?

All of the examples have all the schemas in the same file, then build the schema at the end for the bottom of the file.

Is it possible to put these declarations in separate files?

I tried to use convertSchema, but to put them all as references into the types array and got the error:

Error: Type with name "undefined" does not exists

Then I tried to import g, and export g so I could use the same reference to g in separate files and got the error:

Error: Type with name "Query" does not exists

After that, I moved all of them into the same file, and it works fine. Is it possible to keep the schemas in separate files?

Small details in example schema implementation

I was looking at your example in the readme and noticed a few issues:

  1. You specify a description for the greet field, but it is not shown in the Produces the following GraphQL schema code below. Assuming the description is preserved, it would be cool to show that!

This second one is a bit more pedantic (feel free to ignore!).

The name argument is specified as optional, but it is used as if it were non-optional.

The current implementation would take a query

query {
  greet
}

And return

{
  "greet": "Hello, null"
}

Directives

Add support for specifying and implementing GraphQL directives in Garph

Example:

const d = g.directive('Name', { param: g.string() }, ['FIELD', 'ARGUMENT'])

g.type('Query', {
  test: g.string()
    .args({
      test: g.string().addDirective(d, { param: 'Hello' })
    })
    .addDirective(d, { param: 'Hello' })
})
.addDirective(d, { param: 'Hello' })

How to handle field level resolvers?

Say you have a relationship Authors to Books. The Book type has a field author, which could be a field level resolver to avoid the n+1 problem. Defining the schema for this would be like:

const authorType = g.type('Author', {
  name: g.string(),
})

const bookType = g.type('Book', {
  title: g.string(),
  author: g.ref(bookType)
})

const queryType = g.type('Query', {
  books: g.ref(authorType).list(),
})

The resolver would be something like:

const resolvers: InferResolvers<{ Query: typeof queryType }, {}> = {
  Query: {
    async books(parent, args, context, info) {
      const allBooks = db.findAllBooks()
      // does not include author - TS error saying it's missing `author`
      return allBooks
    },
  },
}

Is it possible for this to be setup to know that there's a field level resolver called author somewhere that will satisfy the requirement? As is, this is a TS error, and I have to use a DeepOmit utility type to ignore the need for books.

So far, I have thought about it being optional, but in reality, it's not optional. The schema needs to say this is required, but the resolver needs to be allowed to return without it. This is super challenging for TS to know there's a field resolver somewhere, but at least there could be something like g.fieldResolver() which makes the field optional when using InferResolvers. If someone leaves it off, it will throw a GraphQL error.

Helper Methods: `or`

or helper would help create a Union with the current type and the type specified in or

The use-case for the feature include error handling

Example:

const error = g.type('Error', {
  message: g.string()
})

g.type('Test', {
  test: g.string()
})
.or(error)

Auth

Allow implementing auth rules and logic directly in Garph

Where can I import convertSchema?

At the README, near the bottom, it mentions you can build your schema with convertSchema, but I can't seem to find that function anywhere.

Also, I believe there's a typo right above it, with buildSchema

Screen Shot 2023-03-22 at 5 20 38 PM

Type visibility

Some fields on the classes need to be readonly, some need to be private

InferResolversStrict not checking resolver params correctly

Hey, here's something I'd like to understand:

Given the following code, InferResolversStrict will happily accept getUsername as a resolver, although its arguments are wrong. I think this is related to the fact that the first arg is supposed to be parent: any, which checks against id: string. However, the number of args isn't enforced either (it should be 4 instead of 1).

(I don't think it's a problem with Deno's TS compiler options as those are pretty strict by default.)

Please enlighten me. ๐Ÿ˜„

import { graphql } from "npm:graphql"
import { buildSchema, g, InferResolversStrict } from "npm:garph"

function getUsername(id: string): string {
  return "User#" + id
}

const queryType = g.type("Query", {
  username: g.string().args({
    id: g.id(),
  }),
})

const resolvers: InferResolversStrict<
  { Query: typeof queryType },
  {}
> = {
  Query: {
    username: getUsername, // wrong
    // username: (_, args, __, ___) => getUsername(args.id), // correct
  },
}

const query = Deno.args[0]
const schema = buildSchema({ g, resolvers })
const result = await graphql({
  schema,
  source: query,
  rootValue: resolvers,
})

console.log(JSON.stringify(result, null, 2))

Use Pothos as a GraphQL builder

Hi!

I discovered you this morning, great idea with GQL and tRPC-like experience!

I briefly reviewed the project. I know it's in a very early stage and much functionality will be added later. Maybe there is a way to accelerate it by using Pothos as a query builder? It has numerous useful plugins (prisma integration, errors as types and more). I miss these features in this project a lot. Also, it's battle-tested with many examples. Would be nice to have it there.

If this is completely absurd, sorry for the trouble, I'm not an expert ๐Ÿ˜„

Validation

How this might look in Garph:

const queryType = g.type('Query', {
  greet: g.string().validate(s => s.length > 50)
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.