DEV Community

Raja Singh
Raja Singh

Posted on

5 1

GraphQL Server with Deno and Oak Framwork

Alt Text

In my previous topic, I have explained about below things

  1. What is GraphQL?
  2. Comparing REST API and GraphQL
  3. GraphQL Terms

Creating A GraphQL Server With Deno

Our Deno dependencies

Oak : A middleware framework for Deno’s http server, including a router middleware.

Oak-GraphQL : A simple graphql middleware for oak deno framework.

GraphQL-Deno : This is a one-to-one port of graphql-js to Deno. It is working, fully adapted to Deno and exports both TS types and JS code.

First we are making the basic deno server with oak framework and oak_graphql. We are creating the common dependency file for import packages.

import { Application } from "https://deno.land/x/oak/mod.ts";
import { config } from "https://deno.land/x/dotenv/mod.ts";
import { applyGraphQL, gql } from "https://deno.land/x/oak_graphql/mod.ts";
import {
GraphQLScalarType,
Kind,
} from "https://raw.githubusercontent.com/adelsz/graphql-deno/v15.0.0/mod.ts";
export {
Application,
GraphQLScalarType,
Kind,
gql,
applyGraphQL,
config
}
view raw dep.ts hosted with ❤ by GitHub
import { Application } from "./config/deps.ts";
import { applyGraphQL } from "./config/deps.ts";
import { Schema } from "./schema/index.ts";
import { resolvers } from "./resolver/index.ts";
export class App {
public app: Application;
public port: number;
public logger: any;
constructor(port: any) {
this.app = new Application();
this.port = port;
this.initializeMiddleware();
this.initializeRoutes();
}
// initialize middleware
private initializeMiddleware() {
this.app.use(async (ctx, next) => {
await next();
const rt = ctx.response.headers.get("X-Response-Time");
console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});
}
// initialize routes
private async initializeRoutes() {
const GraphQLService = await applyGraphQL({
path: '/graphql',
typeDefs: Schema,
resolvers: resolvers,
context: (ctx) => {}
});
this.app.use(GraphQLService.routes(), GraphQLService.allowedMethods());
}
// server listen
public async listen() {
return await this.app.listen({ port: this.port });
}
}
view raw app.ts hosted with ❤ by GitHub

GraphQL Schema

A GraphQL schema is a description of the data clients can request from a GraphQL API. It also defines the queries and mutation functions that the client can use to read and write data from the GraphQL server. In other words, you specify your client or application UI data requirements in your GraphQL schema.

The schema is written using the GraphQL schema language (also called Schema Definition Language, SDL).

With it, you can define object types and fields to represent data that can be retrieved from the API as well as root types that define the group of operations that the API allows.

Object Types

Alt Text

Root Types

The root types are the query type, mutation type, and subscription type, which are the three types of operations you can run request from a GraphQL server.

Alt Text

Let’s go ahead and create a schema. Add a new file src/schema/author.ts with the following content

import { gql } from "../config/deps.ts";
export const AuthorTypes = (gql as any)`
scalar Date
type Author {
_id : ID
firstName: String!
lastName: String!
email: String!
post: [Post!]
}
input AuthorInput {
firstName: String!
lastName: String!
email: String!
}
type Post {
_id : ID
authorId : String!
postTitle : String!
postCategory : String!
postDate: Date
}
input PostInput {
authorId : String!
postTitle : String!
postCategory : String!
postDate: Date
}
extend type Query {
getAuthor(_id: ID): Author
getPost(_id: ID): Post
}
extend type Mutation {
createAuthor(input: AuthorInput): Author
createPost(input: PostInput): Post
}
`;
view raw author.ts hosted with ❤ by GitHub

What we have above is the GraphQL schema. In it, we defined a Author type with four fields, Post type with five fields and a root Query type with two fields.

GraphQL Query

The two fields in the root Query type defines what queries/operations the server can execute. The getAuthor return a Author based on the id passed as an argument to query. The getPost return a Post based on the id passed as an argument to query.

GraphQL Mutation

The GraphQL Mutation is used to perform the Create, Update and Delete Operation. The createAuthor, createPost perform the insert operation based on Input object.

Resolver

Our API is able to run two query operations

one to retrieve a Author with Array of Author’s post based on its id and another one retrieve a Post based on its id. The next step for us is to define how these queries get resolved so that the right fields are returned to the client.

GraphQL has an execution algorithm. The implementation of this execution algorithm is what transforms the query from the client into actual result, by moving through every field in the schema, and executing their “resolver” function to determine its result.

Add the below code to src/resolver/author.ts

import { db } from "../config/db.ts";
let author = db.getDatabase().collection("author");
let post = db.getDatabase().collection("post");
export const AuthorResolvers = {
Query: {
getAuthor: async (parent: any, { _id }: any, context: any, info: any) => {
const authorSelect = await author.findOne({
_id: {
$oid:_id,
},
});
const postSelect = await post.find({ authorId: { $eq: _id } });
let allPost = await postSelect.map((post : any) => { return {
...post, _id: post._id.$oid
} })
authorSelect._id = _id;
authorSelect.post = allPost;
return authorSelect;
},
getPost: async (parent: any, { _id }: any, context: any, info: any) => {
const postSelect = await post.findOne({
_id: {
$oid:_id,
},
});
postSelect._id = _id;
return postSelect;
},
},
Mutation: {
createAuthor: async (parent: any, { input: { firstName, lastName, email } }: any, context: any, info: any) => {
const insertAuthor = await author.insertOne({
firstName,
lastName,
email,
});
const authorSelect = await author.findOne({
_id: {
$oid: insertAuthor.$oid,
},
});
authorSelect._id = insertAuthor.$oid;
return authorSelect;
},
createPost: async (parent: any, { input: { authorId, postTitle, postCategory, postDate } }: any, context: any, info: any) => {
console.log("input:", postCategory, postTitle);
const insertPost = await post.insertOne({
authorId,
postTitle,
postCategory,
postDate
});
const postSelect = await post.findOne({
_id: {
$oid: insertPost.$oid,
},
});
return postSelect;
},
},
};
view raw author.ts hosted with ❤ by GitHub

Every resolver function receives the below four arguments

  1. parent : It contains the result of the previously executed resolver in the call chain.

  2. args : These are the arguments provided to the field in the GraphQL query. Following our example, this will be the id argument for the getAuthor query getAuthor(_id: ID): Author.

  3. context : This is an object that every resolver can read from or write to. You can keep objects that give access to database or that contain information from the HTTP request headers here. The context object is the same across resolvers, and you can write contextual information to it as needed.

  4. info : It holds field-specific information relevant to the current query as well as the schema details.

GraphQL Query Language

Open the command line and run the below command

denon run --allow-net --allow-env --allow-write --allow-read --allow-plugin --unstable server.ts

Now the server running on http://localhost:8080/graphql

Alt Text

Alt Text

Alt Text

Alt Text

Conclusion

If you have suggestions please let me know in the comments section🙋‍♂️

Thank You!🖤

Here is the GitHub Repository for all the source code.

In the next section will be covering following things

  1. GraphQL Subscription

  2. GraphQL with Client side Integration with React JS

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (1)

Collapse
 
coyotte508 profile image
coyotte508

It would be interesting to have examples with bulk data. One major concern with GraphQL is that you'd have to do 100 mongodb queries to get 100 items, instead of just one {_id: {$in: [...]}}.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay