Last active
December 21, 2022 07:06
-
-
Save jeffijoe/8a88b8f22992b2d56e2bc7b70d0d9333 to your computer and use it in GitHub Desktop.
Awilix example for @garbagemule
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// middleware/auth.js | |
export default async function authenticate (ctx, next) { | |
// Do your auth stuff here. | |
const decoded = extractTokenSomehow(ctx) | |
const teamId = decoded.teamId | |
const userId = decoded.userId | |
// Use the User Repository and Team Repository (or a cache?) to fetch | |
// our entities. | |
// Resolve instances of these using Awilix's cradle proxy. | |
const { userRepository, teamRepository, context } = ctx.container.cradle | |
const [user, team] = await Promise.all([ | |
userRepository.get(userId), | |
teamRepository.get(teamId) | |
]) | |
// Authenticate our context | |
context.authenticate(team, user) | |
return await next() | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// domain/context.js | |
export default class Context { | |
team = null | |
user = null | |
// Easy way to throw if we are not authenticated. | |
check () { | |
if (!this.user) { | |
// Handle this totally transport-agnostic error in a Koa error handler. | |
throw new errors.Unauthenticated() | |
} | |
} | |
authenticate (team, user) { | |
this.team = team | |
this.user = user | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// routes.js | |
import { makeInvoker } from 'awilix-koa' | |
import authenticate from './middleware/auth' | |
function makeAPI ({ userService }) { | |
return { | |
findUsers: (ctx) => | |
// One-liner - just call the appropriate service | |
userService.findUsers(ctx.request.query) | |
} | |
} | |
export default function setupRoutes (router) { | |
const api = makeInvoker(makeAPI) | |
router.get( | |
'/users', | |
authenticate, // authenticate middleware first | |
api('findUsers') // calls the findUsers() function and injects dependencies | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// server.js | |
import Koa from 'koa' | |
import KoaRouter from 'koa-router' | |
import { createContainer, Lifetime } from 'awilix' | |
import { scopePerRequest } from 'awilix-koa' | |
import setupRoutes from './routes' | |
function createServer () { | |
const app = new Koa() | |
const router = new KoaRouter() | |
// Create our DI container. | |
const container = createContainer() | |
// Load modules | |
.loadModules([ | |
['services/*.js', Lifetime.SCOPED], // services are to be "per request" | |
['repositories/*.js', Lifetime.SINGLETON] // repos need no context, so reuse single instances | |
]) | |
// Register our "context" as singleton, too | |
.registerClass({ | |
// Lifetime.SCOPED = one instance *per request* | |
context: [require('./domain/context').default, Lifetime.SCOPED] | |
}) | |
// Add the scopePerRequest middleware, | |
// this is what enables the "per request" instancing. | |
app.use(scopePerRequest(container)) | |
app.use(router.routes()) | |
setupRoutes(router) | |
return app | |
} | |
// Ready to roll | |
createServer().listen(1337, () => console.log('Good to go!')) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// repositories/userRepository.js | |
export default class UserRepository { | |
find (query) { | |
// Do whatever you gotta do! | |
return Promise.resolve([ | |
{ id: 1, name: 'Jeff Hansen' } | |
]) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// services/userService.js | |
import omit from 'lodash/omit' | |
export default function createUserService ({ userRepository, context }) { | |
return { | |
async getAllUsers (query) { | |
context.check() // require authentication | |
query = { | |
...query, | |
// We have a context | |
teamId: context.team.id | |
} | |
const users = await userRepository.find(query) // data store don't care about context | |
if (context.user.isAdmin) { | |
return users | |
} | |
// If we are not an admin, strip out contact info | |
return users.map(user => omit(user, ['email'])) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment