Building a GraphQL API
with TypeScript From Scratch
for an eCommerce Project

Cesarc
NicaSource
Published in
10 min readAug 22, 2022

Hello, everyone!

This is a hands-on article for building an API based on the main characteristics of GraphQL. The idea is that we try to recreate an electronic commerce system and adopt GraphQL to expose its information. For the project, we will also be using TypeScript. Even though there are a variety of packages that can be used to create APIs, we will use the Apollo server package.

Let’s start with the basics!

Before we dive into our challenge, it is important that we understand what GraphQL is and what it does; the official website describes GraphQL as a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

GraphQL vs. Rest API

The main reason to use GraphQL is that it helps us solve some problems that Rest API finds more difficult to deal with, among them:

  1. Prevents Over-Fetching

GraphQL allows you to perform queries based on a query language, while Rest APIs use different URLs to represent the requested data. Let’s analyze the following example:

Example of Product Fetching with Rest API
Example of Product Fetching with Rest API

As you can see, the image above represents a Rest API, where an application requests to obtain information about a specific product or several products. Now, if you want to see what this looks like in a GraphQL API, check the following images:

Example of Product Fetching with Rest API
Example of multiple Product Fetching with GraphQL

2. Prevents Over-Fetching

GraphQL can perform the same tasks as RestAPI. One of the main advantages is GraphQL’s ability to nest queries that can be achieved through a defined schema structure, which will be explained in deep in this article. As you can see in the example below, there are two Rest APIs requests to obtain product information based on the category:

Example of Nesting Products Under Categories with Rest API.

The complexity of the response will be related to the number of models requested. When we are working with a few models, such as categories and products, it’s easier to manage a short response. However, the complexity will increase when more models are added to the logic, such as category, product, image, name, price, etc.

In the image below, you can see how GraphQL makes a single request for both models:

Example of Nesting Products Under Categories with GraphQL

Project Implementation: eCommerce Store

Pre-requisites

To be able to run this project, it is necessary to install:

Getting Started

To get us started, go to visual studio code and create a directory and a package.json file to host our project’s code:

mkdir graphql-typescript-api-startercd graphql-typescript-api-starternpm init -y

We must then add the information below to the project’s package.json file to install the packages needed to run our project:

Now we will run the following command so the packages are downloaded to our development environment:

npm i

Configuring TypeScript

In this article, I’m focusing more on GraphQL, but TypeScript will eventually be implemented in the project as it will help us write a more robust code. So, to set it up, we first need to create a tsconfig.json file at the root of our project. To do so, we have to run the following command inside of the project:

touch tsconfig.json

After that, we must add the following code containing our compiler options:

Applying Hot Reloading and Scripts

Applying hot reloading in our local setup is fundamental for having a better development experience. Otherwise, we’ll need to quit and restart the server whenever we change our code.

In the package.json we created a few steps ago, we referenced the nodemon file, which we will be using to execute the following command and finish its configuration:

touch nodemon.json

When the file is created, we will add the following code inside it so we can compile the project correctly when needed:

Now that we have configured Nodemon, we can quickly review the command that we will use to run our project. It is found in the script section of the package.json file:

npm run dev

Setting up the Server with Apollo

This is the most important part of the article. Here we will cover the essential steps for creating our API. When we installed the package.json, we installed three main packages:

  • express: which is the Javascript server-side framework.
  • apollo-server-express: which allows us to set up a GraphQL API on top of Express.
  • graphql: which enable us to write GraphQL in Javascript.

Now, it is time to create the configuration file for our server; for it, you will need to execute the following commands:

mkdir src 
touch src/index.ts

So far, we have managed to completely set up TypeScript, which will help us compile our code. Now we need to introduce the following lines of code:

Based on the lines of the code above, here’s a short explanation of some of the most important ones:

  • Line 5 shows an asynchronous function to start the server created.
    At the end of the code, (in line 39), we can see the function is called (startServer() ).
  • Line 7 creates an instance of express (const app = express() ) and then creates an HTTP server(const httpServer = createServer(app)).
  • Line 10 declares TypeDefs which defines our API schema.
    Here we describe what data can be read and mutated by the front end. For example:
  • Fetch a list of items;
  • Fetch details about a profile;
  • Login a user;
  • Sign up a user.
  • Line 16 declares Resolvers which are responsible for handling the logic and the response for each typeDef we define. It’s where the business logic is executed. Each typeDef will have a matching resolver. For example, if the typeDefs, defines a query to fetch a list of items, we’ll need a matching resolver to handle a question like “find the items in the database and return them”
  • Line 22 initializes an instance of ApolloServer, passing the typeDefs and resolvers. This creates our GraphQL server, but as we’re using Express, we’ll need to apply it as a middleware in the next step.
  • Line 27 awaits for the apolloServer.start() before applying the ApolloServer instance as middleware.
  • Line 29 applies The ApolloServer instance as middleware to the Express instance, enabling the GraphQL server. Different from Rest API, in GraphQL, a single endpoint is served.
    Apollo default sets this endpoint to /graphql, but I’ve updated it to /api. This is optional.

