An introduction GraphQL with AWS AppSync

An introduction GraphQL with AWS AppSync

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

GraphQL is becoming increasingly popular. The problem is that if you are a front-end developer, you are only half of the way there. GraphQL is not just a client technology. The server also has to be implemented according to the specification. This means that in order to implement GraphQL into your application, you need to learn not only GraphQL on the front end, but also GraphQL best practices, server-side development, and everything that goes along with it on the back end.

There will come a time when you will also have to deal with issues like scaling your server, complex authorization scenarios, malicious queries, and more issues that require more expertise and even deeper knowledge around what is traditionally categorized as back-end development.

Thankfully, we have an array of managed back-end service providers today that allow front-end developers to only worry about implementing features on the front end without having to deal with all of the traditional back-end work.

Services like Firebase (API) / AWS AppSync (database), Cloudinary (media), Algolia (search) and Auth0 (authentication) allow us to offload our complex infrastructure to a third-party provider and instead focus on delivering value to end users in the form of new features instead.

In this tutorial, we’ll learn how to take advantage of AWS AppSync, a managed GraphQL service, to build a full-stack application without writing a single line of back-end code.

While the framework we’re working in is React, the concepts and API calls we will be using are framework-agnostic and will work the same in Angular, Vue, React Native, Ionic or any other JavaScript framework or application.

We will be building a restaurant review app. In this app, we will be able to create a restaurant, view restaurants, create a review for a restaurant, and view reviews for a restaurant.

The tools and frameworks that we will be using are React, AWS Amplify, and AWS AppSync.

AWS Amplify is a framework that allows us to create and connect to cloud services, like authentication, GraphQL APIs, and Lambda functions, among other things. AWS AppSync is a managed GraphQL service.

We’ll use Amplify to create and connect to an AppSync API, then write the client side React code to interact with the API.

View Repo

Getting started

The first thing we’ll do is create a React project and move into the new directory:

npx create-react-app ReactRestaurants

cd ReactRestaurants

Next, we’ll install the dependencies we’ll be using for this project. AWS Amplify is the JavaScript library we’ll be using to connect to the API, and we’ll use Glamor for styling.

yarn add aws-amplify glamor

The next thing we need to do to is install and configure the Amplify CLI:

npm install -g @aws-amplify/cli

amplify configure

Amplify’s configure will walk you through the steps needed to begin creating AWS services in your account. For a walkthrough of how to do this, check out this video.

Now that the app has been created and Amplify is ready to go, we can initialize a new Amplify project.

amplify init

Amplify init will walk you through the steps to initialize a new Amplify project. It will prompt you for your desired project name, environment name, and text editor of choice. The CLI will auto-detect your React environment and select smart defaults for the rest of the options.

Creating the GraphQL API

One we’ve initialized a new Amplify project, we can now add the Restaurant Review GraphQL API. To add a new service, we can run the amplify add command.

amplify add api

This will walk us through the following steps to help us set up the API:

? Please select from one of the below mentioned services GraphQL
? Provide API name bigeats
? Choose an authorization type for the API API key
? Do you have an annotated GraphQL schema? N
? Do you want a guided schema creation? Y
? What best describes your project: Single object with fields
? Do you want to edit the schema now? Y

The CLI should now open a basic schema in the text editor. This is going to be the schema for our GraphQL API.

Paste the following schema and save it.

// amplify/backend/api/bigeats/schema.graphql

type Restaurant @model {
  id: ID!
  city: String!
  name: String!
  numRatings: Int
  photo: String!
  reviews: [Review] @connection(name: "RestaurantReview")
}
type Review @model {
  rating: Int!
  text: String!
  createdAt: String
  restaurant: Restaurant! @connection(name: "RestaurantReview")
}

In this schema, we’re creating two main types: Restaurant and Review. Notice that we have @model and @connection directives in our schema.

These directives are part of the GraphQL Transform tool built into the Amplify CLI. GraphQL Transform will take a base schema decorated with directives and transform our code into a fully functional API that implements the base data model.

If we were spinning up our own GraphQL API, then we’d have to do all of this manually:

  1. Define the schema
  2. Define the operations against the schema (queries, mutations, and subscriptions)
  3. Create the data sources
  4. Write resolvers that map between the schema operations and the data sources.

With the @model directive, the GraphQL Transform tool will scaffold out all schema operations, resolvers, and data sources so all we have to do is define the base schema (step 1). The @connection directive will let us model relationships between the models and scaffold out the appropriate resolvers for the relationships.

In our schema, we use @connection to define a relationship between Restaurant and Reviews. This will create a unique identifier for the restaurant ID for the review in the final generated schema.

Now that we’ve created our base schema, we can create the API in our account.

amplify push
? Are you sure you want to continue? Yes
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes

Because we’re creating a GraphQL application, we typically would need to write all of our local GraphQL queries, mutations and subscriptions from scratch. Instead, the CLI will be inspecting our GraphQL schema and then generating all of the definitions for us and saving them locally for us to use.

After this is complete, the back end has been created and we can begin accessing it from our React application.

If you’d like to view your AppSync API in the AWS dashboard, visit https://console.aws.amazon.com/appsync and click on your API. From the dashboard you can view the schema, data sources, and resolvers. You can also perform queries and mutations using the built-in GraphQL editor.

Building the React client

Now that the API is created and we can begin querying for and creating data in our API. There will be three operations we will be using to interact with our API:

  1. Define the schema
  2. Define the operations against the schema (queries, mutations, and subscriptions)
  3. Create the data sources
  4. Write resolvers that map between the schema operations and the data sources.

Before we start building the app, let’s take a look at how these operations will look and work.

Interacting with the AppSync GraphQL API

When working with a GraphQL API, there are many GraphQL clients available.

We can use any GraphQL client we’d would like to interact with an AppSync GraphQL API, but there are two that are configured specifically to work most easily. These are the Amplify client (what we will use) and the AWS AppSync JS SDK (similar API to Apollo client).

The Amplify client is similar to the fetch API in that it is promise-based and easy to reason about. The Amplify client does not support offline out of the box. The AppSync SDK is more complex but does support offline out of the box.

To call the AppSync API with Amplify, we use the API category. Here’s an example of how to call a query:

import { API, graphqlOperation } from 'aws-amplify'
import * as queries from './graphql/queries'

const data = await API.graphql(graphqlOperation(queries.listRestaurants))

For a mutation, it is very similar. The only difference is we need to pass in a a second argument for the data we are sending in the mutation:

import { API, graphqlOperation } from 'aws-amplify'
import * as mutations from './graphql/mutations'

const restaurant = { name: "Babalu", city: "Jackson" }
const data = await API.graphql(graphqlOperation(
  mutations.createRestaurant,
  { input: restaurant }
))

We use the graphql method from the API category to call the operation, wrapping it in graphqlOperation, which parses GraphQL query strings into the standard GraphQL AST.

We’ll be using this API category for all of our GraphQL operation in the app.

Here is the repo containing the final code for this project.

Configuring the React app with Amplify

The first thing we need to do in our app is configure it to recognize our Amplify credentials. When we created our API, the CLI created a new file called aws-exports.js in our src folder.

This file is created and updated for us by the CLI as we create, update and delete services. This file is what we’ll be using to configure the React application to know about our services.

To configure the app, open up src/index.js and add the following code:

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, we will create the files we will need for our components. In the src directory, create the following files:

  • Header.js
  • Restaurant.js
  • Review.js
  • CreateRestaurant.js
  • CreateReview.js

Creating the components

While the styles are referenced in the code snippets below, the style definitions have been omitted to make the snippets less verbose. For style definitions, see the final project repo.

