• Stars
    star
    214
  • Rank 184,678 (Top 4 %)
  • Language
    JavaScript
  • Created about 9 years ago
  • Updated about 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🗺 a simple app that demonstrates koa + postgres + other useful abstractions
skeleton

koa-skeleton

Dependency Status

An example Koa application that glues together Koa + Postgres + good defaults + common abstractions that I frequently use to create web applications.

Also used as a test bed for my own libraries.

Originally this project was intended to be forked and modified, but it's grown to the point that it's better left as a demonstration of how one can structure a Koa + Postgres application.

Deploy

The Stack

Depends on Node v8.x+:

  • Micro-framework: Koa 2.x. It's very similar to Express except it supports async/await.
  • Database: Postgres.
  • User-input validation: koa-bouncer.
  • HTML templating: React/JSX. HTML is rendered on the server via React JSX templates.
  • Deployment: Heroku. Keeps things easy while you focus on coding your webapp. Forces you to write your webapp statelessly and horizontally-scalably.

Setup

You must have Postgres installed. I recommend http://postgresapp.com/ for OSX.

createdb koa-skeleton
git clone [email protected]:danneu/koa-skeleton.git
cd koa-skeleton
touch .env
npm install
npm run reset-db
npm run start-dev

> Server is listening on http://localhost:3000...

Create a .env file in the root directory which will let you set environment variables. npm run start-dev will read from it.

Example .env:

DATABASE_URL=postgres://username:password@localhost:5432/my-database
DEBUG=app:*
RECAPTCHA_SITEKEY=''
RECAPTCHA_SITESECRET=''

Configuration (Environment Variables)

koa-skeleton is configured with environment variables.

You can set these by putting them in a .env file at the project root (good for development) or by exporting them in the environment (good for production, like on Heroku).

You can look at src/config.js to view these and their defaults.

