DEV Community

Cover image for Modern API Development (Part 3) : Add GraphQL
Mitch Chimwemwe Chanza
Mitch Chimwemwe Chanza

Posted on • Updated on • Originally published at blog.mitch.guru

Modern API Development (Part 3) : Add GraphQL

This post is a follow-up to the previous one at Part 2: Initiating Server. If you're looking to integrate GraphQL into an existing REST API, this guide is tailored for you. For those aiming to incorporate GraphQL into a new REST API, please refer to the instructions provided in the earlier posts. you can also follow the guide on my blog Mitch Guru

I'm delighted that you've reached this point, and I hope you're finding the journey enjoyable. As we continue with this series, there's still much more ground to cover. Now, let's shift our focus to the modernization of the API. I understand if you've been pondering, "What's considered modern about this server?" especially since it relies on the trusted 'express.' Take a moment to relax because you're about to delve into the contemporary aspects of this series.

we are going to cover the following topcs in this guide:

  • Install additional dependencies - GraphQl Tools, Apollo Server 4
  • Reconfigure the API to have separate routes, one for Graphql and one for REST

  • Implement GraphQL Endpont and resolvers

  • Test the API

Alright, no more talking – let's jump right in!

# install all dependencies for graphql to work in our API
pnpm api add graphql @apollo/server @graphql-tools/load @graphql-tools/graphql-file-loader
Enter fullscreen mode Exit fullscreen mode

The graphql package is now installed. we can proceed to configure our API.
firstly we need to create a graphql folder in the api folder. this is where our graphql files will be stored. i will create a main.graphql file in that folder.

We are going to have three types initialized in this file:

  • Response - custom type for responses
  • Query - required root graphql query type
  • Mutation - required root mution type
#api/graphql/main.graphql file
type Response {
    message: String!
    success: Boolean!
    error: String
}

type Query {
    hello: String
}

type Mutation {
    hello: String
}
Enter fullscreen mode Exit fullscreen mode

Up next we need to move some code from main.ts to be isolated to their own module. i am refering to the router which is set inside the main file.

//api/src/main.ts file
// ... rest of code
const router = express.Router();
// Set initial route
router.get("/", (_req, res) => {
    res.send({ message: "Monorepo API Configured!", success: true});
});
// .. rest of code 
Enter fullscreen mode Exit fullscreen mode

We are moving this part to a separate module called api/src/endpoints/rest/restEndpoints.ts. where we will do some modifications and export the module


// api/src/endpoints/rest/restEndpoints.ts
import express, { Router } from "express";

const restEndpoint: Router = express.Router();
// define routes
restEndpoint.get("/", (_req, res) => {
    res.send({ message: "Rest Endpoint Initialized!" });
})

export { restEndpoint }
Enter fullscreen mode Exit fullscreen mode

Now lets updated api/src/main.ts to use the rest endpoint.

// api/src/main.ts file
import { restEndpoint } from './endpoints';

// ... rest of code
// Set v1/api endpoint
app.use("/v1/api", restEndpoint);
// .. rest of code 
Enter fullscreen mode Exit fullscreen mode

As observed, the import originates from the ./endpoint module rather than ./endpoint/rest/restEndpoint. This is accomplished by incorporating a barrel file within the endpoints folder, which exports all submodule functionalities.
read more on what a barrel file is here.

We can run our server again to be sure that we did not break anything.

# run server
pnpm api dev
# our server should be running properly  as expected. 
# http://localhost:9100 or http://localhost:9000 depending on the PORT set in .env file
Enter fullscreen mode Exit fullscreen mode

Hold on, this isn't GraphQL yet; we're still operating a REST API! yes you are right, its one of the steps toward adding multiple routes tou our API.
Up next we are going to create another route for our API inside api/src/endpoints/graphql/graphqlEndpoints.ts module. Additionally we also need to create a resolver module api/src/endpoints/graphql/resolvers.ts which is going to be responsible for implementating all resolvers.

// api/src/endpoints/graphql/graphqlEndpoints.ts
import { ApolloServer } from '@apollo/server';
import { loadSchemaSync } from '@graphql-tools/load';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { resolvers } from '.';
// load schema from graphql files
const typeDefs=loadSchemaSync('./graphql/*.graphql', {
    loaders: [
        new GraphQLFileLoader()
    ]
});

// create apollo server
const server = new ApolloServer({
    typeDefs,
    resolvers
});

// export server
 export {  server}
Enter fullscreen mode Exit fullscreen mode

please note this is a minimal configuration for now.

// api/src/endpoints/graphql/resolvers.ts

export const resolvers = {
    Query: {
        hello: () => `Hello there welcome to api monorepo`
    },
    Mutation: {
        hello: (_: unknown, args: { name: string }) => `Hello ${args.name} welcome to api monorepo`

    }
}
Enter fullscreen mode Exit fullscreen mode

A sigh of relief we are now ready to test our REST + GraphQL APIs running on the same server.

# run server
pnpm api dev
# our server should be running properly  as expected. 
# http://localhost:9100/v1/rest and http://localhost:9000/v1/graphql 
Enter fullscreen mode Exit fullscreen mode

We can now happily commit our changes. and push them to our remote repository under the feature/part3 branch.

Watch out for Part 4 where we are going to integrate prisma ORM and part 5 Where we are going to introduce Hasura

Top comments (0)