Next, we’ll create the Header component by updating [src/Header.js](https://github.com/dabit3/bigeats/blob/master/src/Header.js "src/Header.js").

// src/Header.js

import React from 'react'
import { css } from 'glamor'
const Header = ({ showCreateRestaurant }) => (
  <div {...css(styles.header)}>
    <p {...css(styles.title)}>BigEats</p>
    <div {...css(styles.iconContainer)}>
      <p {...css(styles.icon)} onClick={showCreateRestaurant}>+</p>
    </div>
  </div>
)

export default Header

Now that our Header is created, we’ll update [src/App.js](https://github.com/dabit3/bigeats/blob/master/src/App.js "src/App.js"). This file will hold all of the interactions with the API, so it is pretty large. We’ll define the methods and pass them down as props to the components that will call them.

// src/App.js

import React, { Component } from 'react'
import { API, graphqlOperation } from 'aws-amplify'

import Header from './Header'
import Restaurants from './Restaurants'
import CreateRestaurant from './CreateRestaurant'
import CreateReview from './CreateReview'
import Reviews from './Reviews'
import * as queries from './graphql/queries'
import * as mutations from './graphql/mutations'

class App extends Component {
  state = {
    restaurants: [],
    selectedRestaurant: {},
    showCreateRestaurant: false,
    showCreateReview: false,
    showReviews: false
  }
  async componentDidMount() {
    try {
      const rdata = await API.graphql(graphqlOperation(queries.listRestaurants))
      const { data: { listRestaurants: { items }}} = rdata
      this.setState({ restaurants: items })
    } catch(err) {
      console.log('error: ', err)
    }
  }
  viewReviews = (r) => {
    this.setState({ showReviews: true, selectedRestaurant: r })
  }
  createRestaurant = async(restaurant) => {
    this.setState({
      restaurants: [...this.state.restaurants, restaurant]
    })
    try {
      await API.graphql(graphqlOperation(
        mutations.createRestaurant,
        {input: restaurant}
      ))
    } catch(err) {
      console.log('error creating restaurant: ', err)
    }
  }
  createReview = async(id, input) => {
    const restaurants = this.state.restaurants
    const index = restaurants.findIndex(r => r.id === id)
    restaurants[index].reviews.items.push(input)
    this.setState({ restaurants })
    await API.graphql(graphqlOperation(mutations.createReview, {input}))
  }
  closeModal = () => {
    this.setState({
      showCreateRestaurant: false,
      showCreateReview: false,
      showReviews: false,
      selectedRestaurant: {}
    })
  }
  showCreateRestaurant = () => {
    this.setState({ showCreateRestaurant: true })
  }
  showCreateReview = r => {
    this.setState({ selectedRestaurant: r, showCreateReview: true })
  }
  render() {
    return (
      <div>
        <Header showCreateRestaurant={this.showCreateRestaurant} />
        <Restaurants
          restaurants={this.state.restaurants}
          showCreateReview={this.showCreateReview}
          viewReviews={this.viewReviews}
        />
        {
          this.state.showCreateRestaurant && (
            <CreateRestaurant
              createRestaurant={this.createRestaurant}
              closeModal={this.closeModal}   
            />
          )
        }
        {
          this.state.showCreateReview && (
            <CreateReview
              createReview={this.createReview}
              closeModal={this.closeModal}   
              restaurant={this.state.selectedRestaurant}
            />
          )
        }
        {
          this.state.showReviews && (
            <Reviews
              selectedRestaurant={this.state.selectedRestaurant}
              closeModal={this.closeModal}
              restaurant={this.state.selectedRestaurant}
            />
          )
        }
      </div>
    );
  }
}
export default App

We first create some initial state to hold the restaurants array that we will be fetching from our API. We also create Booleans to control our UI and a selectedRestaurant object.

In componentDidMount, we query for the restaurants and update the state to hold the restaurants retrieved from the API.

In createRestaurant and createReview, we send mutations to the API. Also notice that we provide an optimistic update by updating the state immediately so that the UI gets updated before the response comes back in order to make our UI snappy.

Next, we’ll create the Restaurants component ([src/Restaurants.js](https://github.com/dabit3/bigeats/blob/master/src/Restaurants.js "src/Restaurants.js")).

// src/Restaurants.js

import React, { Component } from 'react';
import { css } from 'glamor'

class Restaurants extends Component {
  render() {
    const { restaurants, viewReviews } = this.props
    return (
      <div {...css(styles.container)}>
        {
          restaurants.length === Number(0) && (
            <h1
              {...css(styles.h1)}
            >Create your first restaurant by clicking +</h1>
          )
        }
        {
          restaurants.map((r, i) => (
            <div key={i}>
              <img
                src={r.photo}
                {...css(styles.image)}
              />
              <p {...css(styles.title)}>{r.name}</p>
              <p {...css(styles.subtitle)}>{r.city}</p>
              <p
                onClick={() => viewReviews(r)}
                {...css(styles.viewReviews)}
              >View Reviews</p>
              <p
                onClick={() => this.props.showCreateReview(r)}
                {...css(styles.createReview)}
              >Create Review</p>
            </div>
          ))
        }
      </div>
    );
  }
}

export default Restaurants

This component is the main view of the app. We map over the list of restaurants and show the restaurant image, its name and location, and links that will open overlays to show reviews and create a new review.

Next, we’ll look at the Reviews component ([src/Reviews.js](https://github.com/dabit3/bigeats/blob/master/src/Reviews.js "src/Reviews.js")). In this component, we map over the list of reviews for the chosen restaurant.

// src/Reviews.js

import React from 'react'
import { css } from 'glamor'

class Reviews extends React.Component {
  render() {
    const { closeModal, restaurant } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.container)}>
          <h1>{restaurant.name}</h1>
          {
            restaurant.reviews.items.map((r, i) => (
              <div {...css(styles.review)} key={i}>
                <p {...css(styles.text)}>{r.text}</p>
                <p {...css(styles.rating)}>Stars: {r.rating}</p>
              </div>
            ))
          }
          <p onClick={closeModal}>Close</p>
        </div>
      </div>
    )
  }
}

export default Reviews

Next, we’ll take a look at the CreateRestaurant component ([src/CreateRestaurant.js](https://github.com/dabit3/bigeats/blob/master/src/CreateRestaurant.js "src/CreateRestaurant.js")). This component holds a form that keeps up with the form state. The createRestaurant class method will call this.props.createRestaurant, passing in the form state.

// src/CreateRestaurant.js

import React from 'react'
import { css } from 'glamor';

class CreateRestaurant extends React.Component {
  state = {
    name: '', city: '', photo: ''
  }
  createRestaurant = () => {
    if (
      this.state.city === '' || this.state.name === '' || this.state.photo === ''
    ) return
    this.props.createRestaurant(this.state)
    this.props.closeModal()
  }
  onChange = ({ target }) => {
    this.setState({ [target.name]: target.value })
  }
  render() {
    const { closeModal } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.form)}>
          <input
            placeholder='Restaurant name'
            {...css(styles.input)}
            name='name'
            onChange={this.onChange}
          />
          <input
            placeholder='City'
            {...css(styles.input)}
            name='city'
            onChange={this.onChange}
          />
          <input
            placeholder='Photo'
            {...css(styles.input)}
            name='photo'
            onChange={this.onChange}
          />
          <div
            onClick={this.createRestaurant}
            {...css(styles.button)}
          >
            <p
              {...css(styles.buttonText)}
            >Submit</p>
          </div>
          <div
            {...css([styles.button, { backgroundColor: '#555'}])}
            onClick={closeModal}
          >
            <p
              {...css(styles.buttonText)}
            >Cancel</p>
          </div>
        </div>
      </div>
    )
  }
}

export default CreateRestaurant

Next, we’ll take a look at the CreateReview component ([src/CreateReview.js](https://github.com/dabit3/bigeats/blob/master/src/CreateReview.js "src/CreateReview.js")). This component holds a form that keeps up with the form state. The createReview class method will call this.props.createReview, passing in the restaurant ID and the form state.

// src/CreateReview.js

import React from 'react'
import { css } from 'glamor';
const stars = [1, 2, 3, 4, 5]
class CreateReview extends React.Component {
  state = {
    review: '', selectedIndex: null
  }
  onChange = ({ target }) => {
    this.setState({ [target.name]: target.value })
  }
  createReview = async() => {
    const { restaurant } = this.props
    const input = {
      text: this.state.review,
      rating: this.state.selectedIndex + 1,
      reviewRestaurantId: restaurant.id
    }
    try {
      this.props.createReview(restaurant.id, input)
      this.props.closeModal()
    } catch(err) {
      console.log('error creating restaurant: ', err)
    }
  }
  render() {
    const { selectedIndex } = this.state
    const { closeModal } = this.props
    return (
      <div {...css(styles.overlay)}>
        <div {...css(styles.form)}>
          <div {...css(styles.stars)}>
            {
              stars.map((s, i) => (
                <p
                  key={i}
                  onClick={() => this.setState({ selectedIndex: i })}
                  {...css([styles.star, selectedIndex === i && { backgroundColor: 'gold' }])}
                >{s} star</p>
              ))
            }
          </div>
          <input
            placeholder='Review'
            {...css(styles.input)}
            name='review'
            onChange={this.onChange}
          />
          <div
            onClick={this.createReview}
            {...css(styles.button)}
          >
            <p
              {...css(styles.buttonText)}
            >Submit</p>
          </div>
          <div
            {...css([styles.button, { backgroundColor: '#555'}])}
            onClick={closeModal}
          >
            <p
              {...css(styles.buttonText)}
            >Cancel</p>
          </div>
        </div>
      </div>
    )
  }
}

export default CreateReview

Running the app

Now that we have built our back-end, configured the app and created our components, we’re ready to test it out:

npm start

Now, navigate to [http://localhost:3000](http://localhost:3000 "http://localhost:3000"). Congratulations, you’ve just built a full-stack serverless GraphQL application!

Conclusion

The next logical step for many applications is to apply additional security features, like authentication, authorization and fine-grained access control. All of these things are baked into the service. To learn more about AWS AppSync security, check out the documentation.

If you’d like to add hosting and a Continuous Integration/Continuous Deployment pipeline for your app, check out the Amplify Console.

I also maintain a couple of repositories with additional resources around Amplify and AppSync: Awesome AWS Amplify and Awesome AWS AppSync.

AWS Certified Solution Architect Associate

AWS Certified Solution Architect Associate

This course will help you in the final preparation steps for your AWS Certified Solution Architect Associate - Certification Exam.

In this course , we will go through the different concepts that get asked in the exam and map them to the different domain objectives for the exam.

This is good revision guide before you attempt the certification exam

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about AWS

A Complete Guide on Deploying a Node app to AWS with Docker

AWS Certified Solutions Architect - Associate 2019

AWS Lambda vs. Azure Functions vs. Google Functions

AWS Certified Developer - Associate 2019

Create and Deploy AWS and AWS Lambda using Serverless Framework

Introduction To AWS Lambda

Why Azure is Better Than AWS

How to set up GraphQL with Golang: a deep dive from basics to advanced

How to set up GraphQL with Golang: a deep dive from basics to advanced

GraphQL has become a buzzword over the last few years after Facebook made it open-source. I have tried GraphQL with the Node.js, and I agree with all the buzz about the advantages and simplicity of GraphQL.

GraphQL has become a buzzword over the last few years after Facebook made it open-source. I have tried GraphQL with the Node.js, and I agree with all the buzz about the advantages and simplicity of GraphQL.

So what is GraphQL? This is what the official GraphQL definition says:

GraphQL is a query language for APIs and 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.
I recently switched to Golang for a new project I’m working on (from Node.js) and I decided to try GraphQL with it. There are not many library options with Golang but I have tried it with Thunder, graphql, graphql-go, and gqlgen. And I have to say that gqlgen is winning among all the libraries I have tried.

gqlgen is still in beta with latest version 0.7.2 at the time of writing this article, and it’s rapidly evolving. You can find their road-map here. And now 99designs is officially sponsoring them, so we will see even better development speed for this awesome open source project. vektah and neelance are major contributors, and neelance also wrote graphql-go.

So let’s dive into the library semantics assuming you have basic GraphQL knowledge.

Highlights

As their headline states,

GraphQL is a query language for APIs and 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.
I think this is the most promising thing about the library: you will never see map[string]interface{} here, as it uses a strictly typed approach.

Apart from that, it uses a Schema first Approach: so you define your API using the graphql Schema Definition Language. This has its own powerful code generation tools which will auto-generate all of your GraphQL code and you will just need to implement the core logic of that interface method.

I have divided this article into two phases:

  • The basics: Configuration, Mutations, Queries, and Subscription
  • The advanced: Authentication, Dataloaders, and Query Complexity

Phase 1: The Basics - Configuration, Mutations, Queries, and Subscriptions

We will use a video publishing site as an example in which a user can publish a video, add screenshots, add a review, and get videos and related videos.

mkdir -p $GOPATH/src/github.com/ridhamtarpara/go-graphql-demo/

Create the following schema in the project root:

type User {
    id: ID!
    name: String!
    email: String!
}

type Video {
    id: ID!
    name: String!
    description: String!
    user: User!
    url: String!
    createdAt: Timestamp!
    screenshots: [Screenshot]
    related(limit: Int = 25, offset: Int = 0): [Video!]!
}

type Screenshot {
    id: ID!
    videoId: ID!
    url: String!
}

input NewVideo {
    name: String!
    description: String!
    userId: ID!
    url: String!
}

type Mutation {
    createVideo(input: NewVideo!): Video!
}

type Query {
    Videos(limit: Int = 25, offset: Int = 0): [Video!]!
}

scalar Timestamp

Here we have defined our basic models and one mutation to publish new videos, and one query to get all videos. You can read more about the graphql schema here. We have also defined one custom type (scalar), as by default graphql has only 5 scalar types that include Int, Float, String, Boolean and ID.

So if you want to use custom type, then you can define a custom scalar in schema.graphql (like we have defined Timestamp) and provide its definition in code. In gqlgen, you need to provide marshal and unmarshal methods for all custom scalars and map them to gqlgen.yml.

Another major change in gqlgen in the last version is that they have removed the dependency on compiled binaries. So add the following file to your project under scripts/gqlgen.go.

// +build ignore

package main

import "github.com/99designs/gqlgen/cmd"

func main() {
	cmd.Execute()
}

and initialize dep with:

dep init

Now it’s time to take advantage of the library’s codegen feature which generates all the boring (but interesting for a few) skeleton code.

go run scripts/gqlgen.go init

which will create the following files:

gqlgen.yml — Config file to control code generation.

**generated.go **— The generated code which you might not want to see.

models_gen.go — All the models for input and type of your provided schema.

resolver.go — You need to write your implementations.

server/server.go — entry point with an http.Handler to start the GraphQL server.

Let’s have a look at one of the generated models of the Video type:

type Video struct {
	ID          string        `json:"id"`
	Name        string        `json:"name"`
	User        User          `json:"user"`
	URL         string        `json:"url"`
	CreatedAt   string        `json:"createdAt"`
	Screenshots []*Screenshot `json:"screenshots"`
	Related     []Video       `json:"related"`
}

Here, as you can see, ID is defined as a string and CreatedAt is also a string. Other related models are mapped accordingly, but in the real world you don’t want this — if you are using any SQL data type you want your ID field as int or int64, depending on your database.

For example I am using PostgreSQL for demo so of course I want ID as an int and CreatedAt as a time.Time. So we need to define our own model and instruct gqlgen to use our model instead of generating a new one.

type Video struct {
	ID          int       `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	User        User      `json:"user"`
	URL         string    `json:"url"`
	CreatedAt   time.Time `json:"createdAt"`
	Related     []Video
}

// Lets redefine the base ID type to use an id as int
func MarshalID(id int) graphql.Marshaler {
	return graphql.WriterFunc(func(w io.Writer) {
		io.WriteString(w, strconv.Quote(fmt.Sprintf("%d", id)))
	})
}

// And the same for the unmarshaler
func UnmarshalID(v interface{}) (int, error) {
	id, ok := v.(string)
	if !ok {
		return 0, fmt.Errorf("ids must be strings")
	}
	i, e := strconv.Atoi(id)
	return int(i), e
}

func MarshalTimestamp(t time.Time) graphql.Marshaler {
	timestamp := t.Unix() * 1000

	return graphql.WriterFunc(func(w io.Writer) {
		io.WriteString(w, strconv.FormatInt(timestamp, 10))
	})
}

func UnmarshalTimestamp(v interface{}) (time.Time, error) {
	if tmpStr, ok := v.(int); ok {
		return time.Unix(int64(tmpStr), 0), nil
	}
	return time.Time{}, errors.TimeStampError
}

and update gqlgen to use these models like this:

schema:
- schema.graphql
exec:
  filename: generated.go
model:
  filename: models_gen.go
resolver:
  filename: resolver.go
  type: Resolver
models:
  Video:
    model: github.com/ridhamtarpara/go-graphql-demo/api.Video
  ID:
    model: github.com/ridhamtarpara/go-graphql-demo/api.ID
  Timestamp:
model: github.com/ridhamtarpara/go-graphql-demo/api.Timestamp

So, the focal point is the custom definitions for ID and Timestamp with the marshal and unmarshal methods and their mapping in a gqlgen.yml file. Now when the user provides a string as ID, UnmarshalID will convert a string into an int. While sending the response, MarshalID will convert int to string. The same goes for Timestamp or any other custom scalar you define.

Now it’s time to implement real logic. Open resolver.go and provide the definition to mutation and queries. The stubs are already auto-generated with a not implemented panic statement so let’s override that.

func (r *mutationResolver) CreateVideo(ctx context.Context, input NewVideo) (api.Video, error) {
	newVideo := api.Video{
		URL:         input.URL,
		Name:        input.Name,
		CreatedAt:   time.Now().UTC(),
	}

	rows, err := dal.LogAndQuery(r.db, "INSERT INTO videos (name, url, user_id, created_at) VALUES($1, $2, $3, $4) RETURNING id",
		input.Name, input.URL, input.UserID, newVideo.CreatedAt)
	defer rows.Close()

	if err != nil || !rows.Next() {
		return api.Video{}, err
	}
	if err := rows.Scan(&newVideo.ID); err != nil {
		errors.DebugPrintf(err)
		if errors.IsForeignKeyError(err) {
			return api.Video{}, errors.UserNotExist
		}
		return api.Video{}, errors.InternalServerError
	}
  
	return newVideo, nil
}

func (r *queryResolver) Videos(ctx context.Context, limit *int, offset *int) ([]api.Video, error) {
	var video api.Video
	var videos []api.Video

	rows, err := dal.LogAndQuery(r.db, "SELECT id, name, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2", limit, offset)
	defer rows.Close();
	
    if err != nil {
		errors.DebugPrintf(err)
		return nil, errors.InternalServerError
	}
	for rows.Next() {
		if err := rows.Scan(&video.ID, &video.Name, &video.URL, &video.CreatedAt, &video.UserID); err != nil {
			errors.DebugPrintf(err)
			return nil, errors.InternalServerError
		}
		videos = append(videos, video)
	}

	return videos, nil
}

and hit the mutation:

Ohh it worked…… but wait, why is my user empty 😦? So here there is a similar concept like lazy and eager loading. As graphQL is extensible, you need to define which fields you want to populate eagerly and which ones lazily.

I have created this golden rule for my organization team working with gqlgen:

GraphQL is a query language for APIs and 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.
For our use-case, I want to load Related Videos (and even users) only if a client asks for those fields. But as we have included those fields in the models, gqlgen will assume that you will provide those values while resolving video — so currently we are getting an empty struct.

Sometimes you need a certain type of data every time, so you don’t want to load it with another query. Rather you can use something like SQL joins to improve performance. For one use-case (not included in the article), I needed video metadata every time with the video which is stored in a different place. So if I loaded it when requested, I would need another query. But as I knew my requirements (that I need it everywhere on the client side), I preferred it to load eagerly to improve the performance.

So let’s rewrite the model and regenerate the gqlgen code. For the sake of simplicity, we will only define methods for the user.

type Video struct {
	ID          int       `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	UserID      int       `json:"-"`
	URL         string    `json:"url"`
	CreatedAt   time.Time `json:"createdAt"`
}

So we have added UserID and removed User struct and regenerated the code:

go run scripts/gqlgen.go -v

This will generate the following interface methods to resolve the undefined structs and you need to define those in your resolver:

type VideoResolver interface {
	User(ctx context.Context, obj *api.Video) (api.User, error)
	Screenshots(ctx context.Context, obj *api.Video) ([]*api.Screenshot, error)
	Related(ctx context.Context, obj *api.Video, limit *int, offset *int) ([]api.Video, error)
}

And here is our definition:

func (r *videoResolver) User(ctx context.Context, obj *api.Video) (api.User, error) {
	rows, _ := dal.LogAndQuery(r.db,"SELECT id, name, email FROM users where id = $1", obj.UserID)
	defer rows.Close()
	
	if !rows.Next() {
		return api.User{}, nil
	}
	var user api.User
	if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
		errors.DebugPrintf(err)
		return api.User{}, errors.InternalServerError
	}
	
	return user, nil
}

Now the result should look something like this:

So this covers the very basics of graphql and should get you started. Try a few things with graphql and the power of Golang! But before that, let’s have a look at subscription which should be included in the scope of this article.

Subscriptions

Graphql provides subscription as an operation type which allows you to subscribe to real tile data in GraphQL. gqlgen provides web socket-based real-time subscription events.

You need to define your subscription in the schema.graphql file. Here we are subscribing to the video publishing event.

type Subscription {
    videoPublished: Video!
}

Regenerate the code by running: go run scripts/gqlgen.go -v.

As explained earlier, it will make one interface in generated.go which you need to implement in your resolver. In our case, it looks like this:

var videoPublishedChannel map[string]chan api.Video

func init() {
	videoPublishedChannel = map[string]chan api.Video{}
}

type subscriptionResolver struct{ *Resolver }

func (r *subscriptionResolver) VideoPublished(ctx context.Context) (<-chan api.Video, error) {
	id := randx.String(8)

	videoEvent := make(chan api.Video, 1)
	go func() {
		<-ctx.Done()
	}()
	videoPublishedChannel[id] = videoEvent
	return videoEvent, nil
}

func (r *mutationResolver) CreateVideo(ctx context.Context, input NewVideo) (api.Video, error) {
	// your logic ...

	for _, observer := range videoPublishedChannel {
		observer <- newVideo
	}

	return newVideo, nil
}

Now, you need to emit events when a new video is created. As you can see on line 23 we have done that.

And it’s time to test the subscription:

GraphQL comes with certain advantages, but everything that glitters is not gold. You need to take care of a few things like authorizations, query complexity, caching, N+1 query problem, rate limiting, and a few more issues — otherwise it will put you in performance jeopardy.

Phase 2: The advanced - Authentication, Dataloaders, and Query Complexity

Every time I read a tutorial like this, I feel like I know everything I need to know and can get my all problems solved.

But when I start working on things on my own, I usually end up getting an internal server error or never-ending requests or dead ends and I have to dig deep into that to carve my way out. Hopefully we can help prevent that here.

Let’s take a look at a few advanced concepts starting with basic authentication.

Authentication

In a REST API, you have a sort of authentication system and some out of the box authorizations on particular endpoints. But in GraphQL, only one endpoint is exposed so you can achieve this with schema directives.

You need to edit your schema.graphql as follows:

type Mutation {
    createVideo(input: NewVideo!): Video! @isAuthenticated
}

directive @isAuthenticated on FIELD_DEFINITION

We have created an isAuthenticated directive and now we have applied that directive to createVideo subscription. After you regenerate code you need to give a definition of the directive. Currently, directives are implemented as struct methods instead of the interface so we have to give a definition.

I have updated the generated code of server.go and created a method to return graphql config for server.go as follows:

func NewRootResolvers(db *sql.DB) Config {
	c := Config{
		Resolvers: &Resolver{
			db: db,
		},
	}

	// Schema Directive
	c.Directives.IsAuthenticated = func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
		ctxUserID := ctx.Value(UserIDCtxKey)
		if ctxUserID != nil {
			return next(ctx)
		} else {
			return nil, errors.UnauthorisedError
		}
	}
	return c
}
rootHandler:= dataloaders.DataloaderMiddleware(
		db,
		handler.GraphQL(
			go_graphql_demo.NewExecutableSchema(go_graphql_demo.NewRootResolvers(db)
		)
	)
http.Handle("/query", auth.AuthMiddleware(rootHandler))

We have read the userId from the context. Looks strange right? How was userId inserted in the context and why in context? Ok, so gqlgen only provides you the request contexts at the implementation level, so you can not read any of the HTTP request data like headers or cookies in graphql resolvers or directives. Therefore, you need to add your middleware and fetch those data and put the data in your context.

So we need to define auth middleware to fetch auth data from the request and validate.

I haven’t defined any logic there, but instead I passed the userId as authorization for demo purposes. Then chain this middleware in server.go along with the new config loading method.

Now, the directive definition makes sense. Don’t handle unauthorized users in your middleware as it will be handled by your directive.

Demo time:

You can even pass arguments in the schema directives like this:

directive @hasRole(role: Role!) on FIELD_DEFINITION
enum Role { ADMIN USER }

Dataloaders

This all looks fancy, doesn’t it? You are loading data when needed. Clients have control of the data, there is no under-fetching and no over-fetching. But everything comes with a cost.

So what’s the cost here? Let’s take a look at the logs while fetching all the videos. We have 8 video entries and there are 5 users.

query{
  Videos(limit: 10){
    name
    user{
      name
    }
  }
}

Query: Videos : SELECT id, name, description, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1
Resolver: User : SELECT id, name, email FROM users where id = $1

Why 9 queries (1 videos table and 8 users table)? It looks horrible. I was just about to have a heart attack when I thought about replacing our current REST API servers with this…but dataloaders came as a complete cure for it!

This is known as the N+1 problem, There will be one query to get all the data and for each data (N) there will be another database query.

This is a very serious issue in terms of performance and resources: although these queries are parallel, they will use your resources up.

We will use the dataloaden library from the author of gqlgen. It is a Go- generated library. We will generate the dataloader for the user first.

go get github.com/vektah/dataloaden
dataloaden github.com/ridhamtarpara/go-graphql-demo/api.User

This will generate a file userloader_gen.go which has methods like Fetch, LoadAll, and Prime.

Now, we need to define the Fetch method to get the result in bulk.

func DataloaderMiddleware(db *sql.DB, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		userloader := UserLoader{
			wait : 1 * time.Millisecond,
			maxBatch: 100,
			fetch: func(ids []int) ([]*api.User, []error) {
				var sqlQuery string
				if len(ids) == 1 {
					sqlQuery = "SELECT id, name, email from users WHERE id = ?"
				} else {
					sqlQuery = "SELECT id, name, email from users WHERE id IN (?)"
				}
				sqlQuery, arguments, err := sqlx.In(sqlQuery, ids)
				if err != nil {
					log.Println(err)
				}
				sqlQuery = sqlx.Rebind(sqlx.DOLLAR, sqlQuery)
				rows, err := dal.LogAndQuery(db, sqlQuery, arguments...)
				defer rows.Close();
				if err != nil {
					log.Println(err)
				}
				userById := map[int]*api.User{}

				for rows.Next() {
					user:= api.User{}
					if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
						errors.DebugPrintf(err)
						return nil, []error{errors.InternalServerError}
					}
					userById[user.ID] = &user
				}

				users := make([]*api.User, len(ids))
				for i, id := range ids {
					users[i] = userById[id]
					i++
				}

				return users, nil
			},
		}
		ctx := context.WithValue(r.Context(), CtxKey, &userloader)
		r = r.WithContext(ctx)
		next.ServeHTTP(w, r)
	})
}

Here, we are waiting for 1ms for a user to load queries and we have kept a maximum batch of 100 queries. So now, instead of firing a query for each user, dataloader will wait for either 1 millisecond for 100 users before hitting the database. We need to change our user resolver logic to use dataloader instead of the previous query logic.

func (r *videoResolver) User(ctx context.Context, obj *api.Video) (api.User, error) {
	user, err := ctx.Value(dataloaders.CtxKey).(*dataloaders.UserLoader).Load(obj.UserID)
	return *user, err
}

After this, my logs look like this for similar data:

Query: Videos : SELECT id, name, description, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2
Dataloader: User : SELECT id, name, email from users WHERE id IN ($1, $2, $3, $4, $5)

Now only two queries are fired, so everyone is happy. The interesting thing is that only five user keys are given to query even though 8 videos are there. So dataloader removed duplicate entries.

Query Complexity

In GraphQL you are giving a powerful way for the client to fetch whatever they need, but this exposes you to the risk of denial of service attacks.

Let’s understand this through an example which we’ve been referring to for this whole article.

Now we have a related field in video type which returns related videos. And each related video is of the graphql video type so they all have related videos too…and this goes on.

Consider the following query to understand the severity of the situation:

{
  Videos(limit: 10, offset: 0){
    name
    url
    related(limit: 10, offset: 0){
      name
      url
      related(limit: 10, offset: 0){
        name
        url
        related(limit: 100, offset: 0){
          name
          url
        }
      }
    }
  }
}

If I add one more subobject or increase the limit to 100, then it will be millions of videos loading in one call. Perhaps (or rather definitely) this will make your database and service unresponsive.

gqlgen provides a way to define the maximum query complexity allowed in one call. You just need to add one line (Line 5 in the following snippet) in your graphql handler and define the maximum complexity (300 in our case).

rootHandler:= dataloaders.DataloaderMiddleware(
	db,
	handler.GraphQL(
		go_graphql_demo.NewExecutableSchema(go_graphql_demo.NewRootResolvers(db)),
		handler.ComplexityLimit(300)
  ),
)

gqlgen assigns fix complexity weight for each field so it will consider struct, array, and string all as equals. So for this query, complexity will be 12. But we know that nested fields weigh too much, so we need to tell gqlgen to calculate accordingly (in simple terms, use multiplication instead of just sum).

func NewRootResolvers(db *sql.DB) Config {
	c := Config{
		Resolvers: &Resolver{
			db: db,
		},
	}

	// Complexity
	countComplexity := func(childComplexity int, limit *int, offset *int) int {
		return *limit * childComplexity
	}
	c.Complexity.Query.Videos = countComplexity
	c.Complexity.Video.Related = countComplexity

	// Schema Directive
	c.Directives.IsAuthenticated = func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
		ctxUserID := ctx.Value(UserIDCtxKey)
		if ctxUserID != nil {
			return next(ctx)
		} else {
			return nil, errors.UnauthorisedError
		}
	}
	return c
}

