Published on 2025-03-08
Creating a Procedure
Published on
Procedures
Procedures in Igniter are powerful middleware functions that allow you to implement cross-cutting concerns like authentication, validation, error handling, and logging. They can be applied to individual controller actions or globally to your router.
Creating a Procedure
To create a procedure in Igniter, you use the igniter.procedure()
function. Here's a basic example:
// src/procedures/auth.procedure.ts
import { igniter } from "@/igniter";
import { verifyToken } from "@/utils/jwt";
export const auth = igniter.procedure({
handler: async (input, ctx) => {
// Get the authorization header
const authHeader = ctx.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return ctx.response.unauthorized(
"Missing or invalid authorization token",
);
}
const token = authHeader.split(" ")[1];
try {
// Verify the token
const user = await verifyToken(token);
// Add the user to the context
ctx.user = user;
// Continue to the next middleware or handler
return ctx.next();
} catch (error) {
return ctx.response.unauthorized("Invalid token");
}
},
});
The procedure configuration includes:
handler
: A function that receives the input and context, and returns a response or callsctx.next()
to continue to the next middleware or handler
Using Procedures with Controllers
Procedures can be applied to individual controller actions:
// src/features/user/controllers/user.controller.ts
import { igniter } from "@/igniter";
import { auth } from "@/procedures/auth.procedure";
export const userController = igniter.controller({
path: "/users",
actions: {
list: igniter.query({
path: "/",
use: [auth()], // Apply the auth procedure
handler: async (ctx) => {
// This handler will only be called if the auth procedure passes
const users = await ctx.providers.database.user.findMany();
return ctx.response.ok(users);
},
}),
},
});
Using Procedures with Routers (Coming Soon)
Note: This feature is currently in development and will be available in a future release.
In the future, procedures will be able to be applied globally to your router:
// src/igniter.router.ts
import { igniter } from "@/igniter";
import { userController } from "@/features/user/controllers/user.controller";
import { logger } from "@/procedures/logger.procedure";
import { errorHandler } from "@/procedures/error-handler.procedure";
export const AppRouter = igniter.router({
baseURL: "https://localhost:3000",
basePATH: "/api/v1",
controllers: {
users: userController,
},
use: [
logger(), // Applied to all requests (coming soon)
errorHandler(), // Applied to all requests (coming soon)
],
});
Common Procedure Patterns
Note: Igniter.js has built-in support for input validation in mutations and queries. The examples below focus on other common use cases for procedures.
Error Handling
// src/procedures/error-handler.procedure.ts
import { igniter } from "@/igniter";
export const errorHandler = igniter.procedure({
handler: async (_, ctx) => {
try {
// Continue to the next middleware or handler
return await ctx.next();
} catch (error) {
// Log the error
console.error("API Error:", error);
// Return an appropriate error response
if (error.name === "ValidationError") {
return ctx.response.badRequest(error.message);
}
if (error.name === "NotFoundError") {
return ctx.response.notFound(error.message);
}
// Default error response
return ctx.response.internalServerError("An unexpected error occurred");
}
},
});
Logging
// src/procedures/logger.procedure.ts
import { igniter } from "@/igniter";
export const logger = igniter.procedure({
handler: async (_, ctx) => {
const start = Date.now();
// Log the request
console.log(`${ctx.method} ${ctx.path} - Request received`);
// Continue to the next middleware or handler
const response = await ctx.next();
// Log the response
const duration = Date.now() - start;
console.log(
`${ctx.method} ${ctx.path} - Response sent (${response.status}) in ${duration}ms`,
);
return response;
},
});
Chaining Procedures
Procedures can be chained together to create a pipeline of middleware:
export const userController = igniter.controller({
path: "/users",
actions: {
create: igniter.mutation({
path: "/",
method: "POST",
use: [
logger(), // First procedure in the chain
auth(), // Second procedure in the chain
],
handler: async (ctx) => {
// Handler is only called if all procedures pass
},
}),
},
});
Best Practices
-
Keep Procedures Focused: Each procedure should focus on a single concern.
-
Use Procedures for Cross-Cutting Concerns: Use procedures for authentication, validation, logging, and other cross-cutting concerns.
-
Chain Procedures in a Logical Order: Order your procedures logically, with general-purpose procedures (like logging) first and more specific ones (like validation) later.
-
Handle Errors Gracefully: Implement proper error handling in your procedures to provide meaningful error messages to clients.
-
Share Context Between Procedures: Use the context object to share data between procedures and handlers.
-
Make Procedures Reusable: Design your procedures to be reusable across different controllers and actions.
Quick Tip
Always implement proper error handling and reconnection logic in your SSE clients to ensure a robust user experience.