Decorators in Base, what, why, and how
The vocabulary you decorate your classes with, and how the framework reads them to wire your worker.
Decorators in Base: what, why, and how
Decorators are the surface you touch most in Base, so it is worth understanding what they are actually doing. The short version: a decorator is a label you put on a class, a method, or a property that tells Base what that thing is and how to treat it. You write plain TypeScript, you label it, and Base does the rest at startup.
What a decorator is
Syntactically, a decorator is an @-prefixed name attached to a class or one of its members:
Each @Something is a function that runs once, when your worker starts, and records metadata about the thing it is attached to. Base reads that metadata to build its routes, its schema, its injection graph, its validation rules. By the time a request arrives, all of that has already been assembled. Decorators are a startup-time description, not a per-request cost.
Why Base uses them
The alternative to decorators is a registry you maintain by hand: a file that lists every route and its handler, every GraphQL field and its type, every validation rule and the property it guards. That file is a second source of truth, and second sources of truth drift. Someone adds a field and forgets the registry; now the code and the description disagree, and the bug is subtle.
Decorators put the description on the thing being described. The label for a route lives on the route. The label for a GraphQL field lives on the field. There is one source of truth, and it is the code, because the description is attached to the code. Nothing to keep in sync, because there is no second copy.
The deeper payoff is that one class can wear several hats at once. Look at this, lifted from the test worker:
That single class is, at the same time, a GraphQL output type, a GraphQL input type, and a validated shape. @GqlField tells the schema builder about each property; @VerifyIsNotEmpty and @VerifyMax tell the validator how to guard them. You did not write a schema file, a separate input type, and a validation function that all describe the same three fields. You wrote the fields once and labeled them with what they are.
How: the decorator families
Base's decorators are named so you can guess them. The prefix tells you the subsystem; the rest tells you the job. Once you know the families, the individual decorators read themselves.
@Gql*for GraphQL:@GqlResolver,@GqlQuery,@GqlMutation,@GqlObjectType,@GqlInputType,@GqlField,@GqlArgument. Define your schema in code, by labeling classes and methods.@Http*for the HTTP router:@HttpService,@HttpRoute, and the parameter decorators@HttpBody,@HttpQuery,@HttpPath,@HttpHeader,@HttpCookie. Bind a method to a URL and pull the pieces of the request you want.@Rpc*for typed calls between workers and from the browser:@RpcService,@Rpc,@RpcArgument,@WebSocketRpc, plus@InjectRpcClientto call out.@Verify*for validation. A large family that reads like a sentence:@VerifyIsNotEmpty,@VerifyIsEmail,@VerifyMax,@VerifyMinLength,@VerifyIsUUID, and many more. Label a property with the rule it must satisfy.@Orm*for the ORM:@OrmTable,@OrmColumn,@OrmPrimaryKey, the relation decorators (@OrmManyToOne,@OrmOneToMany, and friends), and the lifecycle hooks (@OrmBeforeInsert,@OrmAfterLoad). Describe your tables as decorated classes.@Serializable*for the serialization contract:@SerializableObject,@SerializableField. Mark what crosses the wire and how.@Inject*/@Providerfor dependency injection, covered in its own post.
You will not memorize every decorator, and you do not need to. You learn a family when you reach for that subsystem, and the naming carries you the rest of the way.
How: where you actually use them
Two layers. On your classes, the decorators above describe what each class is. In your settings object, you list the classes so Base knows to load them:
That is the loop: decorate the class to say what it is, name it in settings so Base loads it, and Base reads the decorators to wire it. Add one key, write one decorated class. It is the same two moves everywhere in Base, and decorators are the half that lives on your code.
The mental model to keep
A decorator is a label that the framework reads at startup to learn what your code is. It keeps the description on the thing described, so there is no second source of truth to drift. And because a class can carry labels from several subsystems at once, you describe a shape one time and let it be a schema, a validated input, and a database row all together.