Skip to content
Open
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
15 changes: 15 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"presets": [
"@babel/typescript",
[
"@babel/env",
{
"modules": false,
"targets": {
"node": "8.20"
}
}
]
],
"plugins": ["@babel/plugin-proposal-object-rest-spread"]
}
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
**Edit a file, create a new file, and clone from Bitbucket in under 2 minutes**

When you're done, you can delete the content in this README and update the file with details for others getting started with your repository.

*We recommend that you open this README in another tab as you perform the tasks below. You can [watch our video](https://youtu.be/0ocf7u76WSo) for a full demo of all the steps in this tutorial. Open the video in a new tab to avoid leaving Bitbucket.*

---

## Edit a file

You’ll start by editing this README file to learn how to edit a file in Bitbucket.

1. Click **Source** on the left side.
2. Click the README.md link from the list of files.
3. Click the **Edit** button.
4. Delete the following text: *Delete this line to make a change to the README from Bitbucket.*
5. After making your change, click **Commit** and then **Commit** again in the dialog. The commit page will open and you’ll see the change you just made.
6. Go back to the **Source** page.

---

## Create a file

Next, you’ll add a new file to this repository.

1. Click the **New file** button at the top of the **Source** page.
2. Give the file a filename of **contributors.txt**.
3. Enter your name in the empty file space.
4. Click **Commit** and then **Commit** again in the dialog.
5. Go back to the **Source** page.

Before you move on, go ahead and explore the repository. You've already seen the **Source** page, but check out the **Commits**, **Branches**, and **Settings** pages.

---

## Clone a repository

Use these steps to clone from SourceTree, our client for using the repository command-line free. Cloning allows you to work on your files locally. If you don't yet have SourceTree, [download and install first](https://www.sourcetreeapp.com/). If you prefer to clone from the command line, see [Clone a repository](https://confluence.atlassian.com/x/4whODQ).

1. You’ll see the clone button under the **Source** heading. Click that button.
2. Now click **Check out in SourceTree**. You may need to create a SourceTree account or log in.
3. When you see the **Clone New** dialog in SourceTree, update the destination path and name if you’d like to and then click **Clone**.
4. Open the directory you just created to see your repository’s files.

Now that you're more familiar with your Bitbucket repository, go ahead and add a new file locally. You can [push your change back to Bitbucket with SourceTree](https://confluence.atlassian.com/x/iqyBMg), or you can [add, commit,](https://confluence.atlassian.com/x/8QhODQ) and [push from the command line](https://confluence.atlassian.com/x/NQ0zDQ).
11 changes: 11 additions & 0 deletions dynamodb/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DynamoDB } from "aws-sdk";

let dynamoDbClient = new DynamoDB.DocumentClient();
if (process.env.IS_OFFLINE) {
dynamoDbClient = new DynamoDB.DocumentClient({
endpoint: "http://localhost:8000",
region: "localhost",
});
}

export { dynamoDbClient };
16 changes: 16 additions & 0 deletions env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Add the environment variables for the various stages

prod:
ENV_TYPE: "prod"
default:
ENV_TYPE: "dev"
ELASTICSEARCH_URL:
Fn::GetAtt:
- EventsGqlElasticSearch
- DomainEndpoint # get domain endpoitn attribute from elastic search resource
test:
ENV_TYPE: "test"
ELASTICSEARCH_URL:
Fn::GetAtt:
- EventsGqlElasticSearch
- DomainEndpoint
24 changes: 24 additions & 0 deletions handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import schema from "./schemas/index";
import { graphql } from "graphql";
import { APIGatewayProxyEvent } from "aws-lambda";
// Highly scalable FaaS architecture :)
// Export a function which would be hooked up to the the λ node/ nodes as specified on serverless.yml template
export async function queryEvents(
event: APIGatewayProxyEvent,
) {
const parsedRequestBody = event && event.body ? JSON.parse(event.body) : {};
try {
const graphQLResult = await graphql(
schema,
parsedRequestBody.query,
null,
null,
parsedRequestBody.variables,
parsedRequestBody.operationName,
);

return { statusCode: 200, body: JSON.stringify(graphQLResult) };
} catch (error) {
throw error;
}
}
72 changes: 72 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"name": "serverless-graphql-template",
"version": "1.0.0",
"author": {
"name": "Dasith kuruppu",
"email": "dasithkuruppu@gmail.com",
"url": "https://github.com/DasithKuruppu"
},
"description": "Initial boilerplate for serverless lambda with dynamodb,elasticsearch and graphql - events booking app",
"keywords": [
"FaaS",
"nodejs",
"dynamodb",
"lambda",
"serverless",
"graphql",
"elasticsearch"
],
"main": "handler.js",
"scripts": {
"test-lambda": "serverless invoke test --stage default -f queryEvents",
"start": "serverless offline start",
"compile-debug": "serverless webpack --out dist",
"deploy-dev": "serverless deploy -v --stage=devdefault --force",
"deploy-test": "serverless deploy -v --stage=test",
"deploy-prod": "serverless deploy -v --stage=prod",
"deploy-to-bucket": "serverless s3sync",
"tslint": "tslint --project tsconfig.json --config tslint.json",
"compile-tests": "webpack --config configs/test.webpack.config.js",
"unit-tests": "jest --config jest.config.js --verbose",
"dynamodb-start": "serverless dynamodb start",
"dynamodb-install": "serverless dynamodb install"
},
"license": "ISC",
"dependencies": {
"@elastic/elasticsearch": "^7.0.0-rc.2",
"aws-sdk": "^2.361.0",
"graphql": "^14.0.2",
"graphql-dynamodb-connections": "^1.0.2",
"graphql-iso-date": "^3.6.1",
"serverless-s3-sync": "^1.8.0",
"uuid": "^3.3.2"
},
"devDependencies": {
"@babel/cli": "^7.1.5",
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
"@babel/preset-env": "^7.1.6",
"@babel/preset-typescript": "^7.1.0",
"@types/aws-lambda": "^8.10.15",
"@types/graphql": "^14.0.3",
"@types/jest": "^23.3.10",
"@types/node": "^10.12.11",
"babel-loader": "^8.0.4",
"jest": "^24.6.0",
"serverless": "^1.41.1",
"serverless-dynamodb-local": "^0.2.25",
"serverless-jest-plugin": "^0.2.1",
"serverless-offline": "^3.31.3",
"serverless-plugin-offline-dynamodb-stream": "^1.0.18",
"serverless-webpack": "^5.2.0",
"source-map-loader": "^0.2.4",
"ts-jest": "^23.10.5",
"tslint": "^5.11.0",
"tslint-eslint-rules": "^5.4.0",
"typescript": "^3.2.1",
"webpack": "^4.26.0",
"webpack-cli": "^3.1.2",
"webpack-node-externals": "^1.7.2",
"serverless-s3-deploy": "^0.8.0"
}
}
27 changes: 27 additions & 0 deletions resolvers/events/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IEvent } from "./typings";
import { dynamoDbClient } from "../../dynamodb";
import * as uuidv4 from "uuid/v4";