Just like directives, complexity is also defined as struct, so we have changed our config method accordingly.

I haven’t defined the related method logic and just returned the empty array. So related is empty in the output, but this should give you a clear idea about how to use the query complexity.

Final Notes

This code is on Github. You can play around with it, and if you have any questions or concerns let me know in the comment section.

Learn More

Build a Basic App with Spring Boot and JPA using PostgreSQL

How to Install PostgreSQL on Ubuntu 18.04

An Introduction to Queries in PostgreSQL

GraphQL Tutorial: Understanding Spring Data JPA/SpringBoot

How To Manage an SQL Database

GraphQL with React: The Complete Developers Guide

GraphQL with Angular & Apollo - The Full-stack Guide

GraphQL: Learning GraphQL with Node.Js

Complete guide to building a GraphQL API

GraphQL: Introduction to GraphQL for beginners

File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

In this tutorial, I'll be showing you how to hand file uploads in GraphQL by building a fullstack app.

The tutorial will be divided into two main sections: building the GraphQL API and the frontend app. The GraphQL API will be built using Apollo Server and the frontend app will be built with Vue.js and Vue Apollo.

Table of Contents

  • Prerequisites
  • What we'll be building
  • Getting your Cloudinary keys
  • Building the GraphQL server
  • Building the frontend app
  • Fetching all photos
  • Uploading photo
  • Conclusion
