My GraphQL Note
— graphql — 4 min read
GraphQL is a query language for APIs, and a runtime for executing these queries against your data. It was developed by Facebook and publicly released in 2015.
In traditional REST APIs, you would have to make multiple API calls to retrieve the desired data, but with GraphQL, you make a single call to retrieve exactly what you need. With GraphQL, the client specifies the structure of the data it needs, and the server returns only the requested data.
GraphQL also has a type system that allows you to describe the data in your API, so that the client knows what it can request and what the server can return. This type system provides a contract between the client and the server, and helps catch errors before they happen.
One of the main advantages of GraphQL is that it allows for more efficient and flexible communication between the client and the server. Clients can specify exactly what data they need, and the server will return only that data, reducing the amount of unnecessary data transfer.
GraphQL IDE
To use Apollo Studio, follow the tutorial:
Schema
Schema Definition Language, SDL, is the syntax for writing schemas.
type Topping { id: Int! topping: String!}
type Pizza { id: Int! pizza: String! toppings: [Topping!]!}
Query
The Query
type is a special object type that defines all of the top-level entry points for queries that clients execute against your server. Each field of the Query
type defines the name and return type of a different entry point
type Query { hello: String jobs(companyId: Int!): [Job] user(email: String!): User}
Structuring query
// operation, operation namequery getPizzas{ pizzas{ pizza }}
{ "data": { "pizzas": [ { "pizza": "Neapolitan Pizza" }, { "pizza": "Chicago Pizza" } ] }}
The Mutation type
The Mutation
type is similar in structure and purpose to the Query
type. Whereas the Query
type defines entry points for read operations, the Mutation
type defines entry points for write operations.
Each field of the Mutation
type defines the signature and return type of a different entry point
type Mutation { addBook(title: String, author: String): Book}
Server
Apollo Server is an open-source, spec-compliant GraphQL server that's compatible with any GraphQL client, including Apollo Client.
Create a project and install dependencies
With @as-integrations/aws-lambda
package, you can easily set up and run Apollo Server on AWS lambda
yarn add @apollo/server @as-integrations/aws-lambda graphql
import { Context } from 'aws-lambda'require('dotenv').config()
import { ApolloServer } from '@apollo/server'import { ApolloServerPluginLandingPageLocalDefault, ApolloServerPluginLandingPageProductionDefault,} from '@apollo/server/plugin/landingPage/default'import { startServerAndCreateLambdaHandler, handlers,} from '@as-integrations/aws-lambda'
import config from '../config'import resolvers from './resolvers'
const Sentry = require('@sentry/serverless')Sentry.AWSLambda.init({ dsn: config.sentryDsn, tracesSampleRate: 1.0,})
const typeDefs = require('./schema.graphql')const server = new ApolloServer({ typeDefs, resolvers, introspection: true, csrfPrevention: true, cache: 'bounded', plugins: [ process.env.NODE_ENV === 'production' ? ApolloServerPluginLandingPageProductionDefault() : ApolloServerPluginLandingPageLocalDefault({ embed: true }), ],})
console.info('Starting graphql server')
export const handler = startServerAndCreateLambdaHandler( server, handlers.createAPIGatewayProxyEventRequestHandler())
Define GraphQL schema
I put all schema definitions in schema.graphql
file. It’s handy to define schemas in a separate file with .graphql
extension as VS Code will syntax-highlighting the content. To import the file, I use Webpack’s asset/source
loader
const path = require('path')
module.exports = { target: 'node', entry: { graphql: './src/graphql/index.ts', }, devtool: 'inline-source-map', module: { rules: [ { test: /\.ts$/, exclude: /node_modules/, use: 'ts-loader', }, { test: /\.(graphql|gql)$/, exclude: /node_modules/, type: 'asset/source', }, ], }, resolve: { extensions: ['.ts', '.js'], }, output: { library: { type: 'commonjs2', }, filename: '[name]/index.js', path: path.resolve(__dirname, 'dist'), },}
# schema.graphqltype Job { jobNo: String companyId: String customer: Customer product: Product serviceRequestDate: String jobStatus: String jobNotes: String services: [Service] photos: [Photo] estimatedSymptom: String customerComment: String engineer: Engineer reportDate: String reportDateTime: String creationDateTime: String boilerReadings: Readings}
Define a resolver
import { isInLastXDays } from '../../common/services/dateFunctions'import { jobStatusCodes } from '../common/codes'import { getJobs } from '../common/jobsRepository'
const resolvers = { Query: { hello() { return 'Hello world!' }, async jobs( parent: any, args: { companyId: string }, contextValue: any, info: any ) { const [jobs, statusCode, errorMessage] = await getJobs(args.companyId) const jobsForOverview = jobs.filter( x => x.jobStatus !== jobStatusCodes.COMPLETED || (x.jobStatus === jobStatusCodes.COMPLETED && isInLastXDays(x.reportDate, 7)) )
return errorMessage ? [] : jobsForOverview }, },}
export default resolvers