Create your first plugin
In this guide, we’ll walk you through the steps of creating your first Faire Auth plugin.
This guide assumes you have setup the basics of Faire Auth and are ready to create your first plugin.
Plan your idea
Before beginning, you must know what plugin you intend to create.
In this guide, we’ll create a birthday plugin to keep track of user birth dates.
Server plugin first
Faire Auth plugins operate as a pair: a server plugin and a client plugin. The server plugin forms the foundation of your authentication system, while the client plugin provides convenient frontend APIs to interact with your server implementation.
You can read more about server/client plugins in our documentation.
Creating the server plugin
Go ahead and find a suitable location to create your birthday plugin folder, with an index.ts file within.
In the index.ts file, we’ll export a function that represents our server plugin.
This will be what we will later add to our plugin list in the auth.ts file.
import type { FaireAuthPlugin } from "faire-auth";
export const birthdayPlugin = () =>
({
id: "birthdayPlugin",
} satisfies FaireAuthPlugin);Although this does nothing, you have technically just made yourself your first plugin, congratulations! 🎉
Defining a schema
In order to save each user’s birthday data, we must create a schema on top of the user model.
By creating a schema here, this also allows Faire Auth’s CLI to generate the schemas required to update your database.
You can learn more about plugin schemas here.
//...
export const birthdayPlugin = () =>
({
id: "birthdayPlugin",
schema: {
user: {
fields: {
birthday: {
type: "date", // string, number, boolean, date
required: true, // if the field should be required on a new record. (default: false)
unique: false, // if the field should be unique. (default: false)
references: null // if the field is a reference to another table. (default: null)
},
},
},
},
} satisfies FaireAuthPlugin);Authorization logic
For this example guide, we’ll set up authentication logic to check and ensure that the user who signs-up is older than 5. But the same concept could be applied for something like verifying users agreeing to the TOS or anything alike.
To do this, we’ll utilize Hooks, which allows us to run code before or after an action is performed.
export const birthdayPlugin = () => ({
//...
// In our case, we want to write authorization logic,
// meaning we want to intercept it `before` hand.
hooks: {
before: [
{
matcher: (context) => /* ... */,
handler: createHook()(async (ctx) => {
//...
}),
},
],
},
} satisfies FaireAuthPlugin)In our case we want to match any requests going to the signup path:
{
matcher: (context) => context.path.startsWith("/sign-up/email"),
//...
}And for our logic, we’ll write the following code to check the if user’s birthday makes them above 5 years old.
import { APIError } from "faire-auth";
import { createHook } from "faire-auth/plugins";{
//...
handler: createHook()(async (ctx) => {
const { birthday } = ctx.body;
if(!(birthday instanceof Date)) {
throw new APIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
}
const today = new Date();
const fiveYearsAgo = new Date(today.setFullYear(today.getFullYear() - 5));
if(birthday >= fiveYearsAgo) {
throw new APIError("BAD_REQUEST", { message: "User must be above 5 years old." });
}
return { context: ctx };
}),
}Authorized! 🔒
We’ve now successfully written code to ensure authorization for users above 5!
Use Your Plugin
Now that the server plugin is ready, the last step is to add it to your auth config and pass the App type to the client. Since the birthday plugin only adds a schema field (no custom client actions), you don’t need a separate client plugin — the App type handles route inference automatically.
Server setup
import { faireAuth, defineOptions } from "faire-auth";
import { birthdayPlugin } from "./birthday-plugin";
const cfg = defineOptions({
baseURL: "http://localhost:3000",
plugins: [
birthdayPlugin(),
]
});
export const auth = faireAuth(cfg);
export const App = auth.$Infer.App(cfg); Client setup
import { createAuthClient } from "faire-auth/client";
import type { App } from "./server";
const authClient = createAuthClient<typeof App>()({}) By passing <typeof App> to createAuthClient, all plugin routes are automatically typed on the client. You only need a separate client plugin if your plugin adds custom actions, atoms, or path method overrides — not for route inference.
Oh yeah, the schemas!
Don’t forget to add your birthday field to your user table model!
Or, use the generate CLI command:
npx @faire-auth/cli@latest generateWrapping Up
Congratulations! You’ve successfully created your first ever Faire Auth plugin. We highly recommend you visit our plugins documentation to learn more information.
If you have a plugin you’d like to share with the community, feel free to let us know through a pull-request.