Building an SSR React application with GraphQL and Deno

Alonso Garza
5 min readSep 24, 2020

--

Introduction

Unless you have been living under a rock, I am sure you have been hearing all the hype on Deno and/or GraphQL in the past few years. If you are intrigued by these technologies and enjoy learning new technologies by building with them, then you're in the right place!

Background on Deno

On May 13, 2018, Ryan Dahl, the creator of Node.js announced Deno, a new secure runtime for JavaScript and Typescript, during his “10 Things I Regret About Node.js” talk. In this talk, he mentioned some of the improvements that Deno aims to implement, such as abstracting the need for dependency solutions like NPM modules and package.json, supporting TypeScript out of the box by implementing a snapshotted TypeScript compiler, and breaking the sandboxed environment of the V8 engine.

After two years, on May 13, 2020, it was announced that Deno was ready for production, and has been backed my major cloud providers such as AWS Lambda, Azure Functions, and Google Cloud Run.

Overview

Throughout this article, we will be building a simple application to show Rick and Morty character information utilizing React, GraphQL, and Deno. During the process, we will be implementing server-side rendering with Deno’s Oak framework, as well as executing and caching GraphQL queries with Deno’s Obsidian library.

Let's get right into it!

First things first, we need to set up our file structure.

my-app
├── client
│ ├── Components
| ├── static
│ ├── app.tsx
│ └── client.tsx
├── server.tsx
├── deps.ts
├── serverDeps.tsx
├── staticFileMiddleware.ts
├── tsconfig.json
└── README.md
  • client is where we will store all our React components, static files, and theapp.tsx andclient.tsx files.
  • deps.ts and serverDeps.ts serve to import all of the Deno modules used throughout the application in one place.
  • staticFileMiddleware.ts will contain the logic to handle requests for static files.
  • tsconfig.json specifies the root files and the compiler options required to compile the TypeScript project.

Importing Deno Modules

Since we have determined all of the libraries that we will be importing on our client-side and server-side, we will begin by creating deps.ts files to import them into our application.

  • deps.ts is where we will be importing all the Deno modules that will be utilized in the client-side of the application.
  • serverDeps.ts is where we will be importing all the Deno modules that will be utilized in the server-side of the application.

Server-side Rendering

Now that we have imported all our modules in one central location, we can begin setting up our server. We will be utilizing Deno’s Oak middleware framework, and ReactDOMServer to render components to static markup.

If you are familiar with Express.js and/or Koa.js the structure for creating routers and endpoints with the Oak framework above should be very familiar. If you skim through the code there are two main things that differ from what you may be used to: the Deno.bundle function on line 17 and the HTML file at the bottom of the code block.

The Deno.bundle function is one of the great features that Deno provides. Similar to how you would use a bundler like Webpack to transpile and compile your application in Node.js, Deno has that functionality out of the box. The Deno.bundle function takes an entry point of the client-side code as an argument and transpiles and bundles the client-side file structure into a single javascript file to be sent to the client.

The second nuance above is the handlePage function, starting on line 38. When this function is invoked, it begins by utilizing the rendertoString function that is provided by the ReactDOMServer package. The React application is passed into this function and converts the entire application into an HTML string. So when a “GET” request for the root endpoint is received by the server, the entire React application is sent to the client as a string.

While the page does load faster, it is a marked down version of the page, meaning that it does not have any of the React functionality — this is why a request is made for the bundled javascript file on line 52. With server-side rendering, the React application never uses the render method and instead uses a hydrate method. Since the webpage is already rendered, the browser receives the bundled javascript file and parses through the rendered application, seamlessly reinjecting the React functionality into the application.

Client-side code and GraphQL

Now that we have covered server-side rendering from the server-side, let's take a look at how we are able to achieve this functionality on the client-side.

The code snippet above demonstrates the entry point into the client-side code. Notice how the hydrate method is used instead of render.

Pretty simple, right? Well, let’s look into how we can incorporate GraphQL fetching and caching functionality into our application.

In order to cache our GraphQL requests, we will be using the ObsidianWrapper . Similar to how you would wrap your application in a provider when using a React state management library, utilizing this wrapper supplies caching capabilities to our GraphQL requests. This improves the performance of the app, providing consistent data throughout the application and reducing the load on the backend.

Fetching and caching GraphQL requests

Finally, we will be taking a look at how we will be fetching and caching our GraphQL requests in the following code block.

We will first begin by importing the useObsidian hook which allows us to access the functionality provided by the wrapper. On line 31 we invoke the hook which gives us access to a few different methods. Here we are using the gather method which gives us the ability to fetch and cache GraphQL requests. The first parameter of this method is the GraphQL query and the second is an “options” parameter. For the sake of simplicity, we are using an external API and specifying to not destructure the query and response since this requires us to provide the application with the GraphQL schema. If you are curious about Obsidian’s normalization and destructuring capabilities, check out the article below.

Wrapping up

Now that we have built our core structure, we can add some additional functionality to query for more specific query information about each individual character. Finally, we add some minor styling to wrap it all up!

If you would like to have a look at the source code you can find the repository at https://github.com/Alonsog66/GraphQL-Deno-Medium-Demo.

Conclusion

And there you have it! As you can see there are some major changes when working with Deno as opposed to Node.js — we do not store external modules locally, TypeScript can be compiled out of the box, and we did not need to configure a webpack.config.js file to specify the compiling as Deno can natively perform all of the transpiling and bundling.
If you have any questions about any of this material covered feel free to post a comment below or reach out via LinkedIn.

Cheers!

--

--