Assignment 2: Anchors & Links
Released: October 5th, 9:00am ET
Due: October 17th, 11:59pm ET
Introduction
Now that we have finished making and preparing the dough for our donuts, it’s time to bake our dough 🧑🍳! In this assignment, you will be building upon the concepts introduced in Lab 2 and provide anchor and linking support to a standard node file system. At the end, you will have built a full-stack application that will allow users to create, delete and navigate with Hypertext links via the frontend. You will be graded on the functionality of your frontend and the backend via a test suite. We also want to re-emphasize the 30-minute rule – if you can’t get something that appears straightforward to work in 30 minutes, it may not be your problem. Don’t be afraid to send a message in Slack if something unexpected comes up.
We have refactored our code base to support additional features for this assignment. Please build your system with the new stencil code we provided in this new GitHub Classroom Assignment. Accept the GitHub repository here.
This assignment places a heavy emphasis on the frontend, and contains heavier workload than Assignment 1: Nodes. Start early, start today, start yesterday!
Checklist
Backend:
Frontend:
Deployment:
Architecture
The following architecture diagram should give you a better understanding of the new structure of our full-stack application. A few important things to note:
- Additional functionality is added to our application through more backend services that do not depend on each other.
- Our frontend is a single application that consumes all of our backend services.
- Although all of our MongoDB collections belong to the same cluster, each collection is entirely independent of the other collections in the cluster.
In your next technical interview, you can refer to this project as a Monolithic Front End (a.k.a a single, large component) that interacts with a microservice (a.k.a. multiple, smaller components) backend. For more information on the tradeoffs between monolithic applications and microservices take a look at this article.

Tips and essential shortcuts for large codebases
Typescript tips
TypeScript/JavaScript has powerful operators that can make a programmer’s life easier. Here are some tips for making your code cleaner!
-
Logical AND (&&) operator
donut && console.log('delicious')
is the same thing as
if (donut) {
console.log('delicious')
}
-
Nullish coalescing operator (??)
It returns its right-hand side operand when its left-hand side operand is null
or undefined
, and otherwise returns its left-hand side operand. When used properly, it ensures that the variable we are assigning value to is never undefined
or null
.
const anchorId: string = currentValue ?? 'default string'
-
Conditional (ternary) operator
isDonut ? 'coffee' : 'milk'
is the same as writing
if (isDonut){
return 'coffee'
} else {
return 'milk'
}
Additionally click here if the example does not suffice.
-
===
vs. ==
==
converts the variable values to the same type before performing comparison. This is called type coercion.
===
does not do any type conversion (coercion) and returns true only if both values and types are identical for the two variables being compared.
Note: Gains in performance between these two operations is negligable.
-
Optional Chaining
Introduced in 2020, the optional chaining operator (?.) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
In the following example we need to first check for donut.frosting
before we can access the flavor
of the frosting.
let nestedProp = donut.frosting && donut.frosting.flavor
However, with optional chaining you don’t have to explicitly test and short-circuit based on the state of donut.frosting before trying to access donut.frosting.flavor:
let nestedProp = donut.frosting?.flavor
Essential VS Code shortcuts
cmd + left click
: navigate to implementation/usage
cmd + P
: search by filename
cmd + shift + P
: VS Code commands
cmd + F
: search current file
cmd + shift + F
: search entire code base
Note, these commands are for Mac. Please replace all cmd
with ctrl
for Windows.
Demo
You can find a demo of the MyHypermedia application here.
Note: It is entirely possible that there will be some unexpected behavior in this application as it is not set up to handle concurrency properly. This means that the application might behave weirdly if multiple users are interacting with the demo at the same time.
If there is anything that is unclear in this assignment, or you find a bug in our code base, please let us know in Slack!
Known bugs in TA implementation
Here is a list of known bugs in our implementation. You are not expected to have these bugs fixed in your system. We will update this list if more bugs are found.
- Linking multiple times from the same anchor is not supported
- Text anchors can occasionally cause issues. Note that we would normally use an external library to handle certain functionality however, since we wanted to have full control of immutable nodes we have implemented this ourselves. Note that there are many ways to implement HTML text manipulation and we have just implemented one of them!
- FIX IN PROGRESS When you start an anchor, even when you switch to other nodes the start anchor is still selected