Prerequisites

This tutorial assumes you conformatable with GraphQL and using it in a Vue app.

What we'll be building

For the purpose of this tutorial, we'll be building a simple photo album app, where users will be able to upload as well as view their photos. All the photos will be uploaded directly to Cloudinary. Below is a quick demo of the final app:

File Upload with Vue, Apollo Client and GraphQL

Getting your Cloudinary keys

Before we dive into code, let’s make sure we have our Cloudinary key in place. If you don’t already have an account with them, you can signup for free. Otherwise, login to your dashboard where you would see your account details along with your keys.

File Upload with Vue, Apollo Client and GraphQL

Building the GraphQL server

Now, let's start building the GraphQL server. First, let's create our project directory:

$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload
$ mkdir server && cd server
$ npm init -y

All the GraphQL API related code will be inside the server directory. Next, let’s install the necessary dependencies for our GraphQL server:

$ npm install graphql apollo-server cloudinary dotenv

Addition to installing Apollo Server and it dependence, we also install the Cloudinary Node.js library and a package to read environment variables.

Once that’s done installing, we can start building the GraphQL server. Create a new src directory and create a new index.js file inside it, then add the following code in it:

// server/src/index.js

const { ApolloServer } = require('apollo-server')
require('dotenv').config()
const typeDefs = require('./schema')
const resolvers = require('./resolvers')

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

