#graphqlschema example
Explore tagged Tumblr posts
Video
GraphQL Server in 4 Phases With Express & Typescript - JWT Auth Series Pt 1.
This video is a GraphQL server tutorial which covers GraphQL mutations, gives a GraphQLSchema example, and sets up a GraphQL Postgres db. We will be coding everything in Typescript and using Express/NodeJS for the build. Whether you're learning GraphQL for the first time or a seasoned coder, there are several nuggets of knowledge that you can get from this video.
#graphql server#graphql tutorial#graphql mutations#graphql typescript#express graphql#github graphql#graphql example#graphql js#graphql nodejs#npm graphql#nodejs graphql#howtographql#graphql npm#graphql postgres#graphql node#facebook graphql#postgres graphql#learning graphql#graphqlschema example#graphql db#graphqlerror#graphqlconfig#graphql postman#postman graphql#stack that code#graphql#graphql server tutorial#create a graphql server
1 note
·
View note
Text
Discovering GraphQL
Through this article, we are going to see what GraphQL is and how we can use it. It is going to be a really simple introduction to get our hands on this tool.
Introduction
REST has become the standard for exposing data from a server and designing web APIs. It undeniably offers good advantages and functionalities.
Unfortunately, client applications rarely stay simple. Each one has specific requirements and thus needs different data from our system. To satisfy that need, we will add endpoints or add nested objects and probably deliver irrelevant data. Working and maintaining multiple endpoints could be difficult and as a platform grows, the number of endpoints will increase. Clients will need to ask for data from different endpoints increasing the number of HTTP requests. They will get too much or not enough information. They won't have exactly what they asked for.
GraphQL offers a solution to these problems.
What is GraphQL?
GraphQL is a query language and a server-side runtime for executing queries. GraphQL isn't tied to any specific database or storage engine. It was created by Facebook. It gives clients the power to ask for exactly what they need, nothing more, nothing less.
While typical REST APIs require loading from multiple URLs, GraphQL APIs get all the data we need in a single request.
GraphQL APIs are organized in terms of types and fields, not endpoints. GraphQL uses types to ensure applications only ask for what is possible and provide clear and helpful errors.
The GraphQL layer takes place between the client and one or more data sources. It receives the requests of the client and fetches the necessary data according to the instructions received.
Many different programming languages support GraphQL. Here, we are going to use JavaScript with Node.js.
Basic terminology
Before getting our hands dirty, let's have an overview of the different terms related to the components of GraphQL.
Query
A Query is a read-only operation made against a GraphQL Server. They are similar to GET requests and they allow to ask the server for the data that a client needs.
We write a GraphQL Query as we would declare a JSON object. GraphQL Queries support nested fields. In return, clients will have a JSON result.
Mutation
A Mutation is a read-write operation requested to a GraphQL Server. In REST APIs, they would correspond to POST, DELETE and PUT.
Resolver
A Resolver is a link between an operation and the code responsible for handling it. It provides a way to interact with databases through different operations. It tells GraphQL how and where to fetch the data corresponding to a given field.
Type
A Type defines the shape of response data that can be returned from the GraphQL Server.
Scalar
A Scalar is a primitive Type, such as a String, Int, Boolean or Float.
Field
A Field is a unit of data we can retrieve from an object.
Argument
An Argument is a set of key-value pairs attached to a specific field. Arguments can be of many different types.
Input
Input Types look exactly the same as regular object types. They define the shape of input data that is sent to a GraphQL Server.
Schema
A Schema manages queries and mutations, defining what is allowed to be executed in the GraphQL Server. It defines what each Query or Mutation takes as input, and what each query or mutation returns as output. It describes the complete set of possible data that a client can access.
Interface
An Interface is an abstract type that includes a certain set of Fields that a Type must include to implement the Interface. It stores the names of the Fields and their Arguments, so GraphQL objects can inherit from it.
Implementation
A GraphQL Schema may use the term implements to define how an object inherits from an Interface.
Connection
A Connection allows to query related objects as part of the same call.
Node
Node is a generic term for an object.
Edge
An Edge represents connections between nodes. Every Edge Field has a Node Field and a Cursor Field. Cursors are used for pagination.
A simple example
Now we saw some theoretical notions, let's try to put them into practice by making a very straightforward example. As we said earlier, we are going to use JavaScript, Node.js and Express.js to reach our goal.
We are going to make a really simple API that returns us the name of a band and its albums when we pass the band's id to the main query. We are going to use MusicGraph API for this.
In the end, we are going to be able to make a request like so:
{ artist(id: "string) { name, albums { title, format, year } } }
Let's create a package.json file like so:
{ "name": "simple-app", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node ./index.js", "watch": "nodemon --exec npm start" }, "nodemonConfig": { "ignore": [ "test/*", "docs/*" ], "delay": "2500" }, "author": "", "license": "ISC", "dependencies": { "dotenv": "^4.0.0", "express": "^4.16.2", "express-graphql": "^0.6.11", "graphql": "^0.12.3", "node-fetch": "^1.7.3" }, "devDependencies": { "nodemon": "^1.14.7" } }
package.json file
As we can see, in our dependencies, we have Express.js, as we said, but also Express GraphQL and GraphQL.js. Express GraphQL will help us to create a GraphQL HTTP Server with Express while GraphQL.js is a reference implementation of GraphQL for JavaScript.
We also have some dependencies like Dotenv that lets us load environment variables from a .env file, node-fetch that brings window.fetch to Node.js and nodemon that watches for any changes in our application.
Our index.js file is going to be filled with the following code:
const express = require('express') const graphqlHTTP = require('express-graphql') const app = express() const schema = require('./schema') app.use('/graphql', graphqlHTTP({ schema, graphiql: true })) app.listen(4000) console.log('Now listening to port 4000...')
index.js file
As we can notice here, we create our server and mount Express GraphQL as a route handler. We also set the graphiql option to true. It means that GraphiQL, which is an in-browser IDE for exploring GraphQL, will be presented to us when the GraphQL endpoint is loaded in a browser.
There is also a mention of a file called schema.js, which, obviously, represents our Schema. Let's take a look at it!
const fetch = require('node-fetch') require('dotenv').config() const { GraphQLSchema, GraphQLObjectType, GraphQLInt, GraphQLString, GraphQLList } = require('graphql') // Defining the Album field const AlbumType = new GraphQLObjectType({ name: 'Album', description: 'Album type', // Defining Fields fields: () => ({ title: { type: GraphQLString, resolve: json => json.title }, format: { type: GraphQLString, resolve: json => json.product_form }, year: { type: GraphQLInt, resolve: json => json.release_year } }) }) // Defining the Artist field const ArtistType = new GraphQLObjectType({ name: 'Artist', description: 'Artist type', // Defining the Fields fields: () => ({ name: { type: GraphQLString, // Name Field resolver resolve: json => json.data.name }, albums: { // Defining a GraphQLList filled with object AlbumType type: new GraphQLList(AlbumType), // Albums Field resolver resolve: (parent, args) => fetch( `http://api.musicgraph.com/api/v2/artist/${parent.data.id}/albums?api_key=${process.env.API_KEY}&limit=100` ) .then(data => data.json()) .then(json => json.data.filter(d => d.product_form === "album")) } }) }) // Defining our Schema module.exports = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQuery', description: 'The main query', // Defining the Fields fields: () => ({ artist: { type: ArtistType, args: { id: { type: GraphQLString } }, // Resolver - fetching data from MusicGraph API then returning a JSON object resolve: (root, args) => fetch( `http://api.musicgraph.com/api/v2/artist/${args.id}?api_key=${process.env.API_KEY}` ) .then(res => res.json()) } }) }) })
schema.js file
With the first lines we include a few things that we need from GraphQL.
Then, we define the Album Field and the Artist Field. Finally, we define our Schema before we export it.
Let's go to localhost:4000/graphql and try the following query:
{ artist(id: "82047b8d-738e-4225-8ad9-76fe6f2a486c") { name } }
The result should be the following one:
{ "data": { "artist": { "name": "Depeche Mode" } } }
Now, the next query:
{ artist(id: "82047b8d-738e-4225-8ad9-76fe6f2a486c") { name, albums { title, format, year } } }
And the result should be:
{ "data": { "artist": { "name": "Depeche Mode", "albums": [ { "title": "Depeche Mode - Singles Box 5", "format": "album", "year": 2016 }, { "title": "Songs of Faith and Devotion: Live", "format": "album", "year": 2015 }, { "title": "Live in Berlin Soundtrack", "format": "album", "year": 2014 }, { "title": "Songs of Faith and Devotion (Remastered)", "format": "album", "year": 2013 } ... ] } } }
A little more
Mutation
Of course, in our example, we didn't play with a Mutation because it is not really appropriate. However, a Mutation could be set up this way:
export default new GraphQLSchema({ query: QueryType, mutation: MutationType }) const MutationType = new GraphQLObjectType({ name: 'Item Mutations', description: 'A simple mutation', fields: () => ({ ...itemMutations }) }) const itemMutations = { addItem: { type: ItemType, args: { input: { type: new GraphQLNonNull(ItemInputType), } } }, resolve: (root, {input}) => { const newItem = await new Promise((resolve) => { setTimeout(() => resolve(Object.assign(input, { id: random.uuid(), })), 100); }) return newItem } } const ItemInputType = new GraphQLInputObjectType({ name: 'ItemInputType', fields: () => ({ name: { type: GraphQLString, }, }), })
And it could be used like so:
mutation { addItem(input: { name: "Foo" }) { id, name } }
Subscriptions
Subscriptions are a way for the server to push data itself to interested clients when an event happens. This is how GraphQL deals with real-time communication.
To achieve this, we need GraphQL subscriptions to implement subscriptions in GraphQL and subscriptions-transport-ws that works with a WebSocket connection, which will remain open between the server and subscribed clients.
First, we need to create a PubSub instance like so:
import { PubSub } from 'graphql-subscriptions' const pubsub = new PubSub()
Then we could do the following thing:
const ITEM_ADDED_TOPIC = 'newItem'; export default new GraphQLSchema({ query: QueryType, mutation: MutationType, subscription: SubscriptionType }) const MutationType = new GraphQLObjectType({ name: 'Item Mutations', description: 'A simple mutation', fields: () => ({ ...itemMutations }) }) const itemMutations = { addItem: { type: ItemType, args: { input: { type: new GraphQLNonNull(ItemInputType), } } }, resolve: (root, {input}) => { const newItem = await new Promise((resolve) => { setTimeout(() => resolve(Object.assign(input, { id: random.uuid(), })), 100) }) pubsub.publish(ITEM_ADDED_TOPIC, { itemAdded: newItem }) return newItem } } const SubscriptionType = { itemAdded: { subscribe: () => pubsub.asyncIterator(ITEM_ADDED_TOPIC) } }
We now have to set up the server like so:
import { execute, subscribe } from 'graphql' import { createServer } from 'http' import { SubscriptionServer } from 'subscriptions-transport-ws' const server = express() const ws = createServer(server) ws.listen(PORT, () => { console.log(`GraphQL Server is now running on http://localhost:${PORT}`) new SubscriptionServer({ execute, subscribe, schema }, { server: ws, path: '/subscriptions', }) }) server.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql', subscriptionsEndpoint: `ws://localhost:${PORT}/subscriptions` }))
Caching
Caching could be achieve with Dataloader. It will avoid unnecessary multiple requests.
Using Dataloader could be achieved like so:
... const DataLoader = require('dataloader') ... const fetchArtist = id => fetch(`http://api.musicgraph.com/api/v2/artist/${args.id}?api_key=${process.env.API_KEY}`) .then(res => res.json()) app.use('/graphql', graphqlHTTP(req => { const artistLoader = new DataLoader(keys => Promise.all(keys.map(fetchArtist))) return { schema, context: { artistLoader } graphiql: true } }))
And then...
module.exports = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQuery', description: 'The main query', // Defining the Fields fields: () => ({ artist: { type: ArtistType, args: { id: { type: GraphQLString } }, resolve: (root, args, context) => context.artistLoader.load(args.id) } }) }) })
Conclusion
Through this small article, we took a look at GraphQL and we got our hands on some theoretical and practical notions. We saw that GraphQL is a query language and how to quickly set up simple GraphQL Server.
One last word
If you like this article, you can consider supporting and helping me on Patreon! It would be awesome! Otherwise, you can find my other posts on Medium and Tumblr. You will also know more about myself on my personal website. Until next time, happy headache!
0 notes