Backend
Continuing from the lab, you will be implementing your own MongoDB
queries.
The backend portion is worth 30% of the assignment grade.
Don’t forget to yarn install
and add your .env
file!
To Dos
Your primary objective for the backend assignment is to fill in the stencil code found at LinkCollectionConnection.ts
and AnchorCollectionConnection.ts
. We hope that writing these files will get you more familiar with creating your own MongoDB queries.
Tips:
- We highly recommend looking at
NodeCollectionConnection.ts
as a close reference for these files.
- If you are ever confused about how we have set up the MongoDB environment, please look at the Lab 2 handout for an explanation.
Running Tests
Once finished, you should be able to pass all unit (mock
) and end-to-end (e2e
) tests for Links
and Anchors
. Note that your Nodes
tests should still all pass.
Jest Commands:
Here are the following commands to run tests on a select group of Jest files:
- Running a single file:
yarn test <file-name>
- e.g.
yarn test findAnchorById.spec.ts
will run findAnchorById.spec.ts
- You can remove the file type suffix, so
yarn test findanchorbyid
works too - it is case insensitive
- Running a folder:
yarn test <folder-name>
- e.g.
yarn test Anchors/Mock/AnchorCollectionConnection
- e.g.
yarn test Anchors
will run all tests found in Anchors
- e.g.
yarn test e2e
will run all e2e
unit tests
The tests are not setup with Gradescope, they are setup with GitHub Classroom. If all tests pass, there will be a green checkmark in front of your commits.
Custom Methods and Endpoints
Feel free to add your own custom methods in any of the <x>collectionConnections
, gateways or router files. The stencil only provides methods that we the TA’s need to power our frontend. But if you think that your frontend implementation would benefit from another endpoint or support from the backend, feel free to add your custom code!
Frontend
The frontend portion is worth 60% of the assignment grade.
Introduction
In the last assignment you handled passing in props
to the TextContent
and ImageContent
components so that they would render the content. You also implemented the NodeBreadcrumb
component. The changes that you made were specific to single components rather than the overall system. When implementing links
and anchors
we need different components to interact with each other, so in this assignment you will get more familiar with the frontend system as a whole.
Frontend Roadmap
Gateway
Read and understand the implementation of the frontend LinkGateway
, AnchorGateway
and lastly NodeGateway
. You will be using methods from these files in the rest of the assigment. Through these methods we are able to make HTTP requests to the backend. You are also implementing NodeGateway.deleteNode
Links and anchors
- Make sure that you have completed Lab 2. It is important that you have an understanding of Extent and what it is before you begin this assignment.
- Implement the ability to
Start Link
- Implement the ability to
Complete Link
and design the CompleteLinkModal
- Design your
NodeLinkMenu
. Ensure the menu is always up-to-date.
Folder Structure

