Creating the Linc Demo

How we set up the ultimate demo environment using Linc

This is our live demo of Linc. It serves as an example of what you can accomplish with Linc environments. In this blog, we'll cover how Linc environments helped us to create a maintenance-free demo version of our production application with minimal effort.

Background

In creating a demo version of our product, we wanted to change our frontend/backend infrastructure as little as possible, but also end up with a demo version of our production app that would always stay in sync with our production app code.

Before creating our demo, our production app sourced all data from a single GraphQL API endpoint. Ideally, what we wanted was a second API endpoint that would offer the same, albeit read-only operations as our production API endpoint, and then on top of that, a way to instruct our frontend code to use this alternative endpoint when in demo mode.

With Linc environments and stable release preview URLs, implementing this turned out to be easy!

Creating new GraphQL API endpoint

The first thing we changed was our backend API code. Below is a snippet where we created a new GraphQL API endpoint to be used by our demo application:

app.use(
  '/demo/graphql',
  graphqlExpress(req => {
    return {
      schema: readOnlySchema,
    }
  })
)

This new GraphQL API uses a schema called readOnlySchema. To create this schema, we duplicated our existing production GraphQL API schema and stripped out all of its mutations leaving only queries, limiting this new demo GraphQL API to read-only operations. Here's an example snippet to give you an idea:

import { readAndWriteTypeDefs, readOnlyTypeDefs } from './type-defs'
import { queries, mutations } from './resolvers'

import { makeExecutableSchema } from 'graphql-tools'

// production GraphQL schema
export const readAndWriteSchema = makeExecutableSchema({
  typeDefs: readAndWriteTypeDefs,
  resolvers: [...queries, ...mutations], // pass in queries & mutations
})

// demo GraphQL schema
export const readOnlySchema = makeExecutableSchema({
  typeDefs: readOnlyTypeDefs,
  resolvers: [...queries], // only pass in queries
})

We also pass in the authentication credentials of a dummy user account into the GraphQL server context, effectively allowing users of this API to make queries as though they're logged in as the Linc user.

app.use(
  '/demo/graphql',
  graphqlExpress((req) => {
    return {
      schema: readOnlySchema,
+      context: {
+        token,
+        user_id
+      },
    }
  })
)

Add Demo Environment

After adding a demo GraphQL API endpoint, we then needed to create a Linc environment to enable our frontend code to use this new endpoint. An environment is a set of custom variables that get injected into your frontend code at runtime. These injected variables can be used to override various production settings defined in your code, among other uses. We achieve this using Linc preview URLs.

Linc Preview URLs are special URLs composed of your project's name, your code version, and a given Linc environment. This information instructs our infrastructure to deploy a given version of your code to a shareable preview link.

For instance, you can deploy any version of your code to a given environment:

preview link
version preview link

Or, alternatively, you can deploy the latest released version of your code in a given environment:

release preview link
release preview link

For our purposes, we created a DEMO environment containing a GRAPHQL_ENDPOINT variable linking to our our new API endpoint:

demo env 1
Demo environment with one variable

Now, whenever we deploy our code in this DEMO environment, the GRAPHQL_ENDPOINT variable will be automatically injected into our code at runtime. The next thing we needed to do was edit our frontend code to use this variable.

Using the Demo Environment

As mentioned, Linc environments can be used to override settings defined in your code.

Inline with the FAB documentation, we added a production-settings.json file to our frontend project to contain some default variables to override:

{
  "GRAPHQL_ENDPOINT": "https://graphql.linc.sh/graphql"
}

Here we defined our a GRAPHQL_ENDPOINT variable with the value of our production GraphQL API endpoint. When your code gets compiled with the FAB compiler, any variables defined in this file get injected into your code at run time just like Linc environments.

So what we're effectively doing here is defining our default API endpoint. We then altered our Apollo Client code to use the GRAPHQL_ENDPOINT variable at runtime:

import ApolloClient from 'apollo-boost'

// pull endpoint variable out of window object
const GRAPHQL_ENDPOINT = window.FAB_SETTINGS['GRAPHQL_ENDPOINT']

// set URI to endpoint variable
const client = new ApolloClient({
  uri: GRAPHQL_ENDPOINT,
})

export default client

If we run our code in the DEMO environment using the preview links mentioned above, the GRAPHQL_ENDPOINT variable defined in our production-settings.json file will get overridden by the GRAPHQL_ENDPOINT variable defined in the DEMO environment.

Demo friendly UI

The last thing we needed was a way to flag to our UI that it should switch to demo mode so that certain content and functionality could be altered or disabled for a safer demo experience. To do this, we added one new variable DEMO_MODE to our DEMO Environment like so:

demo env 2
Demo environment with two variables

Then, back in our frontend code, we added a new line to our production-settings.json file as follows:

{
  "GRAPHQL_ENDPOINT": "https://graphql.linc.sh/graphql",
+ "DEMO_MODE": false,
}

Here, we define the default value of the DEMO_MODE variable as false, overriding this value to true when we run our code in the DEMO environment.

After adding this extra variable, we created a new file called in our frontend code called config.js to pull out this variable from the window object and export it for use by our UI code.

export const DEMO_MODE = window.FAB_SETTINGS.DEMO_MODE

We edited a few parts of our UI code to render in a more demo-friendly fashion whenever the value of DEMO_MODE evaluates to true.

And this was our result:

demo mode
screenshot of Linc demo

Summing up

What did we accomplish here?

  • We created a read-only Graphql endpoint, with no mutations, only queries
  • Set up a DEMO environment with our new endpoint and a feature flag DEMO_MODE
  • Changed our frontend code to use FAB_SETTINGS

So what is the benefit of this implementation? Creating a demo version of your application often requires you to spin off separate demo versions of your frontend, database, and APIs. Each duplicated demo piece of infrastructure comes with its' own ongoing maintenance cost. This cost includes the time and energy required to keep the frontend/backend components of your production and demo applications sync.

Our implementation has almost no ongoing maintenance cost. We maintain one version of our frontend code, which has been slightly modified to run in a Linc environment. We maintain one API which exposes a production and a demo GraphQL endpoint, both of which talk to the same production database.

And better yet, for every commit we make to the frontend of our app, Linc gives us preview links against both environments, so we can always make sure both the demo and the production app are working.

pr flow with environment links
Linc Bot comment on Pull Request

This is just one example of what Linc environments can help you to build.

Back to the Blog Home