server.listen().then(({ url }) => console.log(`Server ready at ${url}`))

Next, we need to create the schema and resolvers which our GraphQL server references. We’ll start with creating the schema. Create a schema.js inside src directory and paste the following code into it:

// server/src/schema.js

const { gql } = require('apollo-server')

const typeDefs = gql`type Photo {
    filename: String!
    path: String!
  }

  type Query {
    allPhotos: [Photo]
  }

  type Mutation {
    uploadPhoto(photo: Upload!): Photo!
  }`

module.exports = typeDefs

Here, we define a Photo type that comprises of two fields: the filename of the photo and the path to the actual photo. Then we define a single query allPhotos for fetching all uploaded photos. Lastly, we have a mutation for uploading a photo. The uploadPhoto mutation accepts a single argument, which is the photo that is to be uploaded. The argument is of the scalar type Upload, which is made available to us my Apollo Server, since it has built-in support of file upload. The mutation will return the uploaded photo.

The next, thing to do is create the resolvers. Still inside src directory, create a resolvers.js and add the code below in it:

// server/src/resolvers.js

const cloudinary = require('cloudinary').v2

cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET
})

const photos = []

const resolvers = {
  Query: {
    allPhotos () {
      return photos
    }
  },
  Mutation: {
    async uploadPhoto (parent, { photo }) {
      const { filename, createReadStream } = await photo

      try {
        const result = await new Promise((resolve, reject) => {
          createReadStream().pipe(
            cloudinary.uploader.upload_stream((error, result) => {
              if (error) {
                reject(error)
              }

              resolve(result)
            })
          )
        })

        const newPhoto = { filename, path: result.secure_url }

        photos.push(newPhoto)

        return newPhoto
      } catch (err) {
        console.log(err)
      }
    }
  }
}

module.exports = resolvers

First, we pull in the Cloudinary library and configured it will our credentials, which are getting from the environment variables. Then we create an empty array, which will hold our photos. Next, we define the resolver for the allPhotos query, which simply returns the photos array.

For the uploadPhoto mutation, Apollo Server would return the selected file as Promise, which contains a bunch of details about the file, such as: createReadStream, filename, mimetype and encoding. In this tutorial, we’ll only be making use of the first two, so we extract them from the object. Using createReadStream, we stream the file directly to Cloudinary. Since it is an asynchronous operation we wrap it in a Promise and awaits it. If the Promise was resolved, that is, the file was uploaded successfully to Cloudinary, we create a new object containing the uploaded file name and the Cloudinary path to the file. Then we push the new object to the photos array and finally return the new object.

Lastly, if there was an error uploading the file to Cloudinary, we simply console log the erorr.

Before we wrap up the GraphQL API, let’s quickly add our environment variables. Create a .env file directly in the server directory and add the code below in it:

// server/.env

CLOUD_NAME=YOUR_CLOUD_NAME
API_KEY=YOUR_API_KEY
API_SECRET=YOUR_API_SECRET

Remember to replace the placeholders with your actual account details.

Finally, let’s start the server:

$ node src/index.js

The server should be running on [[http://localhost:4000](http://localhost:4000)](http://localhost:4000](http://localhost:4000))

Building the frontend app

As already mentioned, the frontend app will be built with Vue.js, so let’s create a new Vue.js app using the Vue CLI:

$ vue create client

When prompted, press enter to select the default preset. Start the app and leave it running while we build on it:

$ cd client
$ yarn serve

The app should be running on http://localhost:8080

Once the Vue app is created, let’s install the necessary dependencies:

$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client

Out of these dependencies, the one that is new and that I’d like to point out is [apollo-upload-client]([https://github.com/jaydenseric/apollo-upload-client)](https://github.com/jaydenseric/apollo-upload-client)). It’s a package for Apollo Client that allows us to send GraphQL multipart requests. It will be used in place of apollo-link.

Next, let’s configure Vue Apollo and these dependencies. Update main.js as below:

// client/src/main.js

import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(VueApollo)

const apolloClient = new ApolloClient({
  link: createUploadLink({ uri: 'http://localhost:4000' }),
  cache: new InMemoryCache()
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
})

new Vue({
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

You’ll notice here we are using createUploadLink from apollo-upload-client to create the ApolloClient link, passing to it our GraphQL API endpoint.

To give our app a bit of styling, we’ll be pulling in UIKit. Add the line below to head section of index.html:





Fetching all photos

We’ll start with the GraphQL query for fetching all our photo. Create a graphql directory inside the client/src directory and with in, create a AllPhotos.js file and paste the code below into it:

// client/src/graphql/AllPhotos.js

import gql from 'graphql-tag'

export default gql`query allPhotos {
    allPhotos {
      filename
      path
    }
  }`

For the sake of simplicity, we’ll be making use of just the App.vue component. So let’s update it as below:

// client/src/App.vue


  
    
      ## Photo Album


      
        
          
            
              ![](photo.path)
            
            {{ photo.filename }}

          
        
      
    
  



Within the apollo object, we add the ALL_PHOTOS query to fetch all photos and save it in a allPhotos. Once allPhotos is populated with data from our GraphQL API, we display the photos by looping through them.

If we view our app, we should get something similar to below:
File Upload with Vue, Apollo Client and GraphQL

Uploading photo

Of course, we need to have uploaded some photos before we can see them. Let’s implement that now. Still inside the graphql directory, create a UploadPhoto.js and paste the code below into it:

// client/src/graphql/UploadPhoto.js

import gql from 'graphql-tag'

export default gql`mutation uploadPhoto($photo: Upload!) {
    uploadPhoto(photo: $photo) {
      filename
      path
    }
  }`

Next, add the snippet below to the template section of App.vue, just below the Photo Album heading:

// client/src/App.vue


  

Here, we have a file input field that accepts only images. On change of the input field a uploadPhoto method is triggered.

In the script section, add:

// client/src/App.vue

import UPLOAD_PHOTO from "./graphql/UploadPhoto";

methods: {
  async uploadPhoto({ target }) {
    await this.$apollo.mutate({
      mutation: UPLOAD_PHOTO,
      variables: {
        photo: target.files[0]
      },
      update: (store, { data: { uploadPhoto } }) => {
        const data = store.readQuery({ query: ALL_PHOTOS });

        data.allPhotos.push(uploadPhoto);

        store.writeQuery({ query: ALL_PHOTOS, data });
      }
    });
  }
}

We get extract the target from the input event, then call the mutate method, passing to it the UPLOAD_PHOTO mutation as well as the required argument (through the variables object). We get the selected file from the files on the target object. Once the mutation is executed, we update the cache by adding the newly uploaded photo to the allPhotos array.
File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

Conclusion

So in this tutorial, we have seen how to handle file uploads in GraphQL using Apollo Server on the server side and Vue and Vue Apollo on the client side. Though we used Cloudinary to store our photos, you can easily wrap it for any other cloud storage service or you can even save directly to your own local filesystem.

Top 5 Common Node.js mistakes in Lambda

Top 5 Common Node.js mistakes in Lambda

Top 5 common mistakes to avoid when working with Node.js in Lambda

I have helped quite a few clients with their Node.js serverless projects. In doing so I have seen some recurring mistakes around async/await.

1. Still using callbacks

Many people are still using the callbacks in their async handler functions:

module.exports.handler = async (event, context, cb) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  cb(null, response)
}

instead of the simpler alternative:

module.exports.handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  return response
}

2. Not using promisify

Before Node8, bluebird filled a massive gap. It provided the utility to convert callback-based functions to promise-based. But Node8's built-in util module has filled that gap with the promisify function.

For example, we can now transform the readFile function from the fs module like this:

const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)

No need to use bluebird anymore. That's one less dependency, which helps reduce the cold start time for our functions.

3. Too sequential

async/await lets you write asynchronous code as if they're synchronous, which is awesome. No more dealing with callback hell!

On the flip side, we can also miss a trick and not perform tasks concurrently where appropriate.

Take the following code as an example:

async function getFixturesAndTeam(teamId) {
  const fixtures = await fixtureModel.fetchAll()
  const team = await teamModel.fetch(teamId)
  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

This function is easy to follow, but it's hardly optimal. teamModel.fetch doesn't depend on the result of fixtureModel.fetchAll, so they should run concurrently.

Here is how you can improve it:

async function getFixturesAndTeam(teamId) {
  const fixturesPromise = fixtureModel.fetchAll()
  const teamPromise = teamModel.fetch(teamId)

  const fixtures = await fixturesPromise
  const team = await teamPromise

  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

In this version, both fixtureModel.fetchAll and teamModel.fetch are started concurrently.

You also need to watch out when using map with async/await. The following will call teamModel.fetch one after another:

async function getTeams(teamIds) {
  const teams = _.map(teamIds, id => await teamModel.fetch(id))
  return teams
}

Instead, you should write it as the following:

async function getTeams(teamIds) {
  const promises = _.map(teamIds, id => teamModel.fetch(id))
  const teams = await Promise.all(promises)
  return teams
}

In this version, we map teamIds to an array of Promise. We can then use Promise.all to turn this array into a single Promise that returns an array of teams.

In this case, teamModel.fetch is called concurrently and can significantly improve execution time.

4. async/await inside forEach()

This is a tricky one, and can sometimes catch out even experienced Node.js developers.

The problem is that code like this doesn't behave the way you'd expect it to:

[ 1, 2, 3 ].forEach(async (x) => {
  await sleep(x)
  console.log(x)
})

console.log('all done.')

When you run this you'll get the following output:

all done.

See this post for a longer explanation about why this doesn't work. For now, just remember to avoid using async/await inside a forEach!

5. Not using AWSSDK’s .promise()

Did you know that the AWS SDK clients support both callbacks and promises? To use async/await with the AWS SDK, add .promise() to client methods like this:

const AWS = require('aws-sdk')
const Lambda = new AWS.Lambda()

async function invokeLambda(functionName) {
  const req = {
    FunctionName: functionName,
    Payload: JSON.stringify({ message: 'hello world' })
  }
  await Lambda.invoke(req).promise()
}

No more callback functions, yay!

How to build a simple CRUD with GraphQL and Flutter

How to build a simple CRUD with GraphQL and Flutter

In this tutorial, I am going to show you how to build a simple CRUD with Flutter (client-side) and GraphQL (server-side)

Originally published by Daniel Berrío Barrera at https://medium.com

Flutter is an SDK (Software development kit) to build applications for Android and IOs with a single codebase. On the other hand, GraphQL is a query language that allows us to create queries that will be executing on the server. In this tutorial, I am going to show you how to build a simple CRUD with Flutter (client-side) and GraphQL (server-side). Let’s start

Once we have created our Flutter project we need to import some dependencies in our pubspec.yaml file in order to use GraphQL and HTTP with our API. It’s essential to notice that the API was created on NodeJS, so for this tutorial I’m not going to show how to build it.

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: ^2.0.1
  http: ^0.12.0+2

Then, we are going to create a file called graphQLConf.dart, this file will contain the basic configuration to use GraphQL like our link to the API and the cache configuration. So, we create a class called GraphQLConfiguration, inside this, there are three main parts. The first one is the link to the API, the second one is the ValueNotifier which will notify its listener when any kind of value changed, and finally, the method clientToQuery() will let us make queries and mutation in our application.

import "package:flutter/material.dart";
import "package:graphql_flutter/graphql_flutter.dart";

class GraphQLConfiguration {
static HttpLink httpLink = HttpLink(
uri: "https://examplegraphql.herokuapp.com/graphql",
);

ValueNotifier<GraphQLClient> client = ValueNotifier(
GraphQLClient(
link: httpLink,
cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
),
);

GraphQLClient clientToQuery() {
return GraphQLClient(
cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
link: httpLink,
);
}
}

In our main.dart file we will wrap the MaterialApp Widget with the previous configuration as follow:

import 'package:flutter/material.dart';
import "package:graphql_flutter/graphql_flutter.dart";
import "services/graphqlConf.dart";
import "package:example/components/principal.dart";

GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration();

void main() => runApp(
GraphQLProvider(
client: graphQLConfiguration.client,
child: CacheProvider(child: MyApp()),
),
);

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Principal(),
);
}
}

As you can saw we called the GraphQLObject from the graphQLConf.dart file and then we wrap it into the main method. But we don’t know how the Principal Widget use there is made. Basically, during this example we are going to work with a class Person, this class will contain basic information such as the id, the name, the last name, and the age. The application lets us add a person, edit a person, see all the persons added and finally delete a person (a basic CRUD). So let’s create a class person.

class Person {
Person(this.id, this.name, this.lastName, this.age);

final String id;
final String name;
final String lastName;
final int age;

getId() => this.id;

getName() => this.name;

getLastName() => this.lastName;

getAge() => this.age;
}

As you can see is a simple class Person with the id, name, lastName and age attributes, also we had a constructor and the get methods to access its values. Then we need to do our queries to the API. GraphQL provides two forms:

  • Query: It allows us to access data and read it.
  • Mutation: It allows to modify our data. Add, edit or delete something.

For this, let’s create a class that will contain all the queries and mutation that we are going to use. (Note: Dart uses a notation with three quotes to make the queries).

class QueryMutation {
String addPerson(String id, String name, String lastName, int age) {
return """
mutation{
addPerson(id: "$id", name: "$name", lastName: "$lastName", age: $age){
id
name
lastName
age
}
}
""";
}

String getAll(){
return """
{
persons{
id
name
lastName
age
}
}
""";
}

String deletePerson(id){
return """
mutation{
deletePerson(id: "$id"){
id
}
}
""";
}

String editPerson(String id, String name, String lastName, int age){
return """
mutation{
editPerson(id: "$id", name: "$name", lastName: "$lastName", age: $age){
name
lastName
}
}
""";
}
}

Then, our Principal widget will use an AlertDialog to do the insert, read and delete operations, and then it will be called from our Widget. So let’s create the AlertDialog widget.

import "package:flutter/material.dart";
import "../services/graphqlConf.dart";
import "../services/queryMutation.dart";
import "package:graphql_flutter/graphql_flutter.dart";
import "package:example/components/person.dart";

class AlertDialogWindow extends StatefulWidget {
final Person person;
final bool isAdd;

AlertDialogWindow({Key key, this.person, this.isAdd}) : super(key: key);

@override
State<StatefulWidget> createState() =>
_AlertDialogWindow(this.person, this.isAdd);
}

class _AlertDialogWindow extends State<AlertDialogWindow> {
TextEditingController txtId = TextEditingController();
TextEditingController txtName = TextEditingController();
TextEditingController txtLastName = TextEditingController();
TextEditingController txtAge = TextEditingController();
GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration();
QueryMutation addMutation = QueryMutation();

final Person person;
final bool isAdd;

_AlertDialogWindow(this.person, this.isAdd);

@override
void initState() {
super.initState();
if (!this.isAdd) {
txtId.text = person.getId();
txtName.text = person.getName();
txtLastName.text = person.getLastName();
txtAge.text = person.getAge().toString();
}
}

@override
Widget build(BuildContext context) {
// TODO: implement build
return AlertDialog(
title: Text(this.isAdd ? "Add" : "Edit or Delete"),
content: Container(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width,
),
child: Stack(
children: <Widget>[
Container(
child: TextField(
maxLength: 5,
controller: txtId,
enabled: this.isAdd,
decoration: InputDecoration(
icon: Icon(Icons.perm_identity),
labelText: "ID",
),
),
),
Container(
padding: EdgeInsets.only(top: 80.0),
child: TextField(
maxLength: 40,
controller: txtName,
decoration: InputDecoration(
icon: Icon(Icons.text_format),
labelText: "Name",
),
),
),
Container(
padding: EdgeInsets.only(top: 160.0),
child: TextField(
maxLength: 40,
controller: txtLastName,
decoration: InputDecoration(
icon: Icon(Icons.text_rotate_vertical),
labelText: "Last name",
),
),
),
Container(
padding: EdgeInsets.only(top: 240.0),
child: TextField(
maxLength: 2,
controller: txtAge,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: "Age", icon: Icon(Icons.calendar_today)),
),
),
],
),
),
),
),
actions: <Widget>[
FlatButton(
child: Text("Close"),
onPressed: () => Navigator.of(context).pop(),
),
!this.isAdd
? FlatButton(
child: Text("Delete"),
onPressed: () async {
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client.mutate(
MutationOptions(
document: addMutation.deletePerson(txtId.text),
),
);
if (!result.hasErrors) Navigator.of(context).pop();
},
)
: null,
FlatButton(
child: Text(this.isAdd ? "Add" : "Edit"),
onPressed: () async {
if (txtId.text.isNotEmpty &&
txtName.text.isNotEmpty &&
txtLastName.text.isNotEmpty &&
txtAge.text.isNotEmpty) {
if (this.isAdd) {
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client.mutate(
MutationOptions(
document: addMutation.addPerson(
txtId.text,
txtName.text,
txtLastName.text,
int.parse(txtAge.text),
),
),
);
if (!result.hasErrors) {
txtId.clear();
txtName.clear();
txtLastName.clear();
txtAge.clear();
Navigator.of(context).pop();
}
} else {
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client.mutate(
MutationOptions(
document: addMutation.editPerson(
txtId.text,
txtName.text,
txtLastName.text,
int.parse(txtAge.text),
),
),
);
if (!result.hasErrors) {
txtId.clear();
txtName.clear();
txtLastName.clear();
txtAge.clear();
Navigator.of(context).pop();
}
}
}
},
)
],
);
}
}

