Current Status
No Authentication Implemented : This starter template does not include authentication out of the box. All endpoints are currently public and unprotected.
The Hono OpenAPI Starter is designed as a foundation for building type-safe APIs. Authentication is intentionally not included to allow you to choose the authentication method that best fits your use case.
Why No Authentication?
This starter template focuses on:
Type-safe route definitions with OpenAPI documentation
Database schema management with Drizzle ORM
Request/response validation with Zod
Structured logging with Pino
Testing infrastructure with Vitest
Authentication strategies vary widely between projects (JWT, OAuth, API keys, session-based, etc.), so the template leaves this decision to you.
Adding Authentication
Here are common authentication patterns you can implement with Hono:
Option 1: JWT Authentication
Hono provides a built-in JWT middleware for Bearer token authentication.
Install JWT middleware
The JWT middleware is included in the core Hono package.
Add JWT secret to environment variables
Update src/env.ts to include a JWT secret: const EnvSchema = z . object ({
NODE_ENV: z . string (). default ( "development" ),
PORT: z . coerce . number (). default ( 9999 ),
LOG_LEVEL: z . enum ([ "fatal" , "error" , "warn" , "info" , "debug" , "trace" , "silent" ]),
DATABASE_URL: z . string (). url (),
DATABASE_AUTH_TOKEN: z . string (). optional (),
JWT_SECRET: z . string (). min ( 32 ), // Add this line
});
Add to your .env file: JWT_SECRET = your-secret-key-at-least-32-characters-long
Create authentication middleware
Create src/middleware/auth.ts: import { jwt } from 'hono/jwt' ;
import env from '@/env' ;
export const authMiddleware = jwt ({
secret: env . JWT_SECRET ,
});
Apply middleware to protected routes
Update your route definitions to use the middleware: import { authMiddleware } from '@/middleware/auth' ;
import { createRouter } from '@/lib/create-app' ;
const router = createRouter ()
. use ( '/tasks/*' , authMiddleware ) // Protect all /tasks endpoints
. openapi ( list , handlers . list )
. openapi ( create , handlers . create )
. openapi ( getOne , handlers . getOne )
. openapi ( patch , handlers . patch )
. openapi ( remove , handlers . remove );
Update OpenAPI documentation
Add security definitions to src/lib/configure-open-api.ts: export default function configureOpenAPI ( app : AppOpenAPI ) {
app . doc ( "/doc" , {
openapi: "3.0.0" ,
info: {
version: packageJSON . version ,
title: "Tasks API" ,
},
security: [
{
bearerAuth: [],
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http' ,
scheme: 'bearer' ,
bearerFormat: 'JWT' ,
},
},
},
});
// ... rest of configuration
}
Usage Example:
curl http://localhost:9999/tasks \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Option 2: API Key Authentication
For simpler use cases, you can implement API key authentication:
Create API key middleware
Create src/middleware/api-key.ts: import { createMiddleware } from 'hono/factory' ;
import * as HttpStatusCodes from 'stoker/http-status-codes' ;
import env from '@/env' ;
export const apiKeyMiddleware = createMiddleware ( async ( c , next ) => {
const apiKey = c . req . header ( 'X-API-Key' );
if ( ! apiKey || apiKey !== env . API_KEY ) {
return c . json (
{ message: 'Unauthorized' },
HttpStatusCodes . UNAUTHORIZED
);
}
await next ();
});
Apply to routes
import { apiKeyMiddleware } from '@/middleware/api-key' ;
const router = createRouter ()
. use ( '/tasks/*' , apiKeyMiddleware )
// ... route definitions
Usage Example:
curl http://localhost:9999/tasks \
-H "X-API-Key: your-secret-api-key"
Option 3: Basic Authentication
For internal tools or simple use cases:
Install basic auth middleware
Create basic auth middleware
import { basicAuth } from 'hono/basic-auth' ;
import env from '@/env' ;
export const authMiddleware = basicAuth ({
username: env . BASIC_AUTH_USERNAME ,
password: env . BASIC_AUTH_PASSWORD ,
});
Usage Example:
curl http://localhost:9999/tasks \
-u username:password
Option 4: OAuth 2.0 / Third-Party Auth
For production applications, consider integrating with authentication providers:
Auth0 : Enterprise-grade authentication
Clerk : Developer-first authentication
Supabase Auth : Open-source alternative
Firebase Auth : Google’s authentication service
These providers typically work by validating tokens in middleware and attaching user information to the request context.
Protecting Specific Routes
You can apply authentication selectively:
// Public routes
const publicRouter = createRouter ()
. openapi ( list , handlers . list ) // GET /tasks - public
. openapi ( getOne , handlers . getOne ); // GET /tasks/:id - public
// Protected routes
const protectedRouter = createRouter ()
. use ( '*' , authMiddleware )
. openapi ( create , handlers . create ) // POST /tasks - protected
. openapi ( patch , handlers . patch ) // PATCH /tasks/:id - protected
. openapi ( remove , handlers . remove ); // DELETE /tasks/:id - protected
// Combine routers
const router = createRouter ()
. route ( '/' , publicRouter )
. route ( '/' , protectedRouter );
Adding User Context
Once authentication is implemented, you can add user information to requests:
Extend request context
Update src/lib/types.ts to include user information: export interface AppBindings {
Variables : {
user : {
id : string ;
email : string ;
// ... other user properties
};
};
}
Set user in middleware
export const authMiddleware = createMiddleware ( async ( c , next ) => {
const token = c . req . header ( 'Authorization' )?. replace ( 'Bearer ' , '' );
const user = await verifyToken ( token );
if ( ! user ) {
return c . json ({ message: 'Unauthorized' }, 401 );
}
c . set ( 'user' , user );
await next ();
});
Access user in handlers
export const create : AppRouteHandler < CreateRoute > = async ( c ) => {
const user = c . get ( 'user' );
const task = c . req . valid ( 'json' );
// Associate task with user
const [ inserted ] = await db . insert ( tasks )
. values ({ ... task , userId: user . id })
. returning ();
return c . json ( inserted );
};
Rate Limiting
Even without authentication, consider adding rate limiting to protect your API:
pnpm add @hono/rate-limiter
import { rateLimiter } from '@hono/rate-limiter' ;
const limiter = rateLimiter ({
windowMs: 15 * 60 * 1000 , // 15 minutes
limit: 100 , // Limit each IP to 100 requests per windowMs
standardHeaders: 'draft-6' ,
keyGenerator : ( c ) => c . req . header ( 'x-forwarded-for' ) ?? '' ,
});
app . use ( '*' , limiter );
Security Best Practices
When implementing authentication, follow these security guidelines:
Use HTTPS : Always use HTTPS in production to encrypt tokens in transit
Validate tokens : Verify token signatures and expiration times
Rotate secrets : Regularly rotate JWT secrets and API keys
Implement rate limiting : Protect against brute force attacks
Log authentication events : Track failed login attempts and suspicious activity
Use environment variables : Never hardcode secrets in your source code
Implement refresh tokens : Use short-lived access tokens with refresh tokens for JWT
Add CORS : Configure CORS properly to prevent unauthorized access from browsers
Testing Authenticated Routes
Update your tests to include authentication:
import { testClient } from 'hono/testing' ;
import app from '@/app' ;
describe ( 'Protected Tasks Routes' , () => {
const client = testClient ( app );
const authToken = 'Bearer valid-test-token' ;
it ( 'should require authentication' , async () => {
const res = await client . tasks . $post ({
json: { name: 'Test task' , done: false }
});
expect ( res . status ). toBe ( 401 );
});
it ( 'should create task with valid token' , async () => {
const res = await client . tasks . $post (
{
json: { name: 'Test task' , done: false }
},
{
headers: {
Authorization: authToken
}
}
);
expect ( res . status ). toBe ( 200 );
});
});
Next Steps
Hono Middleware Explore Hono’s built-in authentication middleware
OpenAPI Security Learn how to document security requirements in OpenAPI