Remember, you can search for a file in VSCode using cmd + P
(Mac) or ctrl + P
(Windows). These commands are very useful to navigate between files, which you will have to do in this assignment!
Gateways
Now that we are supporting Links and Anchors, our frontend gateways - AnchorGateway.ts
, LinkGateway.ts
, NodeGateway.ts
- have gotten more complex. In short look at the following methods and implement them:
client/src/nodes/NodeGateway.deleteNode()
now deletes all links and orphan anchors associated with the node. Once all links and orphan anchors have been deleted, the node is then deleted.
Orphan anchors: Anchors that don’t have any links attached to them
Frontend gateway interacting with backend microservices
We have designed this project such that each of the backend microservices (router, gateway, collectionConnection) do not interact with one another. This means that there is absolutely no cross talk between the link, anchor and node backends.
So how do we delete nodes if the deletion of a node relies on the deletion of anchors and links as well? For the purposes of our assignmments (you can do this differently in your final project) we will be using the frontend gateways to handle the cross talk. For example, this is what happens in the frontend’s NodeGateway.deleteNode()
method:
- Make request to
AnchorRouter
to get anchors by nodeId
- If step 1 was successful, make request to
LinkRouter
to get links by anchorIds
- If step 2 was successful, call frontend
LinkGateway.deleteLinks()
to delete links and orphan anchors
- If step 3 was successful, make request to
NodeRouter
to delete node
You can make calls to other gateways by importing them, for example in LinkGateway, we already import AnchorGateway so that we can make a call to an AnchorGateway and delete the orphanAnchors
. You will want to do this when you are implementing NodeGateway.deleteNode()
!
import { AnchorGateway } from '../anchors'
await AnchorGateway.deleteAnchors(orphanAnchors)
TODO: Implement NodeGateway.deleteNode()
Start Link
and Complete Link
In Lab 2
you handled selecting an extent on the node that you are currently on. Once you handled the case of selecting the extent, you used setSelectedExtent
to set a app state variable that was passed down from MainView
so it can be accessible to any component in your app. There are a few other state variables that are essential when implementing Start Link
and Complete Link
. They are as follows:
App States
- isLinking:
boolean
state variable that indicates whether we are linking from a node.
const [isLinking, setIsLinking] = useState(false)
Link States
- startAnchor:
IAnchor
that indicates the particular anchor that we are linking from.
- endAnchor: Indicates the anchor that we are linking to. This is important when we want to complete the link.
const [startAnchor, setStartAnchor] = useState<IAnchor | null>(null)
const [endAnchor, setEndAnchor] = useState<IAnchor | null>(null)
Anchor States
- selectedAnchors: Indicates the list of anchors that are currently selected.
Note: To modify an array state variable you should use an updater function to prevent race conditions.
setArrayState(prevArray => ({
arrayFoo: [...prevArray.arrayFoo, newElement]
}))
const [selectedAnchors, setSelectedAnchors] = useState<IAnchor[]>([])
const [selectedExtent, setSelectedExtent] = useState<Extent | null>(null)
Note: If the Extent is null
then it should link the entire node. If it is undefined
then it should return an error.
In Lab 2
, we setSelectedExtent
in ImageContent.tsx
and FolderContent.tsx
respectively. Since the setSelectedExtent
method was passed down from MainView.tsx
, we are able to access them from MainView.tsx
anywhere in our codebase!
TODO:
- Implement
handleStartLinkClick
and handleCompleteLinkClick
in NodeView.tsx
and also ensure that the functionality meets the expecations as defined below.
- Design the
CompleteLinkModal
Functionality Expectations
- You should be able to start a link from the currently selected extent of any node to go into linking mode - which should be clearly indicated.
- There are no TODOs which indicate where exactly this mode should appear - we leave that up to you!
- The linking mode should have a way to cancel / escape the linking mode.
- When you are in linking mode, you should be able to complete the link based on the currently selected extent. This should open up a
CompleteLinkModal
.
- Design the
CompleteLinkModal
. If you want you can also change the ILink
interface in Types > ILink
and add additional properties to be added to the database! Title
and Explainer
are expected to be part of this modal.
- Make requests via the gateway so that the Anchors and Links are both succesfully created and in the database.
Now that we have the ability to create links between any two Extents
on two documents (or within the same document). Your next task is to completely design the NodeLinkMenu
, which is where you render the links for a particular node, and handle the user interactions with that link list!
TODO: Design and implement your NodeLinkMenu. You can find this in src > components > NodeView > NodeLinkMenu
The NodeLinkMenu
in the demo is only one possible design for this menu. Feel free to relocate it, adjust it, and make it your own, as long as it is usable, well designed, and the following three expectations are clear.
Click Events
You will want to use onClick
when you are designing the NodeLinkMenu component. The following code snippet is an example of how that could look.
const clickDonut = (e: React.MouseEvent, flavor: IFlavor) => {
switch(e.detail){
case 1:
console.log('Left click', flavor.sprinkles)
case 2:
console.log('Right click', flavor.dough)
}
}
return (
<div className="donut" onClick={(e) => clickDonut(e, flavor)}>
</div>
)
Note that e.detail
is how you can specify the difference between left click and right click. The above method would print the type of sprinkles
on left click, and the dough
on right click.
Don’t forget to set the css cursor
property. This is an important thing to set when it comes to usability with onClick elements!
Functionality Expectations
- You should be able to select an item in this menu, which selects the anchors in the currentNode. Think about how we can use the
setSelectedAnchors
state variable to do this! But how do we get the anchors on either end of the link? Look at the ILink interface and REMEMBER, we can always make calls to the AnchorGateway.
- You should be able to follow a link, this means that you should fetch the respective node on the other end of the link and navigates to that node
- You should be able to delete a link (and delete both of the link’s anchors). You should make gateway calls to the AnchorGateway and LinkGateway to do this!
This component should be well designed and usable. We recommend creating a mockup in Adobe Xd
or Figma
, feel free to check in with a TA to ask for feedback on your design!
Deployment
Deployment is worth 10% of the assignment grade.
You have done a great job if you are reaching this step of the assignment! After you completed coding your system, it is time to deploy it to remote servers so everyone with internet can access it. To introduce you to different technologies, we are going to deploy backend to Heroku, and frontend to Google Firebase. Feel free to share the deployment link (not the code) with family and friends, or you may even want to showcase it on your resume!
Deploying Backend
First, we’ll want to deploy our backend service to Heroku. The first step is to register an account and install Heroku CLI. To do that, please follow the link here: Getting Started on Heroku with NodeJS.
Setting up the environment
- Complete the “Introduction” and “Set up” portion of the tutorial only.
- Login to Heroku CLI with your account.
Now you should have the Heroku environment ready to go. We can start deploying our backend code. Since we have our server and client in one repo, it’s crucial that we only deploy a subfolder of the whole repository.
Creating instance and deploying
- Create a new Heroku instance with
heroku create
- Commit your changes
- In the root directory of the repository, deploy the
server
subfolder to Heroku instance:
git subtree push --prefix server heroku master
We have deployed our code to Heroku! But now it will not work yet, because we have not configured environment variables and our application does not know what port to start on.
Configuring environment variables
- Login to Heroku web portal: https://id.heroku.com/login
- Navigate to the instance you just created, and select Settings tab.
- Navigate to
Config Vars
section, and click Reveal Config Vars
.
- Fill the table with your own
DB_URI
, keeping everything else the same as the image below.

