Skip to content
sern

Blog

Deploying sern with Railway

In this guide, I’ll be showing you how to deploy your sern bot with Railway.

This guide assumes you have a sern bot already set up and ready to deploy. If you don’t, you can follow the walkthrough to set up your bot.

GitHub Repository

The first thing you’ll need to deploy your bot is a GitHub repository for your bot. If you don’t have one, you can create one by following the GitHub guide.

Once you have your repository set up, you can push your bot’s code to the repository, and you’re ready to deploy using Railway.

Railway Setup

After you have your bot’s code in a GitHub repository, you’ll need to create an account on Railway. You can sign up using your GitHub account at railway.app.

Once you’ve created your account, navigate to the dashboard and click the “New Project” button. Click the “Deploy from GitHub repo” button, and select your bot’s repository.

Once you’ve selected your repository, click the “Add Variables” button to add your bot’s environment variables. You’ll need your environment variables from your .env file to add to Railway. (You can also add these later if you want.)

Deploy

Once you’ve added your environment variables, click the “Deploy” button, and Railway will start deploying your bot. Once the deployment is complete, you’ll be able to see your bot’s URL and logs.

That’s it! Your bot is now deployed and running on Railway. If you have any issues or questions, feel free to ask in the sern Discord server.

New logo!

Hey everyone! Today we have very special news for you all: We’re changing our logo!

Why?

You see, on today’s standards, having a simple logo is essential. Our logo aligns perfectly with these design principles but it can always be improved.
We were chilling, you know, cooking sern handler v3, sern gui, npm create @sern/bot and serncord when we thought about changing the logo to a sleek design with less colors.
And here we are!

Who did our new branding?

Ropox!

Bro’s the GOAT. This website is maintained by him, the domain costs are funded by him and also he started brainstorming how the logo would be on paper:
paper prototypes

And there it all clicked:
paper logo

seren tried by the way! seren tried

Anyways, here it is:

sern logo

Pretty nice!

By the way, we have animations and variations on the way, so make sure to stay updated in the discord server!

Finally, from the entire sern team, thank you for sticking around ❤️

Release 3.0.0

3.0 Release

Join our discord

Features

Dependency Management

  • Service API (recommended to use this over useContainer hooks)
    • Dependencies type must be globally augmented in order for Services api to function properly
  • new methods on ModuleManager
    • getPublishableCommands()
  • Init Hooks
    • implement starting behavior for dependencies
    • To enforce and type check this, use the Initializable type when making your Dependencies type!
  • Emitter interface
    • More generic interface to define any event emitter
    • You can now swap out the SernEmitter with whatever emitter now.
1
class DatabaseService implements Init {
2
//some hypothetical database
3
_pgsql : database()
4
5
async init() {
6
await _pgsql.load()
7
}
8
9
}
10
11
await makeDependencies({
12
build: root => root.add({
13
db: new DatabaseService() //will be init'ed automatically
14
})
15
})
  • new SernEmitter event modulesLoaded , which allows users to customize behavior after all modules are loaded!
1
export default eventModule({
2
name: "modulesLoaded",
3
type: EventType.Sern,
4
execute: () => {
5
console.log("All modules loaded");
6
},
7
});

Quality of Life

  • faster module loading

    • I utilize async generators for reading files now. A lot faster than the first iteration.
  • better error handling

  • Less boilerplate

    • Services api cleans up v2 boilerplate
  • class modules devex got upgraded and work better than before

  • automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)

    • ignore commands and events with ! prefix on filename or directory (ie: !filename.ts or !directory will be ignored by sern)
  • Service API (recommended to use this over useContainer hooks)

    • Dependencies type must be globally augmented in order for Services api to function properly
  • Less boilerplate

  • new methods on ModuleManager

  • automatically ignore any files not ending in (mts, cts, mjs, cjs, ts, js)

    • ignore commands / events with ! prefix on filename or directory (ie: !filename.ts or !directory)
  • new SernEmitter event modulesLoaded , which allows users to customize behavior after all modules are loaded!

  • Init Hooks

    • implement starting behavior for dependencies

Experimental

  • Experimental things may be subject to removal, need feedback and are not guaranteed stable
  • dev / prod mode
    • sern will behave differently depending on mode set
  • init sern from file option
    • reads from local sern.config.json
1
Sern.init("file");

Breaking changes

  • Sern.makeDependencies -> makeDependencies
    • it is asynchronous and top level function now. Make sure to await it before initing for proper synchronization.
  • module store and manager internally changed, so those using them may recieve breaking changes
  • BaseOptions type removed
Sern.makeDependencies({ build: () => {} })
await makeDependencies({ build: () => {} })

Deprecations

  • Removed all previous marked deprecations in v3
  • ModuleStore will be removed as a dependency in v4. The only way to access modules should be through ModuleManager
  • Default Dependencies will be made internal in the v4. Users should only have access to its interface / contract

Release 2.5.0

2.5 Release

Join our discord

  • Thank you for using sern in your projects. It’s only going to get better!
  • I apologize for the sudden small breaking change. After this update, there won’t be any for a while. Wow! We’re increasing our semantic versioning by +0.3.9
    What does this mean?