Evironment Variable Type Default Description
NODE_ENV String "development" Set to "production" on the production server to enable some optimizations and security checks that are turned off in development for convenience.
PORT Integer 3000 Overriden by Heroku in production.
DATABASE_URL String "postgres://localhost:5432/koa-skeleton" Overriden by Heroku in production if you use its Heroku Postgres addon.
TRUST_PROXY Boolean false Set it to the string "true" to turn it on. Turn it on if you're behind a proxy like Cloudflare which means you can trust the IP address supplied in the X-Forwarded-For header. If so, then ctx.request.ip will use that header if it's set.
HOSTNAME String undefined Set it to your hostname in production to enable basic CSRF protection. i.e. example.com, subdomain.example.com. If set, then any requests not one of `GET
RECAPTCHA_SITEKEY String undefined Must be set to enable the Recaptcha system. https://www.google.com/recaptcha
RECAPTCHA_SITESECRET String undefined Must be set to enable the Recaptcha system. https://www.google.com/recaptcha
MESSAGES_PER_PAGE Integer 10 Determines how many messages to show per page when viewing paginated lists
USERS_PER_PAGE Integer 10 Determines how many users to show per page when viewing paginated lists

Don't access process.env.* directly in the app. Instead, require the src/config.js and access them there.

Features/Demonstrations

  • User authentication (/register, /login). Sessions are backed by the database and persisted on the client with a cookie session_id=<UUID v4>.
  • Role-based user authorization (i.e. the permission system). Core abstraction is basically can(currUser, action, target) -> Boolean, which is implemented as one big switch statement. For example: can(currUser, 'READ_TOPIC', topic). Inspired by ryanb/cancan, though my abstraction is an ultra simplification. My user table often has a role column that's either one of ADMIN | MOD | MEMBER (default) | BANNED.
  • Recaptcha integration. Protect any route with Recaptcha by adding the ensureRecaptcha middleware to the route.
  • Flash message cookie. You often want to display simple messages like "Topic created successfully" to the user, but you want the message to last just one request and survive any redirects.
  • Relative human-friendly timestamps like 'Created 4 hours ago' that are updated live via Javascript as the user stays on the page. I accomplish this with the timeago jQuery plugin.
  • Comes with Bootstrap v3.x. I start almost every project with Bootstrap so that I can focus on the back-end code and have a decent looking front-end with minimal effort.
  • npm run reset-db. During early development, I like to have a reset-db command that I can spam that will delete the schema, recreate it, and insert any sample data I put in a seeds.sql file.
  • Ratelimit middleware. An IP address can only insert a message every X seconds.

Philosophy/Opinions

  • It's better to write explicit glue code between small libraries than credentializing in larger libraries/frameworks that try to do everything for you. When you return to a project in eight months, it's generally easier to catch up by reading explicit glue code then library idiosyncrasies. Similarly, it's easier to catch up by reading SQL strings than your clever ORM backflips.
  • Just write SQL. When you need more complex/composable queries (like a /search endpoint with various filter options), consider using a SQL query building library like knex.js.
  • Use whichever Javascript features that are supported by the lastest stable version of Node. I don't think Babel compilation and the resulting idiosyncrasies are worth the build step.

Conventions

  • Aside from validation, never access query/body/url params via the Koa default like ctx.request.body.username. Instead, use koa-bouncer to move these to the ctx.vals object and access them there. This forces you to self-document what params you expect at the top of your route and prevents the case where you forget to validate params.

    router.post('/users', async (ctx, next) => {
        // Validation
    
        ctx
            .validateBody('uname')
            .isString('Username required')
            .trim()
            .isLength(3, 15, 'Username must be 3-15 chars')
        ctx
            .validateBody('email')
            .optional()
            .isString()
            .trim()
            .isEmail()
        ctx
            .validateBody('password1')
            .isString('Password required')
            .isLength(6, 100, 'Password must be 6-100 chars')
        ctx
            .validateBody('password2')
            .isString('Password confirmation required')
            .eq(ctx.vals.password1, 'Passwords must match')
    
        // Validation passed. Access the above params via `ctx.vals` for
        // the remainder of the route to ensure you're getting the validated
        // version.
    
        const user = await db.insertUser(
            ctx.vals.uname,
            ctx.vals.password1,
            ctx.vals.email
        )
    
        ctx.redirect(`/users/${user.uname}`)
    })

Changelog

The following version numbers are meaningless.

  • 4.1.0 20 Apr 2018
    • Replaced bcrypt with scrypt.
    • Made various small schema changes.
  • 4.0.0 26 Nov 2017
    • Replace Pug with React/JSX for HTML templating.
  • 3.1.0 14 Nov 2017
    • Replace Nunjucks with Pug for HTML templating.
  • 3.0.0 25 Apr 2017
    • Removed Babel since the features we want are now supported by Node 7.x.
  • 2.0.0 29 Oct 2015
    • Refactored from Koa 1.x to Koa 2.x.
  • 0.1.0 29 Oct 2016
    • koa-skeleton is a year old, but I just started versioning it started at v0.1.0.
    • Extracted src/db/util.js into pg-extra npm module. Now util.js just exports the connection pool for other modules to use.

License

MIT

More Repositories

1

darkstrap

(unmaintained, dead) a dark theme for twitter bootstrap 2
CSS
266
star
2

koa-bouncer

⛔ an http param validator for koa apps
JavaScript
62
star
3

kog

🌶 A simple Kotlin web framework inspired by Clojure's Ring.
Kotlin
43
star
4

telegram-chatgpt-bot

a Telegram ChatGPT bot that supports text prompts and two-way voice memos
TypeScript
35
star
5

elm-chrome-extension

a chrome extension composed of three elm apps that synchronize a single model
JavaScript
32
star
6

chaingun

⛓️ a fledgling bitcoin implementation written in clojure
Clojure
25
star
7

craig

(Broken, Not maintained) A Craigslist listings scraper.
Ruby
25
star
8

generator-elm

a yeoman generator for elm + webpack project boilerplate
JavaScript
25
star
9

onetab-to-pinboard

upload OneTab url dumps to Pinboard
Clojure
24
star
10

guild

a node+koa+postgres forum currently in production
HTML
22
star
11

symposium

(old and noobie) a forum system built with rails
Ruby
18
star
12

elm-hex-grid

a hex-grid library for elm
Elm
17
star
13

clj-forum

a forum built with Datomic and Clojure
Clojure
15
star
14

hyclops

Clojurey functions for Hy (a lisp on top of Python)
Hy
15
star
15

kotlin-result

a simple result monad for kotlin
Kotlin
14
star
16

pg-extra

🐘 some helpful extensions for node-postgres
JavaScript
12
star
17

icejaw

❄️ generate a static website from a dynamic one
JavaScript
12
star
18

grinch

a simple blog made with rails
Ruby
11
star
19

captain-githook

(practice) a modest seafaring git POST hook server for auto-updating your git repos abroad
Clojure
11
star
20

garden-compass

a port of Compass to Clojure datastructures for use with Garden
Clojure
9
star
21

kobx

🔮 mobx + react bindings for kotlin-javascript
JavaScript
9
star
22

kotlin-json-combinator

json decode/encode combinators for kotlin
Kotlin
9
star
23

node-fastimage

Quickly determine an image's type, width, and height with only the first response packet.
CoffeeScript
9
star
24

Macflix.app

🍿a simple webview wrapper around netflix for macOS
Swift
8
star
25

node-forum

(old, practice, noob) the start of a simple forum using node.js, express, and mongodb.
JavaScript
7
star
26

elm-mmo

the client + server of a MUD-like multiplayer game over websockets
Elm
7
star
27

klobbdown

a Clojure Markdown implementation expressed with a context-free grammar
Clojure
7
star
28

koa-example

(old, look at koa-skeleton instead) a simple json api server using koa and postgres
JavaScript
7
star
29

p2-space-arena

another iteration towards a multiplayer game, this time with JS + P2 (physics) + Pixi
JavaScript
6
star
30

village

a tiny, weekendware, incremental game hacked together in Elm
Elm
5
star
31

react-template-render

use jsx as your server-side html templating library
JavaScript
5
star
32

elm-speed-reader

A simple Spreeder.com-inspired speed reader
Elm
5
star
33

cloudflare-ip

(Don't use this) Check if an IP address is in Cloudflare range
JavaScript
5
star
34

MediaWiki-DarkMode

Less
5
star
35

elm-ordered-dict

Insertion-ordered dictionaries for Elm
Elm
5
star
36

discourse_pervasive_banner

(Outdated - Do not use) A Hello World plugin example for Discourse.
Ruby
5
star
37

interval-cache

A simple Node library for caching stuff in memory at intervals in the background
JavaScript
4
star
38

linode-api

(practice) a linode api wrapper for clojure
Clojure
4
star
39

pug-render

an improved render() function for pug templates
JavaScript
4
star
40

elm-tile-editor

a tilemap editor built with elm
Elm
4
star
41

infinite-monkey-incremental

An incremental game inspired by the Infinite Monkey Theorem
Elm
4
star
42

generator-choo-webpack

the minimal choo + webpack yeoman project generator
JavaScript
3
star
43

sortedset

a simple sorted set implementation that allows a custom compare function
JavaScript
3
star
44

klobb

A small, experimental Node.js server abstraction focused on immutability and middleware
JavaScript
3
star
45

html-parser

a lenient html5 parser written in Elm
Elm
3
star
46

hansel

simple immutable swift web-server
Swift
3
star
47

node-domainatrix

(Practice, do not use) extends node's url parser with url canonicalizing and breaking apart its public suffix. ported to js from ruby (pauldix/domainatrix).
CoffeeScript
3
star
48

elm-space-arena

(practice) 2D spaceship shooter build with Elm
Elm
2
star
49

speed-reader

(practice) a simple clojurescript speed-reading app
Clojure
2
star
50

instagram-api

(practice) a restful instragram client for clojure.
Clojure
2
star
51

danneu

my Jekyll-inspired danneu.com blog built with Clojure
CSS
2
star
52

kotlin-postgres

an experiment in generalizing database record/json deserializers
Kotlin
2
star
53

blocky

a content blocker for Safari on macOS
2
star
54

schnorr-minimal

secp256k1 implementation without dependencies for node and browser
TypeScript
1
star
55

reddit

👽 a reddit crawler for kotlin
Kotlin
1
star
56

klobb-danneu

my blog made compatible with the Klobb blog engine.
CSS
1
star
57

bulletin

communities as a service
JavaScript
1
star
58

old.danneu.github.com

my (old, replaced) jekyll blog
CSS
1
star
59

space-arena

(practice)
JavaScript
1
star
60

DatomicClojure.docset

(outdated) an unofficial Datomic docset for Dash.app
CSS
1
star
61

crash-landing

a simple idle game
Elm
1
star
62

my_dotfiles

a collection of my .dotfiles
Vim Script
1
star
63

elm-chip8

a chip-8 emulator built with elm
Elm
1
star
64

multima

a modest Clojure MUD
Clojure
1
star
65

recaptcha-validator

a tiny library for verifying recaptcha requests against google's api
JavaScript
1
star
66

bbcode_parser

BBCode parser → HTML transformer
Ruby
1
star
67

spellchecker

A Ruby spellchecker that corrects words based off the word-frequency of text that you feed it.
Ruby
1
star
68

arduino-ereader

a wip low-memory ereader impl for arduino in C
C
1
star