May 31st, 2020

First Look at Azure (Not So) Static Web Apps

Microsoft recently announced a Public Preview of Azure Static Web Apps. Reading through the official documentation, the following points piqued my interest:

  • Built-in integration with Azure Functions, so you can have some dynamic back-end with your static front-end.
  • Integration with Azure AD and a few other authentication providers. This might just be enough for simple use cases.
  • The service is free during the public preview. I could not find any hints on how much it may cost in the future, but I would imagine it will be very affordable.

In this post, I show how to set up a bare-bones Gatsby site with a simple GraphQL API on Azure Static Web Apps.

Gatsby Front-End

I assume that you already have Node, Git and the Gatsby CLI installed. If not, check out the official Gatsby tutorial.

First, create a new Gatsby site from the default starter. You can call it anything, but I called mine azure-webapp-gatsby-graphql.

gatsby new azure-webapp-gatsby-graphql https://github.com/gatsbyjs/gatsby-starter-default
cd azure-webapp-gatsby-graphql

Next, you need to install some packages that enable the use of Apollo GraphQL client for querying data in React components.

npm install gatsby-theme-apollo@2 apollo-boost @apollo/react-common @apollo/react-hooks isomorphic-unfetch

Now, open up your gatsby-config.js file. You need to add gatsby-theme-apollo to your plugins array. It will look something like this:

plugins: [
    `gatsby-theme-apollo`,    `gatsby-plugin-react-helmet`,
    // ...,
]

Create a new file src/gatsby-theme-apollo/client.js with the following code. You will have to come back to this file and specify the GraphQL endpoint URI once you know the domain name for your site.

import fetch from 'isomorphic-unfetch';
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-boost';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: '', // TODO - we will come back to this    fetch
  })
});

export default client;

Finally, open up src/index.js. You will now make some changes to have it query data using Apollo.

In the top section, add the following imports to get access to the packages we need.

import { gql } from 'apollo-boost';
import { ApolloProvider, useQuery } from '@apollo/react-hooks';
import client from '../gatsby-theme-apollo/client';
// ...

After all import statements, define a GraphQL Query. I've called it hello - it has to match the GraphQL type definitions and resolvers covered in the next section.

const QUERY = gql`
  {
    hello
  }
`;

Now, alter the IndexPage component by wrapping it with an ApolloProvider component and pulling in data with the useQuery hook.

In this example, we render an empty <div> if our query is still loading. When loading completes, we show the actual component and display data from our query in a <p> tag. Of course, you can use more complex logic, like showing some loading animation while the page is waiting for data to come in.

const IndexPage = () => {
  const { data, loading, error } = useQuery(QUERY);
  if (loading) return <div />
  return (
    <ApolloProvider client={client}>      <Layout>
        <SEO title="Home" />
        <h1>Hello!</h1>
        <p>{error ? 'Error' : data.hello}</p>        <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
          <Image />
        </div>
      </Layout>
    </ApolloProvider>  )
}

Back-End API

Create an api folder - this is where our Azure Function App code will live. I used the Azure Functions Extension for VS Code to generate a skeleton JavaScript Function App and therein an individual function called graphql. If this is your first time doing anything with Azure Functions, you might want to check out Microsoft's tutorial.

The api folder should look something like this:

api
├── graphql
│   ├── function.json
│   ├── index.js
│   └── sample.dat
├── host.json
├── local.settings.json
├── package.json
└── proxies.json

Now change your directory to the api folder and install the package required to run Apollo Server.

cd api
npm install apollo-server-azure-functions

Delete all the default code in api/graphql/index.js and replace it with the following. This is where you create the type definition and resolver for the hello query.

const { ApolloServer, gql } = require('apollo-server-azure-functions');

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => `Random hello #${Math.floor(Math.random() * 100)} from Azure Functions!`,
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

exports.graphqlHandler = server.createHandler();

Now you need to make some changes in the api/graphql/function.json file:

  1. In the inbound httpTrigger binding, add a route property. This determines the URL path of the API endpoint.
  2. In the outbound http binding, change the name property to $return.

The function.json file should look like this:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "graphql"    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"    }
  ]
}

Finally, Azure requires the node_modules folder of the Function App to be included in the code repository. To do that, edit the api/.gitignore file and delete the line which specifies node_modules/ as an exclusion. Then open the main .gitignore file in the root folder, find the entry for node_modules/ and replace it with /node_modules/. Now your main node_modules folder at the root of the repo will still be excluded from being committed to GitHub.

You are ready to deploy commit changes and push your code to a repo in GitHub.

Deploying to Azure

Now head over to the Azure portal and search for Static Web Apps, and click on New. Fill in the basic subscription and resource group details and pick a name and region for your app. Choose the appropriate GitHub repository.

Screenshot of filling out the basic information for a Static Web App in the Azure portal

In the next section, specify the Build Details as follows.

Screenshot of specifying the build detalis of the Static Web App in the Azure portal

Click on Review + create, make sure that all the details look good, and you are ready to deploy.

Once deployment has finished, you can go to your new resource and look up the domain name assigned to your application.

Screenshot of the Static Web App overview screen after deployment in the Azure Portal

But you are not quite done yet - Apollo client still doesn't know about the full URL of the API. Take note of the application URL. Open up the src/gatsby-theme-apollo/client.js file once more and add the API URL.

In my case, it looked like this:

import fetch from 'isomorphic-unfetch';
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-boost';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: new HttpLink({
    uri: 'https://salmon-forest-0f5115203.azurestaticapps.net/api/graphql',    fetch
  })
});

export default client;

Commit the changes and push to the repo. The site will now get automatically redeployed using a GitHub Action, which Azure created for you. You can check the progress on the Actions tab in your repo.

Once it finishes, browse to your newly deployed site, and you should see the default Gatsby starter page with one line of text sourced from the GraphQL API. Whenever you refresh the page, the number in the text should change, emphasising the dynamic nature of the API.

Screenshot of the final deployed site. This is the default Gatsby starter site with one line of dynamic content sourced from the API.

That's it! You have a Gatsby site with data sourced from a GraphQL back-end powered by Azure Functions.

© Siim Männart 2020