Home
Softono

Gqtx

Open source TypeScript
460
Stars
13
Forks
14
Issues
5
Watchers
1 year
Last Commit

 About Gqtx

Code-first Typescript GraphQL Server without codegen or metaprogramming

Platforms

Web Self-hosted

Languages

TypeScript

Links

Need Help Installing Gqtx?

We provide expert installation service for this software. Our team will install, configure, and secure Gqtx on your server. plans start at just $30.

Why another GraphqQL Server?

Note: 0.9.x contains breaking API changes. Go here for previous readme.

Getting Started

yarn add gqtx

Type-safety without manual work

gqtx is a thin layer on top of graphql-js for writing a type-safe GraphQL server in TypeScript. It provides you with a set of helper functions to create an intermediate representation of a GraphQL schema, and then converts that schema to a raw graphql-js schema. So you get to use everything from the reference implementation of GraphQL, but with way more type safety.

If a schema compiles, the following holds:

  • The type of a field agrees with the return type of the resolver.
  • The arguments of a field agrees with the accepted arguments of the resolver.
  • The source of a field agrees with the type of the object to which it belongs.
  • The return type of the resolver will not be input types (InputObject)
  • The arguments of a field will not be abstract types (Interface, Union)
  • The context argument for all resolver functions in a schema agree.

Most importantly, we achieve all this without having to:

  • Set up code generation tools
  • Write SDL and having your schema partially defined in code and in a DSL file
  • Require special compiler magic such as reflect-metadata and decorators

What does it look like?

import { Gql, buildGraphQLSchema } from 'gqtx';

enum Role {
  Admin,
  User,
}

type User = {
  id: string;
  role: Role;
  name: string;
};

const users: User[] = [
  { id: '1', role: Role.Admin, name: 'Sikan' },
  { id: '2', role: Role.User, name: 'Nicole' },
];

// We can declare the app context type once, and it will
// be automatically inferred for all our resolvers
declare module "gqtx" {
  interface GqlContext {
    viewerId: number;
    users: User[];
  }
}

const RoleEnum = Gql.Enum({
  name: 'Role',
  description: 'A user role',
  values: [
    { name: 'Admin', value: Role.Admin },
    { name: 'User', value: Role.User },
  ],
});

const UserType = Gql.Object<User>({
  name: 'User',
  description: 'A User',
  fields: () => [
    Gql.Field({ name: 'id', type: Gql.NonNull(Gql.ID) }),
    Gql.Field({ name: 'role', type: Gql.NonNull(RoleEnum) }),
    Gql.Field({ name: 'name', type: Gql.NonNull(Gql.String) }),
  ],
});

const Query = Gql.Query({
  fields: [
    Gql.Field({
      name: 'userById',
      type: UserType,
      args: {
        id: Gql.Arg({ type: Gql.NonNullInput(Gql.ID) }),
      },
      resolve: (_, args, ctx) => {
        // `args` is automatically inferred as { id: string }
        // `ctx` (context) is also automatically inferred as { viewerId: number, users: User[] }
        const user = ctx.users.find((u) => u.id === args.id);
        // Also ensures we return an `User | null | undefined` type
        return user
      },
    }),
  ],
});

const schema = buildGraphQLSchema({
  query: Query,
});

Use your favorite server option to serve the schema!

import express from 'express';
import graphqlHTTP from 'express-graphql';

const app = express();

app.use(
  '/graphql',
  graphqlHTTP({
    schema,
    graphiql: true,
  })
);

app.listen(4000);

gqtx works best with TypeScript strict mode

We recommend using TypeScript strict mode in order to have the best developer experience.

tsconfig.json

{
  "compilerOptions": {
+    "strict": true
  }
}

To Recap

  • We created an intermediate representation of a GraphQL schema via the helper functions exported by this library.
  • Then, we converted the schema to a real graphql-js schema by calling buildGraphQLSchema at server startup time.
  • Used existing express middleware express-graphql to server our schema with graphiql explorer
  • That's it! We get a fully type-safe server with almost zero type annotation needed