Breaking changes

  • controller parameter for plugins has been removed
    • You’ll need to import it instead
    • This breaks old CommandPlugin, but not old EventPlugin

Deprecations

Reason

This will probably be the last breaking change in a while. Thanks for using sern!

Release 2.0.0

2.0 Release

Join our discord!
Wow! We’re finally increasing our semantic versioning by +1.7.9.
What does this mean?

Plugin Support for Event Modules

You can now use plugins for event modules. Previous version would throw an error if the plugins field was populated.

1
export function commandPlTest(): SernEmitterPlugin {
2
return {
3
type: PluginType.Command,
4
execute: ({ mod }, controller) => {
5
if (mod.name === "module.register") {
6
console.log("Event Module created correctly");
7
return controller.next();
8
}
9
console.log("event name is wrong");
10
return controller.stop();
11
},
12
};
13
}

Applying this plugin to some eventModule:

1
export default eventModule({
2
name: "error",
3
type: EventType.Sern,
4
plugins: [commandPlTest()],
5
execute(m) {
6
console.log(m);
7
},
8
});

Dependency Injection and Decoupling

The powerful npm package iti decouples sern even more. Decoupling data structures with the Inversion of Control pattern separates data from logic, which will help speed production and make sern even more customizable than before.

How do I start?

src/index.ts
1
//With typescript, you can customize / augment your typings.
2
interface MyDependencies extends Dependencies {
3
"@sern/client": Singleton<Client>;
4
"@sern/logger": Singleton<DefaultLogging>;
5
}
6
7
export const useContainer = Sern.makeDependencies<MyDependencies>({
8
// exclude: new Set(['@sern/logger']), don't autofill optional dependencies
9
build: (root) =>
10
root
11
.add({ "@sern/client": single(client) })
12
.add({ "@sern/logger": single(new DefaultLogging()) }),
13
});
14
15
Sern.init({
16
defaultPrefix: "!", // removing defaultPrefix will shut down text commands
17
commands: "src/commands",
18
// events: 'src/events' (optional),
19
containerConfig: {
20
get: useContainer, //pass in your dependency getter here
21
},
22
});

Using the Sern#makeDependencies function, inject your dependencies. We’ll use specific dependencies that are created with the @sern/keyword key. Using typescript to display all keywords and what they represent:

1
export interface Dependencies {
2
"@sern/client": Singleton<EventEmitter>; //Discord Client
3
"@sern/logger"?: Singleton<Logging>; //Logger
4
"@sern/emitter": Singleton<SernEmitter>; //SernEmitter
5
"@sern/store": Singleton<ModuleStore>; //Stores all Command Modules
6
"@sern/modules": Singleton<ModuleManager>; //Manages Modules
7
"@sern/errors": Singleton<ErrorHandling>; //A Lifetime / Crash Handler
8
}

At the moment, one optional dependency, @sern/logger, exists. If not added explicitly, we’ll autofill with a DefaultLogger.

Use your generated dependency getter useContainer (use whatever name you want), access them from anywhere.

The build field follows createContainer function call.

New CommandTypes

2.0 includes all the new role select menus. CommandType.MenuSelect has been renamed into CommandType.StringSelect. The remaining SelectMenus are CommandType.RoleSelect, CommandType.ChannelSelect, CommandType.UserSelect, CommandType.MentionableSelect

commands/roleselect.ts
1
export default commandModule({
2
type: CommandType.RoleSelect,
3
execute(ctx) {
4
ctx.reply("role select");
5
},
6
});

In addition, commandModules with ContextMenus have been renamed.

CommandType.MenuUser, CommandType.MenuMsg
CommandType.CtxUser, CommandType.CtxMsg

Typings Simplification

Pre 2.0:


Post 2.0:

CommandPlugin<T> and EventPlugin<T> typings have also been static’ified, transformed from types to interfaces

Breaking Changes


All deprecation warnings from previous versions have taken effect, and are removed in 2.0.
type Module = EventModule | CommandModule
type AnyModule = EventModule | CommandModule
export type SpreadParams<T extends (...args: never) => unknown> = (
args: Parameters<T>[number],
) => unknown;

Override type has been removed due to redundancy

discord.js : 14.5
discord.js : 14.7
typescript: 4.7
typescript: 4.9
interface Wrapper {
readonly defaultPrefix?: string;
readonly commands: string;
readonly events?: string;
readonly containerConfig : {
get: (...keys: (keyof Dependencies)[]) => unknown[];
}
}
interface Wrapper {
readonly client: Client;
readonly sernEmitter?: SernEmitter;
readonly defaultPrefix?: string;
readonly commands: string;
readonly events?:
| string
| { mod: EventModule; absPath: string }[]
| (() => { mod: EventModule; absPath: string }[]);
}
DefaultLogger
DefaultModuleManager
SernEmitter
DefaultErrorHandling
type Singleton<T> = () => T
type Transient<T> = () => () => T;
type LogPayload<T = unknown> = { message: T }
export const single = <T>() => T
export const many = <T>() => () => T

Including the previous section, some names to symbols and data structures were altered to be better represented. view changelog

Context refactoring

The context data structure has been internally altered to represent its dynamics better.