OrionJS Documentation

website·Indexed 31 days ago

https://orionjs.com/

A framework for building type-safe APIs with integrated support for GraphQL, tRPC, and MongoDB. Features include a decorator-based approach to controllers, repositories, and schemas, as well as specialized tools like Echoes for event-driven microservices, Dogs for background job management, and Pulse for event processing. Supports deployment with Bun and Node.js.

Tokens
50K
Snippets
271
Records
329
Agent score
50%

What's inside OrionJS

  1. Overview of Pulse event delivery

    Pulse is a typed pub/sub system designed for services already using MongoDB that need distributed, recoverable event delivery without a separate event broker (like Kafka). It provides durable consumer groups, concurrent handlers, retries, and crash recovery. It is published as @orion-js/pulse and can be used in Orionjs services or standalone Node.js/Bun processes. Unlike Kafka, it is not protocol-compatible and does not provide partitions or a globally ordered log.
  2. Overview of the Orionjs Framework

    Orionjs is a TypeScript framework designed for simplicity and optimized for AI-generated code. It follows a layered architecture where controllers initiate operations and services handle core business logic. Starting with v4.3, Bun is the default runtime for faster startup and native TypeScript execution, though Node.js remains supported via the --node flag.
  3. Optimize MongoDB queries using DataLoader

    DataLoader is a built-in utility for Orionjs MongoDB collections designed to solve the N+1 query problem. It optimizes database access through batching (combining multiple individual requests into one query) and caching (avoiding duplicate queries within the same request context). It is especially useful for GraphQL resolvers, nested API endpoints, and heavy read operations. The cache is cleared between requests to prevent stale data.
  4. Understand the Orionjs project directory structure

    Orionjs uses a modular, component-based architecture. The root directory contains configuration and dependency files, while the app/ directory houses the core logic. The app/ directory is divided into config/ for application-wide settings, components/ for domain-specific modules, and an index.ts entry point.
    /
    ├── app/
    │   ├── config/           # Application configuration
    │   ├── components/       # Application components
    │   ├── env.d.ts          # Environment file
    │   └── index.ts          # Application entry point
    ├── node_modules/
    ├── .env.local.yml    # Local environment variables
    ├── biome.json        # Linting/formatting
    ├── package.json      # Dependencies and scripts
    └── tsconfig.json     # TypeScript configuration
  5. Understand automatic schema validation in Orionjs

    Orionjs provides automatic schema validation for specific integration points to ensure data integrity without manual calls. Validation occurs automatically in the following scenarios:

    • MongoDB: Documents are validated before being inserted or updated in the collection.
    • GraphQL: Input data is validated before it is processed by the resolvers.
  6. Implement application logic using Orionjs Core Concepts

    Orionjs uses a modular architecture based on the following building blocks:

    • Controllers: The entry point for code execution. Supported types include HTTP, GraphQL (queries, mutations, subscriptions, model resolvers), Jobs (scheduled/queued), and Echoes (inter-service communication).
    • Services: The layer containing the core business logic. Controllers invoke service functions to perform application tasks.
    • Repositories: The data access layer responsible for fetching data from databases or external sources, isolating data logic from business logic.
    • Schemas: Data model definitions used for validation, database model creation, GraphQL schema generation, and TypeScript interfaces.
    • Components: The primary organizational unit. A single application can consist of multiple components, each containing its own set of controllers, repositories, services, and schemas.
  7. Access job execution context in resolve functions

    The resolve function of a job receives a second argument, the context object, which provides metadata about the current execution, including the jobId, the number of tries (attempts), and the createdAt timestamp.
    @RecurrentJob()
    exampleJob = createRecurrentJob({
      runEvery: '1h',
      resolve: async (_, context) => {
        console.log('Job ID:', context.jobId)
        console.log('Execution attempt:', context.tries)
        console.log('Scheduled at:', context.createdAt)
      }
    })
    
  8. Organize Resolver Files and Naming

    Follow these conventions for resolver structure and naming to maintain a clean project architecture:

    Naming Conventions:

    • Resolver Classes: {Action}{Entity}Resolvers (e.g., GetUserResolvers, UpdateProductResolvers).
    • Mutations: Descriptive of the operation (e.g., createUser, listProducts).
    • Queries: Use the name of the entity (e.g., user, blogPosts).

    File Structure: Place resolvers in app/{component}/controllers/resolvers/{Entity}/{Action}/index.ts.

    Exporting: Create a central controllers/resolvers/index.ts file that exports all resolver classes as a single array.

    // controllers/resolvers/index.ts
    import {GetUserResolvers} from './users/GetUser'
    import {CreateUserResolvers} from './users/CreateUser'
    
    export default [
      GetUserResolvers,
      CreateUserResolvers
    ]
  9. Understand the role of Controllers in Orionjs

    Controllers in Orionjs serve as the entry points for all code execution. They act as an interface between external systems and the application's business logic (which is implemented in services). A controller's primary responsibilities are to accept input from various sources, validate and transform that input, call the appropriate services to execute business logic, and return a response in the required format. To ensure maintainability and testability, controllers should remain 'thin' layers that delegate logic to services.
  10. Adaptive MongoDB index selection for job acquisition in @orion-js/dogs

    Starting with version @orion-js/dogs@4.6.2, the scheduler automatically chooses between two MongoDB indexes to optimize job acquisition based on the specific workload of the startWorkers() instance. This is particularly useful when some servers handle a small subset of job names (benefiting from a jobName-led index) while others handle most jobs (benefiting from a priority-led index).

    Index Options:

    • {jobName: 1, priority: -1, nextRunAt: 1} (Default/Initial hint)
    • {priority: -1, nextRunAt: 1} (Alternative hint)

    Behavior:

    • The scheduler measures both indexes at startup and every 30 minutes using explain('executionStats') to compare the median executionTimeMillis.
    • The winner is applied immediately after the first probe. Subsequent changes require the alternative index to win twice consecutively.
    • If maxParallelExecutionsPerServer filters out some job names, the system temporarily overrides the selection and uses the jobName-leading index for that specific claim.
    • Different servers can use different hints simultaneously if their configured jobs or data distributions differ.
    // The two indexes declared on orionjs.jobs_dogs_records
    {jobName: 1, priority: -1, nextRunAt: 1}
    {priority: -1, nextRunAt: 1}
  11. Access Context and Viewer in Resolvers

    All resolver methods receive the viewer object as the second parameter. This object contains information about the current user and session, which is essential for authorization and user-specific data fetching.
    @Query()
    userProfile = createQuery({
      returns: UserProfile,
      resolve: async (_, viewer) => {
        if (!viewer.user) {
          throw new Error('Unauthorized')
        }
        return await this.userService.getProfile(viewer.user._id)
      }
    })
  12. Implement authentication and authorization using route middleware

    Orionjs allows the use of middleware within route definitions to handle authentication and authorization. Middleware functions can validate tokens, attach user data to the request object, and call next() to proceed to the route handler or return an error response to block access.
    // Auth middleware
    const authMiddleware = async (req, res, next) => {
      const token = req.headers.authorization?.split(' ')[1]
      
      if (!token) {
        return res.status(401).json({error: 'No token provided'})
      }
      
      try {
        const user = await verifyToken(token)
        req.user = user // Attach user to request
        next()
      } catch (error) {
        return res.status(401).json({error: 'Invalid token'})
      }
    }
    
    @Routes()
    export class UserRoutes {
      @Inject(() => UserService)
      private userService: UserService
    
      @Route()
      getProfile = createRoute({
        path: '/profile',
        method: 'get',
        middlewares: [authMiddleware],
        resolve: async (req) => {
          // req.user is guaranteed to exist from the authMiddleware
          return {
            body: await this.userService.getUserProfile(req.user._id)
          }
        }
      })
    }