This document covers Sagefy’s technical architecture. This includes system diagrams, definitions, tools, and high-level decisions. This document does not cover information already in Setup, Technology Stack, User Stories or Cards & Subjects. This document is for new technical contributors and reference.
This document is draft-quality currently. Feel free to suggest edits and improvements!
Sagefy depends on PostgreSQL. Most of Sagefy’s function is completely in PostgreSQL. We use PostgreSQL thoroughly, including: schemas, enums, comments, composed types, functions, triggers, indexes, foreign key relationships, views, common table expressions, recursion, joins, PLPGSQL, notifications, full-text search, constraints, access policies, and row-based access control.
You can view the PostgreSQL specific files in the
postgres/ folder. We use dbmate to maintain database migrations. dbmate automatically generates the latest schema in
schema.sql. Sagefy’s database runs on port 2600. We also use the postgres-json-schema extension for some validations.
A full list of tables and relations is in
The user table is actually two: one that is public information, and one that is private information. This table is only logged in users.
Subjects are in the
subject_version relate to either logged in user or logged out session. Two additional tables,
subject_version_parent_child, are join tables. There is also tables
subject_entity, which Sagefy uses for maintaining foreign key relationships.
Cards are similarily in the
card_version table. A
card_entity table maintains foreign key relationships.
user_subject table stores the intent to learn for users. The table may relate to logged in user or logged out session.
topics are a single table. Topics relate to logged into user or logged out session. Topics belong to subjects and cards.
posts are two tables:
post. Posts relate to logged in user or logged out session. Posts may be post, proposal, or vote. The proposal kind joins with card and subject versions via the
We use dbmate to run migrations. New migrations are with
npm run dbmate new "name", and updates are with
npm run dbmate up.
What makes the Sagefy PostgreSQL database useful with little custom code is Postgraphile. Postgraphile parses the full PostgreSQl schema and turns it into a GraphQL API. Sagefy follows the advice found in their PostgresQL schema design for PostgreSQL design. This service runs on port 2601. The service reads your
.env file, so take care to configure per environment.
We extend Postgraphile and Express here slightly to send emails. We listen to notifications from PostgreSQL, and the service then responds by sending email contents to the Mailgun transactional email service.
You can interact with this service directly on local development at
https://localhost:2601/graphiql. The service is self-documenting.
Tests in this directory check both the PostgreSQL schema for correct function as well as the translation to GraphQL.
pm2 manages the Node.js process.
Sagefy’s web client runs on port 2602. It is a Node.js server with Express. We store a JWT cookie on the client from this service.
The URLs roughly follow a “REST”-like pattern. Most endpoint run a single GraphQL query to the Postgraphile service, format the results slightly, and render HTML with server-side only React.
There is also minimal CSS, using
drab.css and a few small extensions. The CSS can be rebuilt with
npm run prepublish, though the need is rare. There are very few classes, preferring simple HTML tags automatically styled.
There’s a folder of GraphQL queries. They are separate because the
server/ directory uses the actual client queries to run tests. This avoids duplication.
The top level files in the
views/ directory are all pages. A subfolder of
components/ are smaller React views shared between multiple pages.
pm2 manages the Node.js process.
Nginx is the only piece that has direct access to the public internet. So we run Nginx on port 80. In addition to routing traffic to the Node.js client service, we also serve some static files directly with Nginx. We take advantage of Nginx’s rate limiting features.
We monitor the up status of the Sagefy with Freshping.
We run tests on every commit and pull request with CircleCI.
Our test suite runs Jest, Eslint, Supertest, and Joi. Prettier runs with each commit for code formatting.
This is a manual process currently,
sshing into the server,
cd into the sagefy directory, and running