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
.
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
.
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:
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
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
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
- Single Responsibility: Each helper should have a specific purpose.
- Type Definitions: Use types that are as precise as possible.
- Documentation: Describe your functions with JSDoc.
- Resource Management: Create heavy resources (e.g., database connection) once.
- Clean Errors: Use meaningful error messages and custom error types.
- 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
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
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
}
}
})