Skip to content

Using createShared

One of Silgi's most powerful features is the shared system, which provides access to common functions and data across services. Shared helpers modularize your code, reduce repetition, and simplify maintenance.


Why Use Shared Helpers?

  • Reduces Code Repetition: Define common functions once, use them in all services.
  • Ensures Consistency: Centralizes business logic to guarantee the same behavior.
  • Modularity: Breaks down complex functions into small, reusable parts.
  • Efficiency: Improves performance by sharing resources (e.g., database connection).
  • Testability: Isolate and easily test common logic.
  • Team Collaboration: Facilitates code sharing.
  • Inter-Module Communication: Simplifies data and function sharing.

Basic Usage

createShared Function

The main function for creating your shared helpers is createShared. All your helper interfaces must extend ExtendShared.

server/shared/bookHelper.ts
typescript
import type { ExtendShared } from 'silgi/types'
import { createShared } from 'silgi'

export interface BookHelper extends ExtendShared {
  formatBookName: (name: string) => string
}

export const bookHelper = createShared({
  formatBookName: name => `[BOOK] ${name}`,
})

ExtendShared Interface

  • CLI Integration: Silgi CLI automatically recognizes your shared helpers.
  • Type Extension: Extends Silgi's types with your own helper types.
  • Consistency: All helpers have a common base type.

Step-by-Step Shared Helper Creation

1. Create the Helper File

Usually defined under server/shared.

server/shared/dateHelper.ts
typescript
import type { ExtendShared } from 'silgi/types'
import { createShared } from 'silgi'

export interface DateHelper extends ExtendShared {
  formatDate: (date: Date) => string
  dateDiffInDays: (start: Date, end: Date) => number
}

export const dateHelper = createShared({
  formatDate: date => date.toISOString().split('T')[0],
  dateDiffInDays: (start, end) => {
    const diffMs = end.getTime() - start.getTime()
    return Math.floor(diffMs / (1000 * 60 * 60 * 24))
  }
})

Caution

Your shared helpers must start with export const and export interface. Otherwise, they will not be detected by the CLI.

2. Preparation

After defining your helpers, for the CLI to scan them:

bash
pnpm silgi prepare

3. Usage in Service

You can automatically access them in all service handlers:

server/services/book.ts
typescript
import { createError, createService } from 'silgi'

export const getBookByIdService = createService({
  path: 'GET:/api/library/books/:id',
  setup: {
    handler: async (input, shared) => {
      const bookId = input.parameters.path?.id
      if (!bookId)
        throw createError({ statusCode: 400, message: 'Book ID is required' })
      // Shared helper usage
      const name = shared.formatBookName('Sample Book')
      return {
        id: bookId,
        name,
        description: 'Sample Description',
        author: { id: 'auth-1', name: 'John Doe' }
      }
    }
  }
})

Advanced Usage

Database Helper

server/shared/databaseHelper.ts
typescript
import type { ExtendShared } from 'silgi/types'
import { createClient } from 'database-lib'
import { createShared } from 'silgi'

export interface DatabaseHelper extends ExtendShared {
  getUser: (id: string) => Promise<any>
  getOrder: (id: string) => Promise<any>
  dbClient: ReturnType<typeof createClient>
}

const dbClient = createClient({ connectionString: process.env.DATABASE_URL })

export const databaseHelper = createShared({
  getUser: id => dbClient.query('SELECT * FROM users WHERE id = $1', [id]),
  getOrder: id => dbClient.query('SELECT * FROM orders WHERE id = $1', [id]),
  dbClient,
})

External API Helper

server/shared/bookApiHelper.ts
typescript
import type { ExtendShared } from 'silgi/types'
import { createShared } from 'silgi'
import { localConfig } from '../silgi/configs'
import { createBookApiClient } from '../utils/bookApi'

export interface BookApiHelper extends ExtendShared {
  fetchBookDetail: (id: string) => Promise<any>
  searchBooks: (query: string) => Promise<any[]>
  apiClient: ReturnType<typeof createBookApiClient>
}

