Enhancing Express JS Server with Apollo GraphQL

Selvaganesh
5 min readAug 13, 2023

--

In this blog, we will discuss how to implement Graphql into an existing node js express server with minimal changes.

Often, we would like to integrate a new technology with an existing REST API. However, most of the time, we end up doing research and creating a new repo rather than reusing the existing code and infrastructure.

Consider the example below with a straightforward Express server
In this example, a microservice saves data to Redis and retrieves user information.

import * as express from 'express'
import * as swaggerUi from 'swagger-ui-express'
import * as swaggerDocument from './swagger.json'
import * as Redis from './utils/redis'
import AppRouter from './routes/app.route'

const app = express()

app.use(express.json())
app.use(Redis.attachRedisClient)

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument))

app.use('/api/', AppRouter)

app.listen(3000, () => {
console.log(`server started on 3000!`)
})

We have REST controllers that add user information to a Redis instance.The route /add is a POST method allows to create a user.

import * as express from 'express'
import * as appController from '../controller/app.controller'

let router = express.Router()
router.post('/add', appController.addUser)
router.post('/fetch', appController.getUser)
router.get('/health', appController.health)

export default router

Add a new user to the database as shown below.

Now we want to utilise GraphQL to retrieve the user information. To accomplish this, use the following steps to add apollo-server to existing code.

Install the dependencies

Add the following to enable graphql middleware.

npm install @apollo/server graphql

The apollo server has the express middleware which helps to expose the existing service into graphql

Add the expressMiddleware into `index.js`

// Graphql Imports
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';

Initialize the apollo server which accepts two parameter

  • Typedefs
  • Resolvers

Create an entry point to expose the graphql.

app.use(
'/graphql',
cors(),
express.json(),
expressMiddleware(server, {
context: async ({ request, response }) => ({
// Add optional configuration options
request: request,
response: response,
}),
})
)

Resolvers and TypeDefs

Add the queryUserDetails and schemaDetails to the typedefs; the query will take one input object and return the user response as shown below.

const typeDefs = `
input QueryUserRequest{
id: String!
}

type UserResponse {
id: Int
name: String
role: String
org: String
}

type QueryUserResponse{
success: Boolean
errors: ErrorInfo
user: UserResponse
}

type ErrorInfo{
code:String
message:String
}

type Query {
queryUserDetails(request:QueryUserRequest): QueryUserResponse
}

`
export default typeDefs

Define the resolver responsible for data manipulation.

import { getUser } from '../controller/app.controller'
async function queryUserDetails(obj, args, context) {
let { request, response } = context
request.body = args['request']
return await getUser(request, response)
}

const resolvers = {
Query: {
queryUserDetails: queryUserDetails,
},
}

export default resolvers

Define the Typescript interface as follows


interface UserResponse {
success: Boolean
user: User
errors: Error
}

interface User {
id: String
name: String
role: String
org: String
}
interface Error {
code: String
message: String
}

The getUser function will contain controller logic that retrieves user information from the Redis database using the user id. The code below explains how to handle all success and error circumstances.

export async function getUser(request, response) {
let result: UserResponse = {
success: false,
user: null,
errors: null,
}
try {
let userResult = await request.redisClient.get(request.body.id)
if (userResult == null) {
result.errors = {
code: '200',
message: 'No result',
}
result.success = true
} else {
result.user = JSON.parse(userResult)
result.success = true
}
} catch (error) {
result.success = true
result.errors = {
code: '500',
message: error.message,
}
}
return result
}

Start the server using `npm run dev` command and navigate to the graphql url

http://localhost:3000/graphql

Apollo Server Interface

Execute the below query to fetch the user details

query queryUserDetails{
queryUserDetails(request: {
id:"1"
}) {
success
errors {
code
message
}
user {
name
org
role
}
}
}

Simultaneously, navigate to http://localhost:3000/api-docs, where the existing express rest is running on the same port. Here, we may add the user by making use of the rest resource that is already in place.

This makes it easier to add graphql to current code without reworking the resources.

Code repo used in this blog is available here
https://github.com/ganny26/express-apollo-graphql

Advantages

  • We can server both REST and GraphQL at the same code using Express
  • It also maintains the existing infrastructure and authentication

Best practices to consider

Some best practices to follow add the plugin `ApolloServerPluginDrainHttpServer` to ensure that the express server shuts down gracefully

import { expressMiddleware } from '@apollo/server/express4'
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'
import resolvers from './resolvers/user.resolvers'
import typeDefs from './typedefs/typeDefs'
import * as http from 'http'
const app = express()

async function startServer() {
const httpServer = http.createServer(app)
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production',
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
})
await server.start()
}

Conclusion:

To conclude, this blog has delved into the seamless integration of GraphQL into an established Node.js Express server with minimal changes. Please let me know in the comments if you’re using a different middleware, and I’ll write an article about

--

--

Responses (1)