A backend service for storing and manipulating and syncing documents
.
├── README.md -- You are reading this now
├── package.json -- NodeJS dependencies
├── yarn.lock
├── serverless.yml -- Serverless configuration
├── src
│ ├── entities -- Data models
│ │ └── documents.ts -- Data models dealing with documents
│ ├── handlers -- Lambda handler functions
│ │ ├── connection.ts -- connection / disconnection
│ │ ├── documents.ts -- document operations
│ │ └── message.ts -- message routing
│ └── utils
│ └── aws.ts -- AWS utility functions
├── tests -- TODO
The intrastructure dependencies are described in serverless.yml
.
Currently 2 DynamoDB tables are provisioned by serverless
.
connectionTable
: Stores the connection IDs used for messaging the clients connected over WebSocketsdocsTable
: Stores document data
- Install
sls
tool:
brew install serverless
- clone repo
git clone <repo_url>
- deploy to aws
sls deploy
- Install dependencies
yarn install
- Invoke functions
CONNECTION_TABLE=connectionTable DOCUMENTS_TABLE=docsTable sls invoke local --function default --data '{ "requestContext": { "routeKey": "$default", "messageId": "Yz5MEf7dIAMCJfw=", "eventType": "MESSAGE", "extendedRequestId": "Yz5MEE6QoAMFusQ=", "requestTime": "21/Sep/2022:12:50:59 +0000", "messageDirection": "IN", "stage": "dev", "connectedAt": 1663764639475, "requestTimeEpoch": 1663764659493, "identity": { "sourceIp": "94.21.159.157" }, "requestId": "Yz5MEE6QoAMFusQ=", "domainName": "i5lj1y1eui.execute-api.us-east-1.amazonaws.com", "connectionId": "Yz5I8fWGIAMCJfw=", "apiId": "i5lj1y1eui" }, "body": "{\"operation\": \"LIST\"}", "isBase64Encoded": false }'
(To initialize blank project)
sls create --template aws-nodejs-typescript --path
Document:
- documentId: UUID -- identified, auto generated on creation)
- name: string -- name of document
- content: string -- content of document
After connecting to the service over websocket, clients have to use a custom message protocol for document operations.
Theres a special key always required in the message:
operation: <GET|LIST|CREATE|UPDATE|DELETE>
, which specifies the type of document operation. Payload is dependent on the operation.
List documents
Returns all documents.# LIST DOCUMENTS
Client request ->
{
"operation": "LIST"
}
Server response <-
{
"documents": [
{
"name": "mydoc",
"content": "<html><body>Hello</body></html>",
"documentId": "ea7b41c0-08db-41b6-af95-973640a68a6e"
},
...
}
Get document
Returns a single document.# GET DOCUMENT
Client request ->
{
"operation": "GET",
"documentId": "0935fae2-2d97-4da5-a97e-b3e2f6ccdbcd"
}
Server response <-
{
"document": {
"name": "mydoc",
"content": "<html><body>Hello</body></html>",
"documentId": "0935fae2-2d97-4da5-a97e-b3e2f6ccdbcd"
}
}
Create document
Creates a document, `documentId` will be autogenerated.# CREATE DOCUMENT
Client request ->
{
"operation": "CREATE",
"document": {
"content": "<html><body>New shiny document</body></html>",
"name": "shiny_doc"
}
}
Server response <-
{
"operation": "CREATE",
"document": {
"name": "shiny_doc",
"content": "<html><body>New shiny document</body></html>",
"documentId": "9b4d6c25-011d-4438-ab45-61c29242e438"
}
}
Update document
Updates the document if matching `documentId` is found. Otherwise inserts a new document.# UPDATE DOCUMENT
Client request ->
{
"operation": "UPDATE",
"document": {
"content": "<html><body>Updated doc</body></html>",
"name": "Updated doc",
"documentId": "81d53e7f-28a7-442a-bfbb-627bea8327d0"
}
}
Server response <-
{
"operation": "UPDATE",
"document": {
"name": "Updated doc",
"content": "<html><body>Updated doc</body></html>",
"documentId": "81d53e7f-28a7-442a-bfbb-627bea8327d0"
}
}
Delete document
Deletes document with `documentId`.# DELETE DOCUMENT
Client request ->
{
"operation": "DELETE",
"documentId": "d1957b9c-057e-431a-9641-b463dfdfc007"
}
Server response <-
{
"operation": "DELETE",
"document": "d1957b9c-057e-431a-9641-b463dfdfc007"
}
Upon write operations, broatcast message is emitted to all clients, so the clients can act upon the messages and update their state.
Document creation
A document was created. `document` key contains the document object the clients should store internally.{
"operation": "CREATE",
"document": {
"name": "a",
"content": "<html><body>New shiny document</body></html>",
"documentId": "9b4d6c25-011d-4438-ab45-61c29242e438"
}
}
Document update
A document was updated. `document` key contains the new state of document object the clients should update internally.{
"operation": "UPDATE",
"document": {
"name": "a",
"content": "<html><body>Updated document</body></html>",
"documentId": "9b4d6c25-011d-4438-ab45-61c29242e438"
}
}
Document deletion
A document was deleted. Document with `documentId` does not exist anymore, the clients should update their state accordingly.{
"operation": "DELETE",
"document": "d1957b9c-057e-431a-9641-b463dfdfc007"
}