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.
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
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
type Response {
message: String!
success: Boolean!
error: String
}
type Query {
hello: String
}
type Mutation {
hello: String
}
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.
// ... 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
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 }
Now lets updated api/src/main.ts
to use the rest endpoint.
import { restEndpoint } from './endpoints';
// ... rest of code
// Set v1/api endpoint
app.use("/v1/api", restEndpoint);
// .. rest of code
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
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}
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`
}
}
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
We can now happily commit our changes. and push them to our remote repository under the feature/part3 branch.