I like to dab into a lot of different languages and frameworks to see how the grass is on the other side and when trying out a new framework for the first time I usually make a simple CRUD app with authentication like a todo MVC or a live chat. I figured it'd help me keep things organized if I stocked all of those examples within a single repository so here it is.
The client directory stores my fronted app. Made in Vue, purposely very basic because the point of this repo is to test different backend solutions so I don't really care what the two forms I have look like.
The APIs connect to a PostgreSQL Database. The connection preferably is abstracted to an ORM that handles database management through a CLI (create, migrate, seed, drop) and database queries.
-
Task
-
The
Task
model belongs to auser
record -
It has a required
title
-
It has a
completed
status that isfalse
by default
-
-
User
-
The
User
model has manytasks
records -
It has a required
email
which matches the pattern/\A[^@\s]+@[^@\s]+\.[a-z]{2,}\z/
-
It has a required
password
which is encrypted before being saved to the database and is hidden from logs
For example
#!/usr/bin/env ruby puts User.first # => #<User id: 1 email: "user@example.com", password: [FILTERED]> puts User.first.to_json # => { id: 1, email: "user@example.com" }
-
It can be created with a
password_confirmation
that matches thepassword
-
It has a
jti
that is automatically assigned on creation, is reset when signing in and out and is hidden from logs (seepassword
example) -
It is authenticable with the
email
andpassword
-
It is authenticable with a JWT token
-
-
/tasks
-
GET /tasks
requires aAuthorization
header with auser
's JWT and renders itstasks
records as json and astatus :ok
-
POST /tasks
requires aAuthorization
header with auser
's JWT and creates a newTask
record for that user
If thetask
is persisted then it renders thetask
as json and astatus: :created
Else it renders an array of error messages and astatus: :unprocessable_entity
-
DELETE /tasks/:id
requires aAuthorization
header with auser
's JWT, deletes theTask
record and renders astatus: :ok
-
PATCH /tasks/:id/complete
aAuthorization
header with auser
's JWT, updates theTask
record'scompleted
status totrue
and renders thatTask
as json and astatus: :ok
-
-
/users
-
POST /users
creates a newUser
record
If theuser
is persisted then it renders theuser
as json with astatus: :created
and aAuthorization
header with theuser
's JWT
Else it renders an array of error messages and astatus: :unprocessable_entity
-
POST /users/sign_in
finds aUser
from the givenemail
andpassword
If theuser
exists then it updates theuser
'sjti
, renders theuser
as json with astatus: :ok
and aAuthorization
header with theuser
's JWT
Else it renders an array of error messages and astatus: :unprocessable_entity
-
DELETE /users/sign_out
requires aAuthorization
header with auser
's JWT, updates theuser
'sjti
and renders astatus: :ok
-
-
/current_user
GET /current_user
requires aAuthorization
header with auser
's JWT and renders theuser
as json with astatus: :created
and aAuthorization
header with theuser
's JWT
A testing environment is setup and connected to a testing database. The tests are transactional, meaning any changes to the database done inside an example is rollbacked after each test in order to isolate each test example. They are also randomised, meaning tests are run in a random order at each itterations.
The tests also include a fixture system to quickly generate record instances.
The coverage for test suite is measured and as close as possible to 100%
Each APIs have their own formatter/linter as well as a CLI task runner. They also have Github actions for specs, formatting and performance if available.
- Express (Work in progress)