Skip to main content

Service object wrapper pattern

Use case

Encapsulate API calls in a typed service object for better organisation and reusability.

Example

import { createClient } from '@parcely/core'
import type { HttpResponse } from '@parcely/core'

interface User {
id: string
name: string
email: string
}

interface CreateUserInput {
name: string
email: string
}

const http = createClient({
baseURL: 'https://api.example.com',
headers: { Accept: 'application/json' },
timeout: 5000,
})

const userService = {
async list(): Promise<User[]> {
const { data } = await http.get<User[]>('/users')
return data
},

async getById(id: string): Promise<User> {
const { data } = await http.get<User>(`/users/${id}`)
return data
},

async create(input: CreateUserInput): Promise<User> {
const { data } = await http.post<User, CreateUserInput>('/users', input)
return data
},

async update(id: string, input: Partial<CreateUserInput>): Promise<User> {
const { data } = await http.patch<User, Partial<CreateUserInput>>(`/users/${id}`, input)
return data
},

async remove(id: string): Promise<void> {
await http.delete(`/users/${id}`)
},
}

// Usage:
const users = await userService.list()
const newUser = await userService.create({ name: 'Mickey', email: 'm@example.com' })

Notes

  • Type parameters on .get<T>(), .post<T, B>(), etc. provide type inference for both requests and responses.
  • Combine with runtime validation (validate) for additional safety.
  • The service object pattern works well with dependency injection frameworks.