Skip to content

E1ecti/moleculer-template

Repository files navigation

Template

Template app based on moleculer, with API Gateway powered by ultimate-express, validation via fastest-validator-decorators and typegoose ORM.

OpenAPI generation provided by @spailybot/moleculer-auto-openapi with Scalar UI and custom OpenAPI decorators.

Bundling powered by rolldown with some fixes.

Project structure

This template follows the approach of one folder per service and one file per action.

project-name
├── src
│   ├── index.ts
│   ├── rolldown.config.ts
│   ├── moleculer.config.ts
│   ├── utils
│   └── services
│       ├── auth
│       │   ├── index.ts
│       │   └── actions
│       │       ├── signin.ts
│       │       └── signup.ts
│       └── ...
├── package.json
└── tsconfig.json

Validation Schema Declaration

Using @ObjectId without transform: true results in a string. Note that automatic transformation to a native ObjectId may impact performance.

@Schema({ strict: true })
export class BookParams {
	@String()
	title: string

	@Array({ items: { type: "objectID" }, max: 24 })
	tags: string[]

	@Number({ positive: true, integer: true })
	pages: number

	@ObjectId()
	file: string

	@Boolean({ optional: true, default: false })
	hidden?: boolean
}

Even though the original library requires using getSchema(Schem), the @Params(Schem) decorator applies it automatically.

Example of Service Action schema

For security reasons, always use the @Params decorator to ensure user data validation. The @Authorization decorator is optional. @Rest exposes the action via the API Gateway. The @Visibility decorator controls action accessibility (e.g., published, public, protected, private). See the Moleculer documentation for more details.

Note, @Action must be on top of the class declaration.

@Action("stop")
@Authorization()
@Rest("POST", "/stop")
@Params(SessionStopParams)

@O.Describe("Short summary", "Short description of the action.")
@O.Response(204, "Short description of the response.")
@O.Response(404, "Short description of the response.", Refs.NotFound)
class SessionStop extends DecoratedMixin {
	async handler(ctx: AppContext<SessionStopParams>) {
		const { id } = ctx.params
		const { user } = ctx.meta

		const result = await Session.updateOne({
			_id: id,
			user: user,
			state: SessionState.ACTIVE
		}, {
			$set: { state: SessionState.TERMINATED }
		})
			.catch(err => {
				this.logger.error(err)
				return false
			})

		if (!result || !result.modifiedCount)
			return E.NOT_FOUND("Session not found")
		
		await ctx.broadcast("session.updated", { id })

		ctx.meta.$statusCode = 204
		return
	}
}

// Use `.schema` for legacy compa
export default SessionStop.schema

Текущий шаблон предлагает два варианта создания ошибок, которые должны быть заранее объявлены в @utils/Errors.

  1. E.ERROR_CODE(message?: string, data?: any)
  2. MakeError(code: string, { message?: string, data?: any })
import { E } from "@utils/Errors"

return E.LIMIT_REACHED("Message", {
	expected: { min: 1, max: 20},
	recieved: 100
})

Либо-же:

import { MakeError } from "@utils/Errors"

MakeError("ERROR_CODE", {
	message: "Message",
	data: <...>
})

About

Draft

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors