Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dataloader): create an example app that uses Apollo, koa, a dataloader and Mongo #68

Merged
merged 1 commit into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions graphql-koa-dataloader/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:lts-slim
EXPOSE 4000

RUN apt-get update && apt-get install httpie jq -y

WORKDIR /app
COPY app .
COPY entrypoint.sh /entrypoint.sh
RUN npm ci

ENTRYPOINT ["/entrypoint.sh"]
7 changes: 7 additions & 0 deletions graphql-koa-dataloader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This is an example of an Apollo server using the Koa middleware and dataloader

Get it running:

1. Copy sample.env to .env
2. Edit .env to add your New Relic ingest key, and any other desired changes.
3. Start with `docker-compose up --build`.
17 changes: 17 additions & 0 deletions graphql-koa-dataloader/app/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2022 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

module.exports = {
extends: '@newrelic',
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
'no-console': 'off',
'func-names': 'off'
}
}
26 changes: 26 additions & 0 deletions graphql-koa-dataloader/app/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import DataLoader from 'dataloader'
import { getUserById } from './datastore.js'

const loaders = () => ({
getUserById: new DataLoader(
(ids) => {
return Promise.all(ids.map((id) => getUserById(Number(id))))
},
{
batchScheduleFn: (callback) => setTimeout(callback, 100)
}
)
})

const getContext = () => {
return {
loaders: loaders()
}
}

export default getContext
78 changes: 78 additions & 0 deletions graphql-koa-dataloader/app/datastore-dynamodb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import {
DynamoDBClient as Client,
CreateTableCommand,
DeleteTableCommand,
waitUntilTableExists,
waitUntilTableNotExists
} from '@aws-sdk/client-dynamodb'
import { DynamoDBDocumentClient, GetCommand, PutCommand } from '@aws-sdk/lib-dynamodb'
import { generateUsers } from './utils.js'

const tableParams = {
AttributeDefinitions: [
{
AttributeName: 'id',
AttributeType: 'N'
}
],
KeySchema: [
{
AttributeName: 'id',
KeyType: 'HASH'
}
],
ProvisionedThroughput: {
ReadCapacityUnits: 1,
WriteCapacityUnits: 1
},
TableName: 'Users',
StreamSpecification: {
StreamEnabled: false
}
}

async function initData() {
const client = new Client({})
try {
console.log('Deleting table...')
await client.send(new DeleteTableCommand({ TableName: 'Users' }))
await waitUntilTableNotExists({ client }, { TableName: 'Users' })
} catch (err) {
console.log(err.message)
}
console.log('Creating table...')
await client.send(new CreateTableCommand(tableParams))
await waitUntilTableExists({ client }, { TableName: 'Users' })

console.log('Adding users...')
const docClient = DynamoDBDocumentClient.from(client)
const users = generateUsers(10)
for (const user of users) {
user.id = user._id
await docClient.send(
new PutCommand({
TableName: 'Users',
Item: user
})
)
}
console.log('added 10 users')
}

async function getUserById(id) {
const client = DynamoDBDocumentClient.from(new Client({}))
const { Item: item } = await client.send(
new GetCommand({
TableName: 'Users',
Key: { id }
})
)
return item
}

export { getUserById, initData }
40 changes: 40 additions & 0 deletions graphql-koa-dataloader/app/datastore-mongo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import { MongoClient } from 'mongodb'
import { generateUsers } from './utils.js'

async function initData() {
const client = new MongoClient(process.env.MONGO_URL)
try {
await client.connect()
const allUsers = generateUsers(10)
const db = client.db('users')
const friends = db.collection('friends')
await friends.deleteMany()

const result = await friends.insertMany(allUsers, { ordered: true })
console.log(`${result.insertedCount} users were created`)
} finally {
client.close()
}
}

async function getUserById(id) {
console.log(`Fetch user #${id}`)
const client = new MongoClient(process.env.MONGO_URL)
try {
await client.connect()
const db = client.db('users')
const friends = db.collection('friends')
const result = await friends.findOne({ _id: id })
console.log(result)
return result
} finally {
client.close()
}
}

export { getUserById, initData }
33 changes: 33 additions & 0 deletions graphql-koa-dataloader/app/datastore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import * as dynamodb from './datastore-dynamodb.js'
import * as mongo from './datastore-mongo.js'

async function initData() {
if (process.env.NEW_RELIC_DATASTORE?.toLowerCase() === 'dynamodb') {
return await dynamodb.initData()
}
if (process.env.NEW_RELIC_DATASTORE?.toLowerCase() === 'mongo') {
return await mongo.initData()
}
throw new Error(
'Unknown datastore. Set the "NEW_RELIC_DATASTORE" environment variable to either "DynamoDB" or "Mongo"'
)
}

async function getUserById(id) {
if (process.env.NEW_RELIC_DATASTORE?.toLowerCase() === 'dynamodb') {
return await dynamodb.getUserById(id)
}
if (process.env.NEW_RELIC_DATASTORE?.toLowerCase() === 'mongo') {
return await mongo.getUserById(id)
}
throw new Error(
'Unknown datastore. Set the "NEW_RELIC_DATASTORE" environment variable to either "DynamoDB" or "Mongo"'
)
}

export { getUserById, initData }
18 changes: 18 additions & 0 deletions graphql-koa-dataloader/app/make-requests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
shopt -s expand_aliases

alias http='http --print b --ignore-stdin'

echo "Starting request loop"
while true; do

# Get all users
echo "Fetching all users"
http localhost:4000 query='{allUsers{age, email, hobbies, id, name, friends}}' | jq -C

for id in {1..10}; do
echo "Fetching user #${id}"
http localhost:4000 query="{user(id:${id}){age, email, hobbies, id, name, friends}}" | jq -C
done
sleep 5
done
Loading