Skip to content

Commit

Permalink
Koa route helper & example (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmarshall authored Oct 11, 2022
1 parent 0c96a85 commit 027902f
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 11 deletions.
2 changes: 1 addition & 1 deletion examples/express/routes/users/[id]/likes/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports.get = (req, res) => {
res.send({ userId: req.params.id, likes: ['a', 'b', 'c' ]})
res.send({ userId: req.params.id, likes: ['a', 'b', 'c' ] })
}
14 changes: 14 additions & 0 deletions examples/koa/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const Koa = require('koa')
const soda = require('soda')
const app = new Koa()

async function startup() {
const sodaRouter = await soda.withKoaRouter('./routes')
app
.use(sodaRouter.routes())
.use(sodaRouter.allowedMethods())

app.listen(5555)
console.log('Server listening on :5555')
}
startup()
17 changes: 17 additions & 0 deletions examples/koa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "soda-example-koa",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"repository": "tmarshall/soda",
"dependencies": {
"@koa/router": "^12.0.0",
"koa": "^2.13.4",
"soda": "latest"
}
}
6 changes: 6 additions & 0 deletions examples/koa/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports.get = (ctx, next) => {
ctx.body = {
success: true,
page: 'root',
}
}
5 changes: 5 additions & 0 deletions examples/koa/routes/users/[id]/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports.get = (ctx, next) => {
ctx.body = {
userId: parseInt(ctx.params.id, 10),
}
}
3 changes: 3 additions & 0 deletions examples/koa/routes/users/[id]/likes/[likeId].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports.get = (ctx, next) => {
ctx.body = { userId: ctx.params.id, likeId: ctx.params.likeId }
}
3 changes: 3 additions & 0 deletions examples/koa/routes/users/[id]/likes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports.get = (ctx, next) => {
ctx.body = { userId: ctx.params.id, likes: ['a', 'b', 'c' ] }
}
14 changes: 14 additions & 0 deletions examples/koa/routes/users/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const mockQuery = () => new Promise((resolve) => resolve([{
id: 1,
name: 'Tim',
}, {
id: 2,
name: 'Cosmo'
}]))

module.exports.get = async (ctx, next) => {
const users = await mockQuery()
ctx.body = {
users,
}
}
28 changes: 28 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,31 @@ startup()
### Routes in Express

Any route filepath like `routes/accounts/[accountId].js` will get converted to an Express route path like `/routes/accounts/:accountId`.

## Koa middleware

Want to use [Koa](https://koajs.com/)? No problem.

```js
const Koa = require('koa')
const soda = require('soda')
const app = new Koa()

async function startup() {
// `withKoaRouter` takes the same arguments as `soda.serve`
const sodaRouter = await soda.withKoaRouter('./routes')
app
.use(sodaRouter.routes())
.use(sodaRouter.allowedMethods())

app.listen(5555)
console.log('Server listening on :5555')
}
startup()
```

### Routes in Koa

`withKoaRouter` uses [the `@koa/router` package](https://www.npmjs.com/package/koa-router). The return value is an instance of the Koa router.

Any route filepath like `routes/accounts/[accountId].js` will get converted to a Koa route path like `/routes/accounts/:accountId`.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import serve from './serve'
import walkRoutes from './walkRoutes'
import walkMiddleware from './walkMiddleware'
import withExpress from './withExpress'
import withKoaRouter from './withKoaRouter'

export interface SodaRequest extends IncomingMessage {
params: Record<string, string | number>
Expand Down Expand Up @@ -34,4 +35,5 @@ module.exports = {
serve,
walkRoutes,
withExpress,
withKoaRouter,
}
11 changes: 1 addition & 10 deletions src/withExpress.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { DefineRoute, RouteVerbKey } from './walkRoutes'

import walkRoutes, { ParamTypes } from './walkRoutes'
import walkRoutes from './walkRoutes'
import walkMiddleware from './walkMiddleware'

interface RouteDefinition {
Expand All @@ -9,15 +9,6 @@ interface RouteDefinition {
func: Function
}

const typedParamAttributes = {
[ParamTypes.string]: {
mutator: (paramString: string) => paramString,
},
[ParamTypes.number]: {
mutator: (paramString: string) => Number(paramString)
},
}

const defineRoute: DefineRoute<RouteDefinition> = ({ verb, routePath, func }) => {
const pathParamCheck = new RegExp(`/\\[(?<paramName>[a-zA-Z_$][a-zA-Z0-9_$]*)\\](?=/|$)`, 'g')

Expand Down
56 changes: 56 additions & 0 deletions src/withKoaRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { DefineRoute, RouteVerbKey } from './walkRoutes'

import walkRoutes from './walkRoutes'
import walkMiddleware from './walkMiddleware'

interface RouteDefinition {
verb: RouteVerbKey
path: string
func: Function
}

const defineRoute: DefineRoute<RouteDefinition> = ({ verb, routePath, func }) => {
const pathParamCheck = new RegExp(`/\\[(?<paramName>[a-zA-Z_$][a-zA-Z0-9_$]*)\\](?=/|$)`, 'g')

if (!pathParamCheck.test(routePath)) {
return {
verb,
path: routePath,
func,
}
}

const parts = routePath.split(pathParamCheck)
let resultExpressPath = ''

for (let i = 0; i < parts.length; i++) {
if (i % 2 === 0) {
resultExpressPath += parts[i]
continue
}

const paramName = parts[i]
resultExpressPath += `/:${paramName}`
}

return {
verb,
path: resultExpressPath,
func,
}
}

export default async function withKoaRouter(routesDirpath?: string, middlewareDirpath?: string) {
const Router = require('@koa/router')

const middleware = await walkMiddleware(middlewareDirpath)
const routes = await walkRoutes<RouteDefinition>(routesDirpath, middleware, defineRoute)

const koaRouter = new Router()

for (let routeDef of routes) {
koaRouter[routeDef.verb](routeDef.path, routeDef.func)
}

return koaRouter
}

0 comments on commit 027902f

Please sign in to comment.