Now, if you refresh your backend instance, you should see that “MyHypermedia Backend Service” is rendered on the DOM. Moving on to deploying frontend!
Commonly Asked Questions
Q: What if I already have a Heroku account? Do I need to create a new one for this course?
A: No, you can use your personal account for Heroku. If you run out of your instance limit, consider registering a new account.
Q: The app works locally but Heroku says “Application Error”. What’s wrong?
A: Go to Heroku dashboard and select your instance. Click More
button and select View logs
option. You will see the console output and debug from there.
Check whether you have configured environmental variables (config vars) correctly.
Note that Heroku does not install devDependencies
if you have used your own npm package; consider moving it to dependencies
in package.json
.
TODO: Put your backend Heroku URL in the assignment README.md
Deploying Frontend
After deploying backend, we will be deploying our frontend using Firebase, a Google service. Before we start, we need to change the endpoint in the frontend codebase to point to the backend service we just deployed. Let’s do it!
Where can I find my backend deployment link?
Click the “Open App” button in Heroku instance page, your backend app will open in a new window.
The first step is to change the endpoint in Frontend to point to the remote backend service.
Changing the endpoint
- Navigate to
client/src/global/endpoint.ts
- Update the
endpoint
variable to point to your backend deployment. Do NOT omit the trailing /
in the URL. This will change the end point for all frontend gateways you wrote.
For example, export const endpoint = 'https://vast-caverns-62320.herokuapp.com/'
- Commit your changes
The second step is to install the Firebase CLI.
Installing Firebase CLI
Follow the instruction here to install Firebase CLI.
Next, let’s log into the Google Firebase using your personal Google Account.
The account you use must not be affliated with Brown University. Use your personal Google Account, or register a new one. Brown University accounts does not allow users to create Firebase apps.
After we log in, it is time to create and configure the Firebase app!
Create and configure Firebase app
- Navigate to
client
folder in terminal. This is important!
- Create an Firebase app using
firebase init
- When CLI prompts:
Are you ready to proceed?
, enter Y
and hit enter
- In the next step, use up/down arrow and space to select option
Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
- In the next step, select
Create a new project
- Specify your project id and project name.
- When CLI prompts:
What do you want to use as your public directory?
, enter build
.
- When CLI prompts:
Configure as a single-page app?
, enter N
.
- When CLI prompts:
Set up automatic builds and deploys with GitHub?
, ennter N
.
- Wait for the initialization to complete.
Now we have created a Firebase instance on the cloud! Since we are deploying our React application, we will use the production
version of React. How do we do that?
Build production React and deploy
- Navigate to
client
folder in terminal.
- Build production version of React using
yarn build
. This would compile your React to HTML and JS files to a folder called build
. We will deploy build
folder.
- Deploy your compiled frontend to Firebase using
firebase deploy
.
All done, have fun! If everything runs smoothly, you should be seeing your app deployed on Firebase. Firebase would give you the URL of the app upon successful deployment.
You just deployed a fully-fledged hypertext system. Open your frontend deployment, and test it as a user!
TODO: Put your frontend Firebase URL in the assignment README.md
This extra credit is not a capstone or graduate student requirement.
Now that you have implemented anchors and links for basic image and text nodes, it is the time to try to implement your own node types for extra credit. These can be anything ranging from a PDFNode
, SpreadsheetNode
, AudioNode
, VideoNode
or any other custom node type you want to add. For rendering audio, PDFs, feel free to use any packages of your choice!
These expectations are very broad - feel free to reach out to a TA with any questions you have about the specifics of how to implement one of these node types - a great place to start is thinking about what Node modules may be useful…
Creating the new node type/view counts as 10 pts and adding linking (and anchor) functionality to this view adds an extra 10 pts.
Handin
You should submit your project to the Nodes | Assignment 1 assigment in Gradescope via your GitHub repository. You should also have filled out your README.md
with the following information:
- Notable design choices
- Including any packages that you used.
- Frontend Firebase URL
- Backend Heroku URL
- Known bugs
- Only report bugs related to what you have implemente. We will be more lenient with grading if bugs are reported!
- Time taken
Note: We do not run tests through Gradescope, we run them in GitHub classroom.
Grading Breakdown
This component should include a rubric, for the backend and the frontend
Backend Tests - 30pts
Task |
Grade |
Implement AnchorCollectionConnection.ts |
15 Pts - yarn unit-test - all tests pass |
Implement LinkCollectionConnection.ts |
15 Pts - yarn unit-test - all tests pass |
Frontend Functionality - 60pts
Task |
Grade |
Implement frontend NodeGateway.deleteNode() should delete node as well as relevant anchors and links |
10 pts - TA will verify functionality on deployed version |
Linking works as expected |
25 pts - TA will verify functionality on deployed version |
NodeLinkMenu meets expected functionality |
25 pts - TA will verify |
Rubric specifications for Linking
and NodeLinkMenu
:
Sub-rubric |
Criterion |
Methods implementation |
10 pts - correctly implemented 5 pts - partially correct 0 pts - not implemented |
Design and usability |
10 pts - clicks behave as expected, items are usable and operate as expected 5 pts - clicks are not clear, poorly designed and not easily accessible 0 pts - no design consideration, buttons are not functional |
Overall design and usability |
5 pts - TA will verify |
Deployment - 10pts
Task |
Grade |
Deploy backend |
5 Pts - TA will verify |
Deploy frontend |
5 Pts - TA will verify |
Total = 100 pts
Task |
Grade |
Support an additional node type in your MyHypermedia app |
10 Pts - TA will verify |
Add linking and anchoring functionality for the additional node type that you support |
10 Pts - TA will verify |
Assignment 2: Anchors & Links
Released: October 5th, 9:00am ET
Due: October 17th, 11:59pm ET
Introduction
Now that we have finished making and preparing the dough for our donuts, it’s time to bake our dough 🧑🍳! In this assignment, you will be building upon the concepts introduced in Lab 2 and provide anchor and linking support to a standard node file system. At the end, you will have built a full-stack application that will allow users to create, delete and navigate with Hypertext links via the frontend. You will be graded on the functionality of your frontend and the backend via a test suite. We also want to re-emphasize the 30-minute rule – if you can’t get something that appears straightforward to work in 30 minutes, it may not be your problem. Don’t be afraid to send a message in Slack if something unexpected comes up.
We have refactored our code base to support additional features for this assignment. Please build your system with the new stencil code we provided in this new GitHub Classroom Assignment. Accept the GitHub repository here.
This assignment places a heavy emphasis on the frontend, and contains heavier workload than Assignment 1: Nodes. Start early, start today, start yesterday!
Checklist
Backend:
AnchorCollectionConnection
andLinkCollectionConnection
Frontend:
NodeGateway.deleteNode()
handleStartLinkClick
andhandleCompleteLinkClick
inNodeView.tsx
CompleteLinkModal
NodeLinkMenu
Deployment:
Architecture
The following architecture diagram should give you a better understanding of the new structure of our full-stack application. A few important things to note:
In your next technical interview, you can refer to this project as a Monolithic Front End (a.k.a a single, large component) that interacts with a microservice (a.k.a. multiple, smaller components) backend. For more information on the tradeoffs between monolithic applications and microservices take a look at this article.
Tips and essential shortcuts for large codebases
Typescript tips
TypeScript/JavaScript has powerful operators that can make a programmer’s life easier. Here are some tips for making your code cleaner!
Logical AND (&&) operator
is the same thing as
Nullish coalescing operator (??)
It returns its right-hand side operand when its left-hand side operand is
null
orundefined
, and otherwise returns its left-hand side operand. When used properly, it ensures that the variable we are assigning value to is neverundefined
ornull
.Conditional (ternary) operator
isDonut ? 'coffee' : 'milk'
is the same as writingAdditionally click here if the example does not suffice.
===
vs.==
==
converts the variable values to the same type before performing comparison. This is called type coercion.===
does not do any type conversion (coercion) and returns true only if both values and types are identical for the two variables being compared.Note: Gains in performance between these two operations is negligable.
Optional Chaining
Introduced in 2020, the optional chaining operator (?.) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
In the following example we need to first check for
donut.frosting
before we can access theflavor
of the frosting.let nestedProp = donut.frosting && donut.frosting.flavor
However, with optional chaining you don’t have to explicitly test and short-circuit based on the state of donut.frosting before trying to access donut.frosting.flavor:
let nestedProp = donut.frosting?.flavor
Essential VS Code shortcuts
cmd + left click
: navigate to implementation/usagecmd + P
: search by filenamecmd + shift + P
: VS Code commandscmd + F
: search current filecmd + shift + F
: search entire code baseNote, these commands are for Mac. Please replace all
cmd
withctrl
for Windows.Demo
You can find a demo of the MyHypermedia application here.
Note: It is entirely possible that there will be some unexpected behavior in this application as it is not set up to handle concurrency properly. This means that the application might behave weirdly if multiple users are interacting with the demo at the same time.
If there is anything that is unclear in this assignment, or you find a bug in our code base, please let us know in Slack!
Known bugs in TA implementation
Here is a list of known bugs in our implementation. You are not expected to have these bugs fixed in your system. We will update this list if more bugs are found.
Backend
Continuing from the lab, you will be implementing your own
MongoDB
queries.The backend portion is worth 30% of the assignment grade.
Don’t forget to
yarn install
and add your.env
file!To Dos
Your primary objective for the backend assignment is to fill in the stencil code found at
LinkCollectionConnection.ts
andAnchorCollectionConnection.ts
. We hope that writing these files will get you more familiar with creating your own MongoDB queries.Tips:
NodeCollectionConnection.ts
as a close reference for these files.Running Tests
Once finished, you should be able to pass all unit (
mock
) and end-to-end (e2e
) tests forLinks
andAnchors
. Note that yourNodes
tests should still all pass.Jest Commands:
Here are the following commands to run tests on a select group of Jest files:
yarn test <file-name>
yarn test findAnchorById.spec.ts
will runfindAnchorById.spec.ts
yarn test findanchorbyid
works too - it is case insensitiveyarn test <folder-name>
yarn test Anchors/Mock/AnchorCollectionConnection
yarn test Anchors
will run all tests found inAnchors
yarn test e2e
will run alle2e
unit testsThe tests are not setup with Gradescope, they are setup with GitHub Classroom. If all tests pass, there will be a green checkmark in front of your commits.
Custom Methods and Endpoints
Feel free to add your own custom methods in any of the
<x>collectionConnections
, gateways or router files. The stencil only provides methods that we the TA’s need to power our frontend. But if you think that your frontend implementation would benefit from another endpoint or support from the backend, feel free to add your custom code!Frontend
The frontend portion is worth 60% of the assignment grade.
Introduction
In the last assignment you handled passing in
props
to theTextContent
andImageContent
components so that they would render the content. You also implemented theNodeBreadcrumb
component. The changes that you made were specific to single components rather than the overall system. When implementinglinks
andanchors
we need different components to interact with each other, so in this assignment you will get more familiar with the frontend system as a whole.Frontend Roadmap
Gateway
Read and understand the implementation of the frontend
LinkGateway
,AnchorGateway
and lastlyNodeGateway
. You will be using methods from these files in the rest of the assigment. Through these methods we are able to make HTTP requests to the backend. You are also implementingNodeGateway.deleteNode
Links and anchors
Start Link
Complete Link
and design theCompleteLinkModal
NodeLinkMenu
. Ensure the menu is always up-to-date.Folder Structure
Remember, you can search for a file in VSCode using
cmd + P
(Mac) orctrl + P
(Windows). These commands are very useful to navigate between files, which you will have to do in this assignment!Gateways
Now that we are supporting Links and Anchors, our frontend gateways -
AnchorGateway.ts
,LinkGateway.ts
,NodeGateway.ts
- have gotten more complex. In short look at the following methods and implement them:client/src/nodes/NodeGateway.deleteNode()
now deletes all links and orphan anchors associated with the node. Once all links and orphan anchors have been deleted, the node is then deleted.Orphan anchors: Anchors that don’t have any links attached to them
Frontend gateway interacting with backend microservices
We have designed this project such that each of the backend microservices (router, gateway, collectionConnection) do not interact with one another. This means that there is absolutely no cross talk between the link, anchor and node backends.
So how do we delete nodes if the deletion of a node relies on the deletion of anchors and links as well? For the purposes of our assignmments (you can do this differently in your final project) we will be using the frontend gateways to handle the cross talk. For example, this is what happens in the frontend’s
NodeGateway.deleteNode()
method:AnchorRouter
to get anchors by nodeIdLinkRouter
to get links byanchorIds
LinkGateway.deleteLinks()
to delete links and orphan anchorsNodeRouter
to delete nodeYou can make calls to other gateways by importing them, for example in LinkGateway, we already import AnchorGateway so that we can make a call to an AnchorGateway and delete the
orphanAnchors
. You will want to do this when you are implementingNodeGateway.deleteNode()
!TODO: Implement
NodeGateway.deleteNode()
Start Link
andComplete Link
In
Lab 2
you handled selecting an extent on the node that you are currently on. Once you handled the case of selecting the extent, you usedsetSelectedExtent
to set a app state variable that was passed down fromMainView
so it can be accessible to any component in your app. There are a few other state variables that are essential when implementingStart Link
andComplete Link
. They are as follows:App States
boolean
state variable that indicates whether we are linking from a node.Link States
IAnchor
that indicates the particular anchor that we are linking from.Anchor States
Note: To modify an array state variable you should use an updater function to prevent race conditions.
Note: If the Extent is
null
then it should link the entire node. If it isundefined
then it should return an error.In
Lab 2
, wesetSelectedExtent
inImageContent.tsx
andFolderContent.tsx
respectively. Since thesetSelectedExtent
method was passed down fromMainView.tsx
, we are able to access them fromMainView.tsx
anywhere in our codebase!TODO:
handleStartLinkClick
andhandleCompleteLinkClick
inNodeView.tsx
and also ensure that the functionality meets the expecations as defined below.CompleteLinkModal
Functionality Expectations
CompleteLinkModal
.CompleteLinkModal
. If you want you can also change theILink
interface inTypes > ILink
and add additional properties to be added to the database!Title
andExplainer
are expected to be part of this modal.NodeLinkMenu
Now that we have the ability to create links between any two
Extents
on two documents (or within the same document). Your next task is to completely design theNodeLinkMenu
, which is where you render the links for a particular node, and handle the user interactions with that link list!TODO: Design and implement your NodeLinkMenu. You can find this in
src > components > NodeView > NodeLinkMenu
The
NodeLinkMenu
in the demo is only one possible design for this menu. Feel free to relocate it, adjust it, and make it your own, as long as it is usable, well designed, and the following three expectations are clear.Click Events
You will want to use
onClick
when you are designing the NodeLinkMenu component. The following code snippet is an example of how that could look.Note that
e.detail
is how you can specify the difference between left click and right click. The above method would print the type ofsprinkles
on left click, and thedough
on right click.Don’t forget to set the
css cursor
property. This is an important thing to set when it comes to usability with onClick elements!Functionality Expectations
setSelectedAnchors
state variable to do this! But how do we get the anchors on either end of the link? Look at the ILink interface and REMEMBER, we can always make calls to the AnchorGateway.This component should be well designed and usable. We recommend creating a mockup in
Adobe Xd
orFigma
, feel free to check in with a TA to ask for feedback on your design!Deployment
Deployment is worth 10% of the assignment grade.
You have done a great job if you are reaching this step of the assignment! After you completed coding your system, it is time to deploy it to remote servers so everyone with internet can access it. To introduce you to different technologies, we are going to deploy backend to Heroku, and frontend to Google Firebase. Feel free to share the deployment link (not the code) with family and friends, or you may even want to showcase it on your resume!
Deploying Backend
First, we’ll want to deploy our backend service to Heroku. The first step is to register an account and install Heroku CLI. To do that, please follow the link here: Getting Started on Heroku with NodeJS.
Setting up the environment
Now you should have the Heroku environment ready to go. We can start deploying our backend code. Since we have our server and client in one repo, it’s crucial that we only deploy a subfolder of the whole repository.
Creating instance and deploying
heroku create
server
subfolder to Heroku instance:git subtree push --prefix server heroku master
We have deployed our code to Heroku! But now it will not work yet, because we have not configured environment variables and our application does not know what port to start on.
Configuring environment variables
Config Vars
section, and clickReveal Config Vars
.DB_URI
, keeping everything else the same as the image below.Now, if you refresh your backend instance, you should see that “MyHypermedia Backend Service” is rendered on the DOM. Moving on to deploying frontend!
Commonly Asked Questions
Q: What if I already have a Heroku account? Do I need to create a new one for this course?
A: No, you can use your personal account for Heroku. If you run out of your instance limit, consider registering a new account.
Q: The app works locally but Heroku says “Application Error”. What’s wrong?
A: Go to Heroku dashboard and select your instance. Click
More
button and selectView logs
option. You will see the console output and debug from there.Check whether you have configured environmental variables (config vars) correctly.
Note that Heroku does not install
devDependencies
if you have used your own npm package; consider moving it todependencies
inpackage.json
.TODO: Put your backend Heroku URL in the assignment
README.md
Deploying Frontend
After deploying backend, we will be deploying our frontend using Firebase, a Google service. Before we start, we need to change the endpoint in the frontend codebase to point to the backend service we just deployed. Let’s do it!
Where can I find my backend deployment link?
Click the “Open App” button in Heroku instance page, your backend app will open in a new window.
The first step is to change the endpoint in Frontend to point to the remote backend service.
Changing the endpoint
client/src/global/endpoint.ts
endpoint
variable to point to your backend deployment. Do NOT omit the trailing/
in the URL. This will change the end point for all frontend gateways you wrote.For example,
export const endpoint = 'https://vast-caverns-62320.herokuapp.com/'
The second step is to install the Firebase CLI.
Installing Firebase CLI
Follow the instruction here to install Firebase CLI.
Next, let’s log into the Google Firebase using your personal Google Account.
The account you use must not be affliated with Brown University. Use your personal Google Account, or register a new one. Brown University accounts does not allow users to create Firebase apps.
After we log in, it is time to create and configure the Firebase app!
Create and configure Firebase app
client
folder in terminal. This is important!firebase init
Are you ready to proceed?
, enterY
and hit enterHosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
Create a new project
What do you want to use as your public directory?
, enterbuild
.Configure as a single-page app?
, enterN
.Set up automatic builds and deploys with GitHub?
, ennterN
.Now we have created a Firebase instance on the cloud! Since we are deploying our React application, we will use the
production
version of React. How do we do that?Build production React and deploy
client
folder in terminal.yarn build
. This would compile your React to HTML and JS files to a folder calledbuild
. We will deploybuild
folder.firebase deploy
.All done, have fun! If everything runs smoothly, you should be seeing your app deployed on Firebase. Firebase would give you the URL of the app upon successful deployment.
You just deployed a fully-fledged hypertext system. Open your frontend deployment, and test it as a user!
TODO: Put your frontend Firebase URL in the assignment
README.md
Extra Credit
This extra credit is not a capstone or graduate student requirement.
Now that you have implemented anchors and links for basic image and text nodes, it is the time to try to implement your own node types for extra credit. These can be anything ranging from a
PDFNode
,SpreadsheetNode
,AudioNode
,VideoNode
or any other custom node type you want to add. For rendering audio, PDFs, feel free to use any packages of your choice!These expectations are very broad - feel free to reach out to a TA with any questions you have about the specifics of how to implement one of these node types - a great place to start is thinking about what Node modules may be useful…
Creating the new node type/view counts as 10 pts and adding linking (and anchor) functionality to this view adds an extra 10 pts.
Handin
You should submit your project to the Nodes | Assignment 1 assigment in Gradescope via your GitHub repository. You should also have filled out your
README.md
with the following information:Note: We do not run tests through Gradescope, we run them in GitHub classroom.
Grading Breakdown
This component should include a rubric, for the backend and the frontend
Backend Tests - 30pts
AnchorCollectionConnection.ts
LinkCollectionConnection.ts
Frontend Functionality - 60pts
NodeGateway.deleteNode()
should delete node as well as relevant anchors and linksLinking
works as expectedNodeLinkMenu
meets expected functionalityRubric specifications for
Linking
andNodeLinkMenu
:5 pts - partially correct
0 pts - not implemented
5 pts - clicks are not clear, poorly designed and not easily accessible
0 pts - no design consideration, buttons are not functional
Deployment - 10pts
Total = 100 pts
Extra Credit - 20pts
MyHypermedia
app