Vercel for your backend
Bouncer gets your API from localhost to production in minutes, not months.
$ What's your project called?Acme APIWhere's your API hosted?http://localhost:8080[ ] Auth (Bearer)?Y[ ] Rate limiting (Sliding Window)?Y[ ] Monetization (Stripe)?NSetup complete! Created./bouncer.yaml[INFO] listening on http://localhost:8080
Batteries included without the lock-in.
Keep everything in your database and don't change your stack. Bouncer works alongside your API, not against it while providing sensible defaults for:
- Authentication
- Authorization
- Rate limiting
- Caching
To infinity and beyond.
Bouncer's completely open-sourced under the MIT license. It's also written in Rust and built on top of the performant Axum framework. The included policies are carefully written to adhere to best practices and industry standards.
Did we mention it's easily extensible if you need custom behavior?
Any language, framework, or database.
Bouncer proxies requests to your API after handling the boilerplate for you. That means it works with everything that uses http.
Delete code. Keep your workflow. Improve transparency.
bouncer.yaml serves as a single source of truth for your API's behavior, and it lives in your codebase. That means everything's natively tracked in Git with easily digestable diffs.
There's even support for your existing PR, code review, and CI/CD pipelines right out of the box. You can ditch hundreds of lines of handwritten or stitched-together middleware for a centralized solution without giving up control of your data.
export const bearerAuth = async (req: Request, res: Response, next: NextFunction) => { try { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { // [!code highlight] return res.status(401).json({ // [!code highlight] error: 'Unauthorized', // [!code highlight] message: 'Missing or invalid Authorization header' // [!code highlight] }); // [!code highlight] } const token = authHeader.split(' ')[1]; const hashedToken = hash(process.env.SALT + token); // [!code highlight] const key = await db.query.keys.findFirst({ // [!code highlight] where: eq(users.hashedToken, hashedToken), // [!code highlight] }); // [!code highlight] return { userId: key?.userId, role: key?.role, }; const user = await validateToken(token); if (!user) { return res.status(401).json({ // [!code highlight] error: 'Unauthorized', // [!code highlight] message: 'Invalid token' // [!code highlight] }); // [!code highlight] } req.user = user; next(); } catch (error) { console.error('Auth middleware error:', error); return res.status(500).json({ error: 'Internal Server Error', message: 'Authentication failed' }); } };
bouncer_version: 0.1.* server: bind_address: 127.0.0.1 port: 8000 destination_address: "ENV.DESTINATION_URL" databases: mysql: connection_url: "ENV.MYSQL_URL" max_connections: 5 connection_timeout: 5 "@bouncer/auth/bearer/v1": // [!code focus] db_provider: "mysql" token_validation_query: "SELECT access_level as role FROM users WHERE clerkUuid = ?" realm: "api"
It's time to build your backend better with Bouncer.