In the _AlertDialog we used all the mutations created to add, edit and delete a person. The process is as follows:

  • We called our GraphQl configuration from GraphQLConfiguration: graphQLConfiguration = GraphQLConfiguration();
  • We get the mutations from the class we have created: QueryMutation addMutation = QueryMutation();
  • We create our GraphQL client to connect to our backend: GraphQLClient _client = graphQLConfiguration.clientToQuery();
  • Finally we do our mutation with the required parameters.

Once we have done the previous steps we check is our result is correct, and if that is true, just return to the main page and clear all the TextFields. Basically, all the queries and mutations done during the example are based under the same principle: declare your variables, do your query or mutation and finally check if that has errors.

Finally, we have our whole Principal widget, and there is something more we need to remark. Inside our initState() we are going to call all the data saved in our backend, and to do that we use our function fillList(), and basically it will fill a listPerson with all the data stored, to access to it, we do result.data[“name our query or mutation”][index][“name of the variable”] and that is it.

import "package:flutter/material.dart";
import "package:example/components/alertDialogs.dart";
import "package:graphql_flutter/graphql_flutter.dart";
import "package:example/components/person.dart";
import "../services/graphqlConf.dart";
import "../services/queryMutation.dart";

class Principal extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Principal();
}

class _Principal extends State<Principal> {
List<Person> listPerson = List<Person>();
GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration();

void fillList() async {
QueryMutation queryMutation = QueryMutation();
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client.query(
QueryOptions(
document: queryMutation.getAll(),
),
);
if (!result.hasErrors) {
for (var i = 0; i < result.data["persons"].length; i++) {
setState(() {
listPerson.add(
Person(
result.data["persons"][i]["id"],
result.data["persons"][i]["name"],
result.data["persons"][i]["lastName"],
result.data["persons"][i]["age"],
),
);
});
}
}
}

@override
void initState() {
super.initState();
fillList();
}

void _addPerson(context) {
showDialog(
context: context,
builder: (BuildContext context) {
AlertDialogWindow alertDialogWindow =
new AlertDialogWindow(isAdd: true);
return alertDialogWindow;
},
).whenComplete(() {
listPerson.clear();
fillList();
});
}

void _editDeletePerson(context, Person person) {
showDialog(
context: context,
builder: (BuildContext context) {
AlertDialogWindow alertDialogWindow =
new AlertDialogWindow(isAdd: false, person: person);
return alertDialogWindow;
},
).whenComplete(() {
listPerson.clear();
fillList();
});
}

@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Example"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add_circle_outline),
onPressed: () => _addPerson(context),
tooltip: "Insert new person",
),
],
),
body: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: Text(
"Person",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30.0),
),
),
Container(
padding: EdgeInsets.only(top: 50.0),
child: ListView.builder(
itemCount: listPerson.length,
itemBuilder: (context, index) {
return ListTile(
selected: listPerson == null ? false : true,
title: Text(
"${listPerson[index].getName()}",
),
onTap: () {
_editDeletePerson(context, listPerson[index]);
},
);
},
),
),
],
),
);
}
}

Our result!