You will find line 34 easier to read if you’ve already built Express applications. Here, we are requesting the server to listen to a specific port and log something to the server. We first look in a .env file for the “PORT key,” and if it doesn’t exist, then we’ll use 9000 by default.

So far, this is the structure of our project:

Now that our project is structured, we are ready to run it with the following command:

npm run dev

If you don’t get any errors, you can go to your preferred browser and see the following output:

Everything we’ll do from now on will be built on top of this foundational mental model:

  • typeDefs: describe the queries and mutations available in the API;
  • resolvers: handle the logic and send the data back in the response.

We will separate the typeDefs and resolvers into separate files to keep our project clearer. To do this, we need to run the following commands:

touch src/resolver.ts
touch src/schema.ts

Setting up your schema types

Earlier in the article, when we discussed under-fetching, we talked about a schema structure. The schema definition language (SDL) is a GraphQL specification that defines it as human-readable.

Scalar types

Scalar types are similar to primitive types in our favorite programming language. They always resolve to concrete data.

GraphQL’s default scalar types are:

  • Int: a signed 32‐bit integer;
  • Float: a signed double-precision floating-point value;
  • String: a UTF‐8 character sequence;
  • Boolean: true or false;

ID (serialized as a string): a unique identifier often used to re-fetch an object or as the key for a cache. Although serialized as a string, an ID is not intended to be human-readable.

As a result, in our project, we have the following:

Query type

A query type is a particular object that defines all top-level entry points for queries that clients execute against the server. Each field of the query type specifies the name and returns the type of a different entry point.

Object types

Most of the types you define in a GraphQL schema are object types. An object type contains a collection of fields, each of which has its type.

Applying typeDefs and resolvers for eCommerce

Before we start our eCommerce example, we need to import a file that will simulate a database and its main relations (such as products and categories). It will be added to the following file:

cd src
mkdir db
touch db/db.ts

The above database will be used to relate products and categories in the following way:

Product-Category relationship

typeDefs.ts

Our next step will be to create the object types for product and category

In lines 12 and 18, we have created the relationship between product, object types, and categories, which will help us represent the data delivered when the API is consumed.

Now we are going to connect these types of objects with the right type of query:

products: [Product!]!categories: [Category!]!

Now that we have updated our schemas, it is time to configure our resolvers. They will be in charge of returning the information. But, before that, we need to talk about the context argument.

The context argument

The context argument is useful for passing things that any resolver might need, like authentication scope, database connections, and custom fetch functions. If you’re using data loaders to batch requests across resolvers, you can also attach them to the context. In our case, we are going to pass our database object. It will help us obtain information to resolve our requests from our fake database. Therefore, we must add the following lines of code to our index.js file.

import { db } from './db/db'const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: {db}
})

resolver.ts

Now we can say that we are ready to configure our resolvers. First of all, we must go to our resolve file and complete it with the following lines:

import { Product, Category } from '../db/db'export interface CtxArgs{ 
db:{
products: Product[]
categories: Category[]
}
}

Then, we need to import the interfaces to represent the types of objects used in our application. If we go to the db.ts file, we will find the product and the category interfaces. They reflect the type of objects and the properties that we use to type our arrays of objects.

Based on the two interfaces we have imported, we had to create a new one called CtxArgs, which is in charge of mapping the data types to be passed to our resolvers.

Thanks to TypeScript, our editor can detect which type of object is the one we need to use. Once these lines of code are added, we will have the following result:

And in our playground the following

If we wanted to combine the objects into something like the image below:

We would not be able to because we are not getting the Category from the query object but the product object type. So we must create a resolver for the product and the category. You can do this as follows:

The modification implemented will result in the following outputs:

We have now reached the end of this practical GraphQL API guide. I hope you have enjoyed it and learned new tricks to make your API creation easier!

In case you want to check out this project in deep, check out this GitHub repository.

Don’t forget to like the article and save it for later!

I’ll see you soon!

Reference:

Modern GraphQL Crash Course: https://www.youtube.com/watch?v=qux4-yWeZvo

--

--