loading...
Cover image for GraphQL: Create your API using TypeScript and decorators with Rakkit
daven

GraphQL: Create your API using TypeScript and decorators with Rakkit

owen profile image owen ・3 min read

What?

Okay then, this is related to my previous article about Rakkit. So I'll advise you to go take a look around 😊.

So, here I will show you a more concrete example of what you can do using Rakkit to create a GraphQL API with a user management system.

But first: the installation of Rakkit 💾

So there are few dependencies that we must install to continue:

Here, I would use apollo-server but it's also possible to install apollo-server-koa if you use Rakkit for REST and GraphQL which allows you to link contexts.

Just run this command to install the required dependencies:

npm i rakkit graphql @types/graphql apollo-server reflect-metadata

reflect-metadata allows us to use the decorators with TypeScript

Okay cool, now we just need to configure TypeScript to enable the decorators by creating a tsconfig.json file at the root of the project, containing this:

{
  "compileOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "commonjs",
    "target": "es2016",
    "noImplicitAny": false,
    "sourceMap": true,
    "outDir": "build",
    "declaration": true,
    "importHelpers": true,
    "forceConsistentCasingInFileNames": true,
    "lib": [
      "es2016",
      "esnext.asyncitable"
    ],
    "moduleResolution": "node"
  }
}

./tsconfig.json

The definitions of types 🚻

Okay then let's start by creating our User class, which we'll have to decorate with @ObjectType():

import { ObjectType, Field } from "rakkit";
import * as Crypto from "crypto";

@ObjectType()
export class User {
  @Field()
  username: string;

  @Field()
  email: string;

  @Field()
  id: string;

  // Just to show a computed property:
  @Field(type => String)
  get flatInfos(): string {
    return [this.name, this.email, this.id].join(":");
  }

  constructor(username: string, email: string) {
    this.username = username;
    this.email = email;
    this.id = Crypto.randomBytes(16).toString("hex");
  }
}

./types/User.ts

You need a small "database" 🗂

So we're going to have to play with some users in order to test our app, so I'm just going to create a list of user instances to make it clearer:

You can use a real database with an ORM like TypeORM for your projects

import { User } from "../types/User";

export const users = [
  new User("JohnDoe", "john@doe.com"),
  new User("JaneDoe", "jane@doe.com"),
  new User("Ben", "ben@doe.com")
];

./db/users.ts

Resolver (Query, Mutation, Subscription) 🚀

It is in the following class that we will define our query/mutation/subscription. It will contain a simple CRUD and a subscription to be notified when a user is registered:

import {
  Resolve,
  Query,
  Mutation,
  Subscription,
  IContext,
  Arg
} from "rakkit";
import { User } from "../types/User";
import { users } from "../db/users";

@Resolver()
export class UserResolver {
  @Query(returns => [User])
  getAllUsers() { {
    return users;
  }

  @Query({ nullable: true })
  getOneUserByName(@Arg("name") name: string): User {
    return users.find((user) => user.name ==== name);
  }

  @Mutation()
  addUser(
    // Defining the mutation arguments
    @Arg("name") name: string,
    @Arg("email") email: string,
    context: IContext
  ): User {
    const user = new User(name, email);
    users.push(user);
    // Publish the event for subscriptions with the created user
    context.gql.pubSub.publish("USER_ADDED", user);
    return user;
  }

  @Subscription({ topics: "USER_ADDED" })
  userAddedNotif(createdUser: User): User {
    // Send the created user to the client
    return createdUser;
  }
}

./resolvers/UserResolver.ts

The point of entry 🚪

Now we need to have an entry point for our application:

// It allows us to use decorators:
import "reflect-metadata";

import { Rakkit } from "rakkit";
import { ApolloServer } from "apollo-server";

async function bootstrap() {
  await Rakkit.start({
    gql: {
      // You give an array of glob string:
      resolvers: [`${__dirname}/resolvers/*Resolver.ts`]
    }
  });
  // Retrieve the GraphQL compiled schema:
  const schema = Rakkit.MetadataStorage.Gql.Schema;

  const server = new ApolloServer({
    schema
  });

  server.listen();
}

bootstrap();

./bootstrap.ts

Done, so let's start and test it ! 🎉

To start it you must install ts-node globally to run directly your TypeScript app:

npm i -g ts-node

Then just run this:

ts-node relative-path-to/bootstrap.ts

And just go to http://localhost:4000 with your favorite browser to make some GraphQL queries! 🔥

getAllUsers - Get all users:

getOneUserByName - Get a specific user by name:

addUser - Add an user:

userAddedNotif - Listen to the user creation event:

Et voilà! This example is available on GitHub 😊, thanks!

Discussion

pic
Editor guide
Collapse
marcussa profile image
Marcus S. Abildskov

Oh wow, this looks just like an exact copy of @nestjs/graphql just without the big community to back it up.. lol

Collapse
owen profile image
owen Author

Yes the GraphQL package looks like type-graphql (used by nest) but there is several differences that are mentioned in the docs 🙂
For the community, every projects start from zero, we are building it 😉

Collapse
marcussa profile image
Marcus S. Abildskov

Well, IMO reinventing the wheel is never good :)
And especially in this case it makes absolutely no sense at all.
What would you rather use? A new framework which reinvents everything (not even better), or a battle production tested framework with several community backed packages?