JavaScript Devs, We Hear You
Every year, the State of JavaScript survey asks developers what's broken. Every year, the same pain points rise to the top.
The answer isn't more tools. It's cohesion.
We've stared at lists like this for a decade. And here's what we've realized, most of these aren't tooling problems. They're cohesion problems.
The JavaScript ecosystem has world-class tools. Best-in-class ORMs. Blazing-fast routers. Type-safe validators. Flexible auth libraries. The problem is, they were never designed to work together.
It's like IKEA furniture from five different collections. Every piece is beautifully engineered. But nothing fits. You spend hours shimming, adapting, writing glue code. And the result still creaks.
Some frameworks got type safety right, but left out features. Some have features, but feel like enterprise ceremony. There are excellent auth libraries, but they need excessive plumbing.
Type safety is easy when you own the entire stack.
When one team designs the router, the validator, the ORM, the mailer, the test client, they can make sure types flow through all of them. No adapters. No type gymnastics. No as unknown as Whatever
That's the advantage of cohesion. AdonisJS doesn't just support TypeScript, it was rewritten from the ground up in 2020 with types at the core of every design decision.
Access a variable that doesn't exist or misspell the key, and TypeScript catches it before you run the app.
Define your validation schema once. The validated output is automatically typed to match, with no manual interface definitions.
Events are registered with their payload types. Emit an event that doesn't exist or pass the wrong shape, and you get a compile error.
Redirect to a named route. If the route doesn't exist or you pass the wrong parameters, TypeScript tells you immediately.
Test your endpoints with a typed HTTP client. Request bodies, responses, and status codes are all checked at compile time.
Define how your models transform to JSON. The serialized shape becomes a type you can use on the frontend.
Frontend components that reference your backend routes by name. Refactor a route, and every broken link surfaces as a type error.
Generate a fully typed client from your routes. Plug it into TanStack Query, SWR, or use it directly. End-to-end type safety.
Authentication without the plumbing.
Authentication isn't hard. We know how to hash passwords, manage sessions, issue tokens, and protect routes.
What's hard is the wiring. You pick an auth library. It needs an adapter for your ORM. The session store needs to work with your HTTP framework. The user object shape doesn't match your model. You write glue code. Then more glue code. A week passes.
In a cohesive system, this disappears.
npm i @adonisjs/auth
node ace configure @adonisjs/auth
# ❯ Select authentication guard
# › Session (for web apps)
# Access Tokens (for APIs)
#
# ❯ Select user provider
# › Lucid (database)
#
# ✓ Created config/auth.ts
# ✓ Updated app/models/user.ts
# ✓ Registered auth middleware
import User from '#models/user'
import { loginValidator } from '#validators/auth'
import { HttpContext } from '@adonisjs/core/http'
export default class SessionController {
async store({ request, auth, response }: HttpContext) {
const { email, password } = await request.validateUsing(loginValidator)
const user = await User.verifyCredentials(email, password)
await auth.use('web').login(user)
return response.redirect('/dashboard')
}
async show({ auth }: HttpContext) {
const user = auth.getUserOrFail()
const posts = await user.related('posts').query()
return { user, posts }
}
}
import User from '#models/user'
import { loginValidator } from '#validators/auth'
import { HttpContext } from '@adonisjs/core/http'
export default class TokensController {
async store({ request }: HttpContext) {
const { email, password } = await request.validateUsing(loginValidator)
const user = await User.verifyCredentials(email, password)
const token = await User.accessTokens.create(user)
return {
type: 'bearer',
token: token.value!.release(),
}
}
async show({ auth }: HttpContext) {
const user = auth.getUserOrFail()
return { user }
}
}
import { middleware } from '#start/kernel'
import { controllers } from '#generated/controllers'
import router from '@adonisjs/core/services/router'
router
.group(() => {
router.resource('posts', controllers.posts)
})
.use(middleware.auth({ guards: ['web'] }))
router
.group(() => {
router.resource('posts', controllers.api.posts)
})
.prefix('api')
.use(middleware.auth({ guards: ['api'] }))
import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
export default class GithubController {
async redirect({ ally }: HttpContext) {
return ally.use('github').redirect()
}
async callback({ ally, auth, response }: HttpContext) {
const gh = ally.use('github')
const ghUser = await gh.user()
const user = await User.firstOrCreate(
{ email: ghUser.email },
{ name: ghUser.name, avatar: ghUser.avatarUrl }
)
await auth.use('web').login(user)
return response.redirect('/dashboard')
}
}
Simple code that stays simple.
Structure shouldn't require ceremony. You don't need decorators on every method, module registration files, or an IoC container on your face just to wire up a controller.
AdonisJS code reads as simple as Hono or Express, but with the structure and features of a full-stack framework.
The ecosystem around the framework.
A framework is only as good as what surrounds it. Documentation you can actually learn from. Tooling that doesn't fight you. A community that's been building with it for years.
Guides, tutorials, and explanations that go beyond API references. The "why" alongside the "how."
Over 400 free video tutorials covering everything from first steps to advanced patterns. A whole learning platform dedicated to AdonisJS.
No dual builds or CommonJS compatibility hacks. We went all-in on ES modules in 2020.
Hot module reloading for your backend, a CLI that scaffolds and manages your app, and a first-class VS Code extension.
Auth, ORM, validation, mail, uploads, queues, OTEL, and testing are all first-party packages. Plus a growing collection from the community.
Let's do something different today.
You've assembled the stack before. You've read the integration guides, wired the adapters, written the glue code. You know how that story ends.
This time, try cohesion.