@ministerjs/resource
v2.0.0
Published
Um pacote TypeScript para padronização de APIs REST, fornecendo uma interface consistente para operações CRUD com suporte a filtros, paginação e relacionamentos parent-child.
Readme
@ministerjs/resource
Um pacote TypeScript para padronização de APIs REST, fornecendo uma interface consistente para operações CRUD com suporte a filtros, paginação e relacionamentos parent-child.
📦 Instalação
npm install @ministerjs/resource
# ou
pnpm add @ministerjs/resource
# ou
yarn add @ministerjs/resource🚀 Uso Básico
import { useRestResources } from "@ministerjs/resource";
// Configurar o gerenciador de recursos
const resources = useRestResources(fetch);
// Obter um recurso específico
const usersResource = resources.get<User>("users");
// Usar operações CRUD
const users = await usersResource.list();
const user = await usersResource.get(1);
const newUser = await usersResource.create({
name: "João",
email: "[email protected]",
});📚 API Reference
Métodos Disponíveis
get(id?: string | number)
Busca um item específico ou o primeiro item se nenhum ID for fornecido.
Request:
GET /{resource}
GET /{resource}/{id}Response:
{
data: T;
parents?: Record<string, ResourceItem>;
children?: Record<string, ResourceItem>;
}Exemplo:
// Buscar usuário específico
const user = await usersResource.get(123);
// Buscar primeiro usuário
const firstUser = await usersResource.get();getByProperty<K>(key: K, value: T[K])
Busca um item por uma propriedade específica.
Request:
GET /{resource}/{key}/{value}Response:
{
data: T;
parents?: Record<string, ResourceItem>;
children?: Record<string, ResourceItem>;
}Exemplo:
// Buscar usuário por email
const user = await usersResource.getByProperty("email", "[email protected]");
// Buscar produto por SKU
const product = await productsResource.getByProperty("sku", "ABC123");list(options?: ListQuery<T>)
Lista itens com suporte a filtros, busca, paginação e ordenação.
Request:
GET /{resource}?page=1&resultsPerPage=10&search=termo&sort=name:asc&filters=status:activeParâmetros de Query:
| Parâmetro | Tipo | Descrição |
| ---------------- | -------------------------------- | ---------------------------------------------------- |
| fields | BooleanDict<keyof T> | Campos que serão retornados |
| search | string | Termo de busca |
| page | number | Número da página (padrão: 1) |
| resultsPerPage | number | Itens por página (padrão: 10) |
| sort | SortSet | Ordenação (ex: "name:asc,created_at:desc") |
| filters | FilterSet | Filtros por campo (ex: "status:active,type:premium") |
| parent | Pair<string, string \| number> | Relacionamento parent-child |
Response:
{
data: T[];
pageInfo: {
page: number;
totalResults: number;
resultsPerPage: number;
};
}Exemplo:
// Listagem básica
const users = await usersResource.list();
// Com filtros e paginação
const activeUsers = await usersResource.list({
page: 2,
resultsPerPage: 20,
search: "João",
sort: "name:asc,created_at:desc",
filters: "status:active,role:admin",
fields: { id: true, name: true, email: true },
});
// Com relacionamento parent
const userPosts = await postsResource.list({
parent: ["user_id", 123],
});create(data: Partial<T> | FormData, options?: CreateQuery)
Cria um novo item.
Request:
POST /{resource}
Content-Type: application/jsonBody:
{
"name": "João Silva",
"email": "[email protected]",
"role": "admin"
}Response:
{
message: string;
data: T;
}Exemplo:
// Criar usuário com JSON
const newUser = await usersResource.create({
name: "João Silva",
email: "[email protected]",
role: "admin",
});
// Criar com FormData (para uploads)
const formData = new FormData();
formData.append("name", "João");
formData.append("avatar", file);
const userWithAvatar = await usersResource.create(formData);
// Criar com relacionamento parent
const newPost = await postsResource.create(
{
title: "Meu Post",
content: "Conteúdo do post",
},
{
parent: ["user_id", 123],
},
);update(id: string | number, data: Partial<T> | FormData)
Atualiza um item existente.
Request:
PUT /{resource}/{id}
Content-Type: application/jsonBody:
{
"name": "João Silva Santos",
"email": "[email protected]"
}Response:
{
message: string;
data: T;
}Exemplo:
// Atualizar usuário
const updatedUser = await usersResource.update(123, {
name: "João Silva Santos",
email: "[email protected]",
});
// Atualizar com FormData
const formData = new FormData();
formData.append("avatar", newAvatarFile);
const userWithNewAvatar = await usersResource.update(123, formData);updateProperty<K>(id: string | number, key: K, value: Partial<T>[K])
Atualiza uma propriedade específica de um item.
Request:
PUT /{resource}/{id}/{property}
Content-Type: application/jsonBody:
"novo_valor"Response:
{
message: string;
data: T;
}Exemplo:
// Atualizar apenas o status do usuário
const user = await usersResource.updateProperty(123, "status", "inactive");
// Atualizar apenas o email
const user = await usersResource.updateProperty(123, "email", "[email protected]");updateObject(data: Partial<T> | FormData)
Atualiza o objeto atual sem especificar ID (útil para contextos onde o ID é implícito).
Request:
PUT /{resource}
Content-Type: application/jsonBody:
{
"name": "Novo Nome",
"email": "[email protected]"
}Response:
{
message: string;
data: T;
}Exemplo:
// Atualizar objeto no contexto atual
const updatedItem = await usersResource.updateObject({
name: "Novo Nome",
preferences: { theme: "dark" },
});updatePropertyMany<K>(key: K, data: Pick<T, "id" | K>[])
Atualiza uma propriedade específica em múltiplos itens.
Request:
PUT /{resource}/{property}
Content-Type: application/jsonBody:
[
{ "id": 1, "status": "active" },
{ "id": 2, "status": "inactive" },
{ "id": 3, "status": "active" }
]Response:
{
message: string;
data: T[];
}Exemplo:
// Atualizar status de múltiplos usuários
const updatedUsers = await usersResource.updatePropertyMany("status", [
{ id: 1, status: "active" },
{ id: 2, status: "inactive" },
{ id: 3, status: "pending" },
]);
// Atualizar categoria de múltiplos produtos
const updatedProducts = await productsResource.updatePropertyMany("category", [
{ id: 101, category: "electronics" },
{ id: 102, category: "books" },
{ id: 103, category: "clothing" },
]);delete(id: string | number)
Remove um item.
Request:
DELETE /{resource}/{id}Response:
{
message: string;
}Exemplo:
// Deletar usuário
await usersResource.delete(123);
// Deletar produto
await productsResource.delete("ABC123");🔧 Configuração Avançada
Custom Fetch
Você pode fornecer sua própria implementação de fetch para adicionar headers, autenticação, etc:
// Fetch customizado com autenticação
const customFetch = async (url: string, options: RequestInit) => {
return fetch(url, {
...options,
headers: {
...options?.headers,
Authorization: `Bearer ${getToken()}`,
"Content-Type": "application/json",
},
});
};
const resources = useRestResources(customFetch);Tratamento de Erros
O pacote inclui tratamento de erros padronizado:
import { RequestError } from "@ministerjs/resource";
try {
const user = await usersResource.get(999);
} catch (error) {
if (error instanceof RequestError) {
console.log("Erro da API:", error.message);
console.log("Detalhes:", error.details);
// Verificar erros de campo específico
error.details?.forEach((detail) => {
if (detail.domain === "field") {
console.log(`Erro no campo ${detail.fieldkey}: ${detail.message}`);
}
});
}
}📝 Tipos TypeScript
O pacote é totalmente tipado com TypeScript. Principais interfaces:
// Item base do recurso
type ResourceItem = Record<string, any>;
// Resposta de item único
interface GettedItemResponse<T extends ResourceItem> {
data: T;
parents?: Record<string, ResourceItem>;
children?: Record<string, ResourceItem>;
}
// Resposta de lista
interface ListedItemsResponse<T extends ResourceItem> {
data: T[];
pageInfo: {
page: number;
totalResults: number;
resultsPerPage: number;
};
}
// Resposta de erro
interface ResponseError {
error: string;
message: string;
details?: {
message: string;
domain: "global" | "field";
fieldkey?: string;
}[];
}🏗️ Integração com Framework MinisterJS
Este pacote faz parte do ecossistema MinisterJS e se integra perfeitamente com:
@ministerjs/server- Para implementação do backend@ministerjs/utils- Para utilitários como FilterSet, SortSet@ministerjs/composables- Para composables Vue.js
📄 Licença
Este pacote faz parte do framework MinisterJS (UNLICENSED).