const apiClient = createBookApiClient({
  apiKey: localConfig.book.apiKey,
  baseUrl: localConfig.book.baseUrl
})

export const bookApiHelper = createShared({
  fetchBookDetail: id => apiClient.getBookDetail(id),
  searchBooks: query => apiClient.searchBooks(query),
  apiClient,
})

Contents of the shared Object

In a service handler, you can access the following with the shared parameter:

  • All Shared Helpers: All helpers you defined (e.g., shared.formatBookName, shared.fetchBookDetail).
  • Storage: Key-value storage (e.g., shared.storage('cache').setItem(...)).
  • Runtime Config: Runtime configuration (e.g., shared.useRuntimeConfig()).
  • Framework Context: Infrastructure context.

Usage example:

typescript
handler: async (input, shared) => {
  const bookId = input.parameters.path?.id
  const book = await shared.getBook(bookId)
  const formattedName = shared.formatBookName(book.name)
  await shared.storage('cache').setItem(`book:${book.id}`, book, { ttl: 3600 })
  const apiKey = shared.getBookApiKey()
  return { ...book, formattedName, apiKey }
}

Best Practices

  1. Single Responsibility: Each helper should have a specific purpose.
  2. Type Definitions: Use types that are as precise as possible.
  3. Documentation: Describe your functions with JSDoc.
  4. Resource Management: Create heavy resources (e.g., database connection) once.
  5. Clean Errors: Use meaningful error messages and custom error types.
  6. Testability: Design your helpers to be independent and testable.

Frequently Asked Questions

Helpers for Different Environments

typescript
export const bookDbHelper = createShared({
  connect: () => {
    if (process.env.NODE_ENV === 'test')
      return connectToTestDb()
    return connectToProdDb()
  }
})

Accessing Shared Helper Outside of Service

typescript
import { shareds } from './server/silgi/scan'

export async function fetchBookDetailExternal(id: string) {
  const shared = shareds()
  return shared.fetchBookDetail(id)
}

Advanced Example: Book Service

server/services/book.ts
typescript
import type { ExtendShared } from 'silgi/types'
import { createError, createService, createShared } from 'silgi'

// SHARED
export interface BookHelper extends ExtendShared {
  formatBookName: (name: string) => string
}

export const bookHelper = createShared({
  formatBookName: (name: string) => `[BOOK] ${name}`,
})

// SERVICE
export const getBookByIdService = createService({
  path: 'GET:/api/library/books/:id',
  setup: {
    handler: async (input, shared) => {
      const bookId = input.parameters.path?.id
      const includeAuthor = input.parameters.query?.includeAuthor ?? true
      const format = input.parameters.query?.format ?? 'full'

      if (!bookId) {
        throw createError({
          statusCode: 400,
          message: 'Book ID is required',
        })
      }

      // Shared helper usage
      const name = shared.formatBookName('Sample Book')
      const book = {
        id: bookId,
        name,
        description: format === 'full' ? 'Sample Description' : undefined,
        author: includeAuthor
          ? { id: 'auth-1', name: 'John Doe' }
          : undefined,
      }
      return book
    }
  }
})

Advanced Example: Using Multiple Helpers

server/services/book.ts
typescript
import { createError, createService } from 'silgi'

export const listBooksService = createService({
  path: 'GET:/api/library/books/list',
  setup: {
    handler: async (input, shared) => {
      shared.logInfo('Fetching relevant books')
      const { category, limit, offset } = input.parameters.query || {}
      const cacheKey = `books:${category}:${limit}:${offset}`
      const cachedBooks = await shared.storage('cache').getItem(cacheKey)
      if (cachedBooks) {
        shared.logHelper.logInfo('Books retrieved from cache')
        return cachedBooks
      }
      const rawBooks = await shared.searchBooks({ category, limit, offset })
      const books = rawBooks.map(book => ({
        ...book,
        name: shared.formatBookName(book.name)
      }))
      await shared.storage('cache').setItem(cacheKey, books, { ttl: 600 })
      shared.incrementCounter('books_searched')
      return books
    }
  }
})

Released under the MIT License. (dev). Documentation design is a copy of vite.dev docs.