Я следую учебному пособию: Как использовать DynamoDB с Apollo GraphQL и NodeJS Без сервера в автономном режиме и в работе
Я создал следующие файлы:
compose.yml
version: "3"
services:
knocker-lambda-api:
build:
context: ./knocker-lambda-api/
dockerfile: Dockerfile
expose:
- 3000
ports:
- 3000:3000
restart: on-failure
volumes:
- ./knocker-lambda-api/:/app
# here could be the other applications
db:
image: amazon/dynamodb-local
entrypoint: java
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /data"
expose:
- 8000
ports:
- 8000:8000
volumes:
- ./.db-files/:/data
db.gui:
image: aaronshaf/dynamodb-admin
expose:
- 8001
ports:
- 8001:8001
environment:
- DYNAMO_ENDPOINT=http://db:8000
обработчик. js
export { default as main } from "./index.js";
index. js
import { ApolloServer } from "apollo-server-lambda";
import resolvers from "./graphql/resolvers";
import typeDefs from "./graphql/types";
import { PinballMachineAPI } from "./graphql/data-sources/pinball";
import KnockerDB from "./graphql/data-sources/knocker-db";
// set up any dataSources our resolvers need
const dataSources = () => ({
pinballMachineAPI: new PinballMachineAPI(),
knockerDB: new KnockerDB(),
});
// Set up Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources,
introspection: true,
playground: true,
});
const handler = server.createHandler({
cors: {
origin: "*",
credentials: true,
},
});
/* const handler = (event, context, callback) => {
// tell AWS lambda we do not want to wait for NodeJS event loop
// to be empty in order to send the response
context.callbackWaitsForEmptyEventLoop = false;
// process the request
return handler(event, context, callback);
}; */
export default handler;
база данных. js
import AWS from "aws-sdk";
import tables from "./tables";
export default class Database {
async connect() {
if (!this._connection) {
let params = {};
console.log(__DEV__);
if (__DEV__) {
params = {
endpoint: process.env.DB_URL,
region: "local",
accessKeyId: "local",
secretAccessKey: "local",
};
} else {
params = {
region: "us-east-2",
apiVersion: "2012-08-10",
};
}
this._connection = new AWS.DynamoDB(params);
if (__DEV__) {
// will create tables through lambda only in development
await this.createTables(tables);
}
}
console.log(this._connection);
return this._connection;
}
async get() {
return new Promise((resolve, reject) => {
this._connection.get(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async putItem(params) {
return new Promise((resolve, reject) => {
this._connection.putItem(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async getItem(params) {
return new Promise((resolve, reject) => {
this._connection.getItem(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async updateItem(params) {
return new Promise((resolve, reject) => {
this._connection.updateItem(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async scan(params = {}) {
return new Promise((resolve, reject) => {
this._connection.scan(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async deleteItem(params) {
return new Promise((resolve, reject) => {
this._connection.deleteItem(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
async createTables(tables) {
for (let k = 0; k < tables.length; k++) {
const table = tables[k];
await new Promise((resolve, reject) => {
this._connection.createTable(table, (err) => {
if (err) {
if (err.code !== "ResourceInUseException") {
console.dir(err);
reject(err);
} else {
console.dir(`Table "${table.TableName}" exists`);
resolve();
}
} else {
console.dir(`Created table "${table.TableName}"`);
resolve();
}
});
});
}
}
}
knocker-db. js
import Database from "../../db/database";
import stringGen from "crypto-random-string";
export default class KnockerDB {
// our methods go here, we are going to discuss them below
async put(data) {
const item = {
username: {
S: data.username.toString(),
},
name: {
S: data.name.toString(),
},
machinesPlayed: {
L: [],
},
scores: {
L: [],
},
locationsVisited: {
L: [],
},
email: {
S: data.email.toString(),
},
};
if (data.id) {
item.id = { S: data.id.toString() };
} else {
// as we mentioned before, we need to specify a new key explicitly
item.id = { S: stringGen(12) };
}
const db = await this.getDatabase();
await db.putItem({
TableName: "player",
Item: item,
});
}
async get() {
const db = await this.getDatabase();
return db.get({
TableName: "player",
Key: {
HashKey: "hashkey",
},
});
}
async getById(id) {
const db = await this.getDatabase();
return db.getItem({
TableName: "player",
Key: {
id: {
S: id.toString(),
},
},
});
}
async getByUsername(username) {
const db = await this.getDatabase();
return db.getItem({
TableName: "player",
Key: {
username: {
S: username.toString(),
},
},
});
}
/*
async getForCharacter(id) {
const db = await this.getDatabase();
const result = await db.scan({
TableName: 'player',
ExpressionAttributeValues: {
':cId': {
S: id,
},
},
FilterExpression: 'contains(characters, :cId)',
});
if (result && result.Items) {
// need to "decode" the items, i know this is annoying
return result.Items.map((item) => {
const p = item.parameters ? item.parameters.M : {};
const parameters = [];
Object.keys(p).forEach((k) => {
parameters.push({
name: k,
value: p[k].S,
});
});
return {
name: item.name.S,
damage: item.damage.N,
id: item.id.S,
type: item.type.S,
parameters,
};
});
}
return [];
} */
async delete(id) {
const db = await this.getDatabase();
await db.deleteItem({
TableName: "player",
Key: {
id: {
S: id.toString(),
},
},
});
}
async getDatabase() {
if (!this._db) {
console.log("db created");
this._db = new Database();
await this._db.connect();
}
return this._db;
}
}
Я создал репозиторий для этого проекта: Ссылка
Чтобы создать бэкэнд проекта, я выполните следующее:
npm i
docker-compose -f compose.yml build
Чтобы запустить бэкэнд проекта, я запускаю следующее:
docker-compose -f compose.yml up
Я получаю доступ к пользовательскому интерфейсу GraphQL с помощью: http://localhost:3000/dev/knocker-api
Примечание. URL-адрес в пользовательском интерфейсе GraqhQL совпадает с приведенным выше
При доступе к пользовательскому интерфейсу GraphQL возникает следующая ошибка: брошено:
В https://cdn.jsdelivr.net/npm/@apollographql / graphql-playground-react@1.7.32/build/static/node_modules/apollo-link-http-common/lib/index.js
"TypeError: response.http.headers is not iterable"
Кто-нибудь знает, почему я у меня проблема?