I am using ElysiaJS to create an API. The code is in an open-source repo here.
I have three files auth.routes.ts, auth.handlers.ts and auth.dto.ts. The routes files has the path, the validation object and the handler function. The validator object is exported from the auth.dto.ts (data transfer object). The handler function which has all the logic and queries is exported from the auth.handler.ts.
auth.routes.ts
const authRoutes = new Elysia({ prefix: '/auth' })
/** Injecting Database Connection */
.use(databaseConfig)
/** Register User with Email and Password */
.post('/register', registerWithEmailAndPassword, registerWithEmailAndPasswordDTO)
export { authRoutes }
Here the databaseConfig is an Elysia Plugin which has the connection code for the database.
auth.dto.ts
import { t } from "elysia"
export const registerWithEmailAndPasswordDTO = {
body: t.Object({
email: t.String({ format: 'email' }),
password: t.String({ minLength: 6 }),
})
}
auth.handlers.ts
/** Importing Schema */
import { type Context } from "elysia"
import Schema from "../../schema"
import { hashPassword } from "../../utils/auth.utils"
/** Destructuring Schema */
const { users } = Schema
export const registerWithEmailAndPassword = async (context: Context): Promise<string> => {
const { set, body: { email, password }, db } = context
// more code here
}
If the handler is placed in the 'auth.routes.ts' all relevant types are injected by Elysia and everything works as expected but if the handler function is present in a separate file, adding the Context to the type of the argument does not work. It does not add types for the body params injected by validator object and the database config object which is added as a plugin.
How can I infer these types in my handler function in a separate file?
Edit
The type for the argument for the handler works if I construct the type manually. The code for it is below:
import { dbType } from "../../config/database.config"
type RegisterType = Context & {
body: {
email: string
password: string
},
db: () => dbType
}
the registerType will be used in the handler as export const registerWithEmailAndPassword = async (context: RegisterType).
database.config.ts
// create the connection
const connection = connect({
host: process.env.DB_HOST,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD
})
const db = drizzle(connection, { schema, logger })
const databaseConfig = new Elysia({ name: "databaseConfig" })
.decorate('db', () => db)
export type dbType = typeof db
export default databaseConfig
Edit 2
I have already done everything that's mentioned in the Dependency Injection docs but it is still not working.
Elysia cannot type external route handlers
I have done the same pattern as you did, if the route
Handlersare separate from the actual route definition, Elysia has no way of propagating the types to the handlers. A simple code example for demonstration:And in our handlers index.ts file, Elysia won't be able to pass the types from routes file:
How to properly initialize routes externally with Elysia without losing types?
Good thing there is a much more Elysia-esque way of doing this, Elysia was made for this purpose.
Going back to our routes index.ts file, I made some changes, it shall no longer hold the actual definition of routes, instead we pass the route definition from elsewhere:
We then move all auth related definitions inside auth index.ts file, we then define another Elysia instance for our auth routes (yes this is the Elysia way of defining routes properly):
With that said let's continue with auth routes. Defining route handlers inline will make Elysia do its thing and propagate types for us!: