DEV Community

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

Posted on • Edited on

GraphQL: Create your API using TypeScript and decorators with Rakkit

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 
Enter fullscreen mode Exit fullscreen mode

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" } } 
Enter fullscreen mode Exit fullscreen mode

./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"); } } 
Enter fullscreen mode Exit fullscreen mode

./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") ]; 
Enter fullscreen mode Exit fullscreen mode

./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; } } 
Enter fullscreen mode Exit fullscreen mode

./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(); 
Enter fullscreen mode Exit fullscreen mode

./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 
Enter fullscreen mode Exit fullscreen mode

Then just run this:

ts-node relative-path-to/bootstrap.ts 
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (3)

Collapse
 
marcus-sa profile image
Marcus S. Abildskov • Edited

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 daven

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
 
marcus-sa profile image
Marcus S. Abildskov • Edited

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?