export function createParams(data: IEvent, TableName: string , uniqueID: string) {
return {
Item: {
name: data.name,
description: data.description,
id: uniqueID,
addedAt: Date.now(),
},
TableName,
};
}

export default (data: IEvent) => {
const putParams = createParams(data, process.env.TABLE_NAME, uuidv4());
return dynamoDbClient
.put(putParams)
.promise()
.then(() => {
return putParams.Item;
}).catch((err) => {
throw err;
});
};
10 changes: 10 additions & 0 deletions resolvers/events/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { DynamoDB } from "aws-sdk";
import { dynamoDbClient } from "../../dynamodb";
export default () =>
dynamoDbClient
.scan({ TableName: process.env.TABLE_NAME })
.promise()
.then((list: DynamoDB.DocumentClient.ScanOutput) => list.Items.map(
(Item) => {
return ({ ...Item, addedAt: new Date(Item.addedAt) });
}));
15 changes: 15 additions & 0 deletions resolvers/events/remove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { dynamoDbClient } from "../../dynamodb";

export default async (id: string) => {
const params = {
TableName: process.env.TABLE_NAME,
Key: { id },
ReturnValues: "ALL_OLD",
};
try {
const response = await dynamoDbClient.delete(params).promise();
return response.Attributes;
} catch (error) {
throw error;
}
};
7 changes: 7 additions & 0 deletions resolvers/events/typings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IEvent {
id: string;
name: string;
description?: string;
addedAt: number;
startingOn?: number;
}
10 changes: 10 additions & 0 deletions resolvers/events/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { dynamoDbClient } from "../../dynamodb";

export default async (id: string) => {
const params = {
TableName: process.env.TABLE_NAME,
Key: { id },
};
const GetEvents = await dynamoDbClient.get(params).promise();
return GetEvents.Item;
};
20 changes: 20 additions & 0 deletions resources/dynamodb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Resources:
EventsGqlDynamoDbTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:custom.tableName}
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
# Set the capacity based on the stage
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES



84 changes: 84 additions & 0 deletions schemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*import {
paginationToParams,
dataToConnection
} from "graphql-dynamodb-connections";
*/
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLNonNull,
GraphQLBoolean,
} from "graphql";

import {
/*GraphQLDate,
GraphQLTime,*/
GraphQLDateTime,
} from "graphql-iso-date";

import { IEvent } from "../resolvers/events/typings";
import addEvent from "../resolvers/events/create";
import viewEvent from "../resolvers/events/view";
import listEvents from "../resolvers/events/list";
import removeEvent from "../resolvers/events/remove";

const eventType = new GraphQLObjectType({
name: "Event",
fields: {
id: { type: new GraphQLNonNull(GraphQLString) },
name: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
addedAt: { type: new GraphQLNonNull(GraphQLDateTime) },
},
});

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: {
listEvents: {
type: new GraphQLList(eventType),
resolve: (parent ) => {
return listEvents();
},
},
viewEvent: {
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
type: eventType,
resolve: (parent, args: { id: string }) => {
return viewEvent(args.id);
},
},
},
}),

mutation: new GraphQLObjectType({
name: "Mutation",
fields: {
createEvent: {
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
description: { type: new GraphQLNonNull(GraphQLString) },
},
type: eventType,
resolve: (parent, args: IEvent) => {
return addEvent(args);
},
},
removeEvent: {
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
type: eventType,
resolve: (parent, args: { id: string }) => {
return removeEvent(args.id);
},
},
},
}),
});
